# Table of Contents
- [Introduction | Somnia Docs](#introduction-somnia-docs)
- [Introduction | Concepts | Somnia Docs](#introduction-concepts-somnia-docs)
- [Somnia-Mission | Concepts | Somnia Docs](#somnia-mission-concepts-somnia-docs)
- [Overview | Concepts | Somnia Docs](#overview-concepts-somnia-docs)
- [MultiStream Consensus | Concepts | Somnia Docs](#multistream-consensus-concepts-somnia-docs)
- [Somnia's IceDB | Concepts | Somnia Docs](#somnia-s-icedb-concepts-somnia-docs)
- [Problem | Concepts | Somnia Docs](#problem-concepts-somnia-docs)
- [Accelerated Sequential Execution | Concepts | Somnia Docs](#accelerated-sequential-execution-concepts-somnia-docs)
- [Use Cases | Concepts | Somnia Docs](#use-cases-concepts-somnia-docs)
- [On-Chain Reactivity | Concepts | Somnia Docs](#on-chain-reactivity-concepts-somnia-docs)
- [Security | Concepts | Somnia Docs](#security-concepts-somnia-docs)
- [Advanced Compression Techniques | Concepts | Somnia Docs](#advanced-compression-techniques-concepts-somnia-docs)
- [Allocation and unlocks | Concepts | Somnia Docs](#allocation-and-unlocks-concepts-somnia-docs)
- [Overview | Concepts | Somnia Docs](#overview-concepts-somnia-docs)
- [Token Staking and Delegation | Concepts | Somnia Docs](#token-staking-and-delegation-concepts-somnia-docs)
- [Conclusion | Concepts | Somnia Docs](#conclusion-concepts-somnia-docs)
- [LEGAL | Concepts | Somnia Docs](#legal-concepts-somnia-docs)
- [LEGAL DISCLAIMER | Concepts | Somnia Docs](#legal-disclaimer-concepts-somnia-docs)
- [Tokens Governance | Concepts | Somnia Docs](#tokens-governance-concepts-somnia-docs)
- [Gas Fees | Concepts | Somnia Docs](#gas-fees-concepts-somnia-docs)
- [Connect Your Wallet To Mainnet | Somnia Docs](#connect-your-wallet-to-mainnet-somnia-docs)
- [Governance | Concepts | Somnia Docs](#governance-concepts-somnia-docs)
- [Testnet STT Tokens | Somnia Docs](#testnet-stt-tokens-somnia-docs)
- [Getting Started for Mainnet | Somnia Docs](#getting-started-for-mainnet-somnia-docs)
- [Audits | Concepts | Somnia Docs](#audits-concepts-somnia-docs)
- [MiCAR Whitepaper | Concepts | Somnia Docs](#micar-whitepaper-concepts-somnia-docs)
- [SOMI coin | Somnia Docs](#somi-coin-somnia-docs)
- [Network Overview (Mainnet / Testnet) | Somnia Docs](#network-overview-mainnet-testnet-somnia-docs)
- [Network Info | Somnia Docs](#network-info-somnia-docs)
- [Tooling | Somnia Docs](#tooling-somnia-docs)
- [Bridging Info | Somnia Docs](#bridging-info-somnia-docs)
- [Somnia Reactivity | Somnia Docs](#somnia-reactivity-somnia-docs)
- [Subscriptions: The Core Primitive | Somnia Docs](#subscriptions-the-core-primitive-somnia-docs)
- [Somnia Data Streams | Somnia Docs](#somnia-data-streams-somnia-docs)
- [What is Reactivity? | Somnia Docs](#what-is-reactivity-somnia-docs)
- [State Consistency Guarantees | Somnia Docs](#state-consistency-guarantees-somnia-docs)
- [AML Compliance | Concepts | Somnia Docs](#aml-compliance-concepts-somnia-docs)
- [Push vs Pull: An Architectural Shift | Somnia Docs](#push-vs-pull-an-architectural-shift-somnia-docs)
- [Quickstart | Somnia Docs](#quickstart-somnia-docs)
- [Airdrop Policy | Concepts | Somnia Docs](#airdrop-policy-concepts-somnia-docs)
- [Comparison | Somnia Docs](#comparison-somnia-docs)
- [Concepts | Somnia Docs](#concepts-somnia-docs)
- [Extending and composing data schemas | Somnia Docs](#extending-and-composing-data-schemas-somnia-docs)
- [No, this is not like regular EVM event subscriptions | Somnia Docs](#no-this-is-not-like-regular-evm-event-subscriptions-somnia-docs)
- [What is Somnia Data Streams? | Somnia Docs](#what-is-somnia-data-streams-somnia-docs)
- [FAQs & Troubleshooting | Somnia Docs](#faqs-troubleshooting-somnia-docs)
- [Tutorials | Somnia Docs](#tutorials-somnia-docs)
- [Quickstart | Somnia Docs](#quickstart-somnia-docs)
- [System Events | Somnia Docs](#system-events-somnia-docs)
- [Building DApps | Somnia Docs](#building-dapps-somnia-docs)
- [On-chain (Solidity) | Somnia Docs](#on-chain-solidity-somnia-docs)
- [Off-Chain (TypeScript) | Somnia Docs](#off-chain-typescript-somnia-docs)
- [API Reference | Somnia Docs](#api-reference-somnia-docs)
- [Data Provenance and Verification in Streams | Somnia Docs](#data-provenance-and-verification-in-streams-somnia-docs)
- [Tutorials | Somnia Docs](#tutorials-somnia-docs)
- [Development Frameworks | Somnia Docs](#development-frameworks-somnia-docs)
- [Intersection with Somnia Reactivity | Somnia Docs](#intersection-with-somnia-reactivity-somnia-docs)
- [Deploy with Thirdweb | Somnia Docs](#deploy-with-thirdweb-somnia-docs)
- [Off-Chain Reactivity: Filtered Subscriptions tutorial | Somnia Docs](#off-chain-reactivity-filtered-subscriptions-tutorial-somnia-docs)
- [Subscription management | Somnia Docs](#subscription-management-somnia-docs)
- [Deploy with RemixIDE | Somnia Docs](#deploy-with-remixide-somnia-docs)
- [Security | Somnia Docs](#security-somnia-docs)
- [Deployment and Production | Somnia Docs](#deployment-and-production-somnia-docs)
- [Ecosystem | Somnia Docs](#ecosystem-somnia-docs)
- [Solidity on-chain Reactivity Tutorial | Somnia Docs](#solidity-on-chain-reactivity-tutorial-somnia-docs)
- [Tokens and NFTs | Somnia Docs](#tokens-and-nfts-somnia-docs)
- [Audit Checklist | Somnia Docs](#audit-checklist-somnia-docs)
- [OnRamps | Somnia Docs](#onramps-somnia-docs)
- [Deploy with Foundry | Somnia Docs](#deploy-with-foundry-somnia-docs)
- [Wildcard Off-Chain Reactivity Tutorial | Somnia Docs](#wildcard-off-chain-reactivity-tutorial-somnia-docs)
- [Understanding Schemas, Schema IDs, Data IDs, and Publisher | Somnia Docs](#understanding-schemas-schema-ids-data-ids-and-publisher-somnia-docs)
- [Cron subscriptions via SDK | Somnia Docs](#cron-subscriptions-via-sdk-somnia-docs)
- [Authenticating with ConnectKit | Somnia Docs](#authenticating-with-connectkit-somnia-docs)
- [Smart Contracts | Somnia Docs](#smart-contracts-somnia-docs)
- [Verifying via Explorer | Somnia Docs](#verifying-via-explorer-somnia-docs)
- [Wallet Integration and Auth | Somnia Docs](#wallet-integration-and-auth-somnia-docs)
- [Oracles | Somnia Docs](#oracles-somnia-docs)
- [Buy SOMI Using Banxa Checkout | Somnia Docs](#buy-somi-using-banxa-checkout-somnia-docs)
- [“Hello World” App | Somnia Docs](#-hello-world-app-somnia-docs)
- [Build Your First Schema | Somnia Docs](#build-your-first-schema-somnia-docs)
- [The DApp Publisher Proxy Pattern | Somnia Docs](#the-dapp-publisher-proxy-pattern-somnia-docs)
- [Data Indexing and Querying | Somnia Docs](#data-indexing-and-querying-somnia-docs)
- [Account Abstraction | Somnia Docs](#account-abstraction-somnia-docs)
- [Ecosystem Showcase | Somnia Docs](#ecosystem-showcase-somnia-docs)
- [Somnia Data vs Event Streams | Somnia Docs](#somnia-data-vs-event-streams-somnia-docs)
- [Using the Viem Library | Somnia Docs](#using-the-viem-library-somnia-docs)
- [Go-Live Checklist | Somnia Docs](#go-live-checklist-somnia-docs)
- [Deploy with Hardhat | Somnia Docs](#deploy-with-hardhat-somnia-docs)
- [Create ERC20 Tokens | Somnia Docs](#create-erc20-tokens-somnia-docs)
- [Support and Community | Somnia Docs](#support-and-community-somnia-docs)
- [Build a Realtime On-Chain Game | Somnia Docs](#build-a-realtime-on-chain-game-somnia-docs)
- [Example Applications | Somnia Docs](#example-applications-somnia-docs)
- [Meme Coins | Somnia Docs](#meme-coins-somnia-docs)
- [NFTs2Me | Somnia Docs](#nfts2me-somnia-docs)
- [DAO UI Tutorial p1 | Somnia Docs](#dao-ui-tutorial-p1-somnia-docs)
- [Authenticating with RainbowKit | Somnia Docs](#authenticating-with-rainbowkit-somnia-docs)
- [APIs | Somnia Docs](#apis-somnia-docs)
- [Somnia Domains (.somi) | Somnia Docs](#somnia-domains-somi-somnia-docs)
- [RPC | Somnia Docs](#rpc-somnia-docs)
- [Wallet Providers | Somnia Docs](#wallet-providers-somnia-docs)
- [Account Abstraction | Somnia Docs](#account-abstraction-somnia-docs)
- [Smart Wallet App with Thirdweb | Somnia Docs](#smart-wallet-app-with-thirdweb-somnia-docs)
- [Explorers | Somnia Docs](#explorers-somnia-docs)
- [Gas Configuration | Somnia Docs](#gas-configuration-somnia-docs)
- [SDKs | Somnia Docs](#sdks-somnia-docs)
- [Elix.fi | Somnia Docs](#elix-fi-somnia-docs)
- [Protofire Price Feeds | Somnia Docs](#protofire-price-feeds-somnia-docs)
- [Integrate Chainlink Oracles | Somnia Docs](#integrate-chainlink-oracles-somnia-docs)
- [On Ramps | Somnia Docs](#on-ramps-somnia-docs)
- [Somnia Gas Differences To Ethereum | Somnia Docs](#somnia-gas-differences-to-ethereum-somnia-docs)
- [READ Stream Data from a UI (Next.js Example) | Somnia Docs](#read-stream-data-from-a-ui-next-js-example-somnia-docs)
- [DAO UI Tutorial p2 | Somnia Docs](#dao-ui-tutorial-p2-somnia-docs)
- [Ecosystem Tools | Somnia Docs](#ecosystem-tools-somnia-docs)
- [Using Native SOMI/STT | Somnia Docs](#using-native-somi-stt-somnia-docs)
- [Safes | Somnia Docs](#safes-somnia-docs)
- [Debug Playbook | Somnia Docs](#debug-playbook-somnia-docs)
- [Authenticating with MetaMask | Somnia Docs](#authenticating-with-metamask-somnia-docs)
- [Tokos | Somnia Docs](#tokos-somnia-docs)
- [Building Subgraph UIs (NextJS/Fetch) | Somnia Docs](#building-subgraph-uis-nextjs-fetch-somnia-docs)
- [Authenticating with Privy | Somnia Docs](#authenticating-with-privy-somnia-docs)
- [Oracles | Somnia Docs](#oracles-somnia-docs)
- [Ormi Subgraph | Somnia Docs](#ormi-subgraph-somnia-docs)
- [Building Subgraph UIs (Apollo Client) | Somnia Docs](#building-subgraph-uis-apollo-client-somnia-docs)
- [Using Verifiable Randomness (VRF) | Somnia Docs](#using-verifiable-randomness-vrf-somnia-docs)
- [Node/Infra Security | Somnia Docs](#node-infra-security-somnia-docs)
- [Building a Simple DEX on Somnia | Somnia Docs](#building-a-simple-dex-on-somnia-somnia-docs)
- [Streams Case Study: Formula 1 | Somnia Docs](#streams-case-study-formula-1-somnia-docs)
- [Working with Multiple Publishers in a Shared Stream | Somnia Docs](#working-with-multiple-publishers-in-a-shared-stream-somnia-docs)
- [Local Testing and Forking | Somnia Docs](#local-testing-and-forking-somnia-docs)
- [Subgraphs | Somnia Docs](#subgraphs-somnia-docs)
- [Create ERC721 NFT Collections | Somnia Docs](#create-erc721-nft-collections-somnia-docs)
- [Somnia Exchange | Somnia Docs](#somnia-exchange-somnia-docs)
- [Using Data APIs (Ormi) | Somnia Docs](#using-data-apis-ormi-somnia-docs)
- [DAO Smart Contract | Somnia Docs](#dao-smart-contract-somnia-docs)
- [Protofire Subgraph | Somnia Docs](#protofire-subgraph-somnia-docs)
- [Responsible Disclosure Policy | Somnia Docs](#responsible-disclosure-policy-somnia-docs)
- [Listening to Blockchain Events (WebSocket) | Somnia Docs](#listening-to-blockchain-events-websocket-somnia-docs)
- [Managing NFT Metadata with IPFS | Somnia Docs](#managing-nft-metadata-with-ipfs-somnia-docs)
---
# Introduction | Somnia Docs

Somnia is a high-performance, cost-efficient EVM-compatible Layer 1 blockchain capable of processing over 1,000,000 transactions per second (TPS) with sub-second finality. It is suitable for serving millions of users and building real-time mass-consumer applications like games, social applications, metaverses, and more, all fully on-chain.
circle-check
Somnia Mainnet is LIVE. Visit the [Network Information Page](https://docs.somnia.network/developer/network-info#network-details)
for Network details.
circle-check
**Developers who are deploying Smart Contracts and need Somnia Test Tokens, STT**. Please join the [Discordarrow-up-right](https://discord.com/invite/somnia)
. Go to the `#dev-chat` channel, tag the Somnia DevRel, `@emma_odia`and request Test Tokens. You can also use the Faucet: [https://testnet.somnia.network/arrow-up-right](https://testnet.somnia.network/)
You can also email `[[email protected]](https://docs.somnia.network/cdn-cgi/l/email-protection) ` with a brief description of what you are building and your GitHub profile.
Somnia is supported by [Improbablearrow-up-right](https://www.improbable.io/)
and [MSquaredarrow-up-right](https://msquared.io/)
. Improbable will develop some of the key technical components of Somnia, including the Blockchain, but the project will require a large and active community to fulfill its vision.
[](https://docs.somnia.network/concepts/litepaper/somnia-mission)

Learn more about Somnia
[](https://docs.somnia.network/developer/network-info)

Start developing on Somnia
[](https://docs.somnia.network/get-started/connect-your-wallet-to-mainnet)

Connect To Mainnet
[NextGetting Started for Mainnetchevron-right](https://docs.somnia.network/get-started/getting-started-for-mainnet)
Last updated 1 month ago
---
# Introduction | Concepts | Somnia Docs

_Disclaimer: This document is for informational purposes and does not constitute an offer to sell or solicitation to buy any tokens or to participate in any way in Somnia Network. The information provided is accurate to the best of Somnia Foundation’s knowledge as of August 22, 2025. However, future-looking statements involve uncertainties and actual outcomes may differ. Somnia Foundation undertakes to update the information herein as required by law or when materially necessary. Prospective and current participants and/or token holders should conduct their own research in addition to reviewing this disclosure. By participating in Somnia Network or holding SOMI tokens, you acknowledge and accept the risks outlined herein._
Somnia is a high-performance, cost-efficient EVM-compatible Layer 1 blockchain capable of processing over 1,000,000 transactions per second (TPS) with sub-second finality. It is suitable for serving millions of users and building real-time mass-consumer applications like games, social applications, metaverses, and more, all fully on-chain.
The Somnia blockchain has achieved 1.05m TPS (ERC-20 swaps) running over 100 nodes distributed globally. During these benchmarks Somnia also achieved 50k uniswaps per second (across one pool) and 300k NFT mints per seconds (one NFT contract). These workloads we do not believe would be achievable by parallel EVM approaches.
Somnia has four key innovations in blockchain architecture to achieve this performance level:
* [Accelerated Sequential Execution](https://docs.somnia.network/concepts/somnia-blockchain/accelerated-sequential-execution)
- through compiled EVM bytecode.
* [IceDB](https://docs.somnia.network/concepts/somnia-blockchain/somnias-icedb)
- a faster, more predictable database for storing blockchain state.
* [MultiStream consensus](https://docs.somnia.network/concepts/somnia-blockchain/multistream-consensus)
- a proof-of-stake, partially synchronous BFT protocol inspired by [Autobahn BFTarrow-up-right](https://arxiv.org/pdf/2401.10369)
.
* [Advanced compression techniques](https://docs.somnia.network/concepts/somnia-blockchain/advanced-compression-techniques)
- to deal with increased node-to-node data traffic due to the throughput.
Somnia is supported by [Improbablearrow-up-right](https://www.improbable.io/)
and [MSquaredarrow-up-right](https://msquared.io/)
. Improbable will develop some of the key technical components of Somnia, including the Blockchain, but the project will require a large and active community to fulfil its vision.
The Somnia token SOMI is issued by the Somnia Token Co Ltd.
[NextSomnia-Missionchevron-right](https://docs.somnia.network/concepts/litepaper/somnia-mission)
Last updated 1 month ago
---
# Somnia-Mission | Concepts | Somnia Docs
To enable mass consumer real-time applications to be built at web2 scale with web3 properties, creating a more open, equitable internet.
###
[hashtag](https://docs.somnia.network/concepts/litepaper/somnia-mission#key-objectives)
Key Objectives
* Applications that can scale to web2 usage levels (millions of CCU, 100,000’s of transactions per second)
* Open censorship-resistant systems accessible to anyone with access to the internet
* No one counterparty controlling and hoarding value
* Composable systems where value is shared among the participants
* Free movement of people and assets between platforms so creators and users have freedom of choice
A more open and equitable world.
###
[hashtag](https://docs.somnia.network/concepts/litepaper/somnia-mission#core-principles)
Core Principles
####
[hashtag](https://docs.somnia.network/concepts/litepaper/somnia-mission#on-chain-experiences)
On-Chain Experiences
We are committed to building technology to make fully on-chain, real-time mass-scale applications possible and practical. We believe this is essential for creating a composable, open internet.
####
[hashtag](https://docs.somnia.network/concepts/litepaper/somnia-mission#multiverse-over-monolith)
Multiverse over Monolith
We believe in a collection of applications, each with distinct, interconnected experiences, much like countries within a global community, sharing utilities that enhance mutual growth.
####
[hashtag](https://docs.somnia.network/concepts/litepaper/somnia-mission#empowering-composability)
Empowering Composability
Central to our vision is the principle of composability—the ability for builders to build upon each other’s work, creating a culture of collaboration in which the collective output surpasses the sum of its parts.
####
[hashtag](https://docs.somnia.network/concepts/litepaper/somnia-mission#builder-empowerment)
Builder Empowerment
We will empower builders with the freedom to build sustainable models of engagement and ownership. This includes safeguarding the rights to digital assets and creating an environment where innovation is not stifled by platform constraints.
####
[hashtag](https://docs.somnia.network/concepts/litepaper/somnia-mission#accessibility-for-all)
Accessibility for All
The promise of the virtual society is in its potential for universal access, removing barriers for content creation and participation. Leveling the playing field such that anyone with an internet connection can participate and succeed.
[PreviousIntroductionchevron-left](https://docs.somnia.network/concepts)
[NextProblemchevron-right](https://docs.somnia.network/concepts/litepaper/problem)
Last updated 1 month ago
* [Key Objectives](https://docs.somnia.network/concepts/litepaper/somnia-mission#key-objectives)
* [Core Principles](https://docs.somnia.network/concepts/litepaper/somnia-mission#core-principles)
---
# Overview | Concepts | Somnia Docs
The Somnia blockchain has many innovations that enable it to increase performance by several orders of magnitude compared to other EVM chains:
* [MultiStream consensus](https://docs.somnia.network/concepts/somnia-blockchain/multistream-consensus)
- a proof-of-stake, partially synchronous BFT protocol inspired by [Autobahn BFTarrow-up-right](https://arxiv.org/pdf/2401.10369)
.
* Independent Data Chains - Each validator operates its own blockchain, or “data chain,” which allows for independent block production. This unique approach eliminates the need for a consensus mechanism within individual data chains, streamlining the data processing workflow.
* Consensus Chain - A separate blockchain aggregates the heads of all data chains, employing a modified PBFT algorithm for proof of stake consensus. This structure decouples data production from the consensus process, significantly enhancing overall efficiency.
* [Compiled Bytecode](https://docs.somnia.network/concepts/somnia-blockchain/accelerated-sequential-execution)
- By translating EVM bytecode to highly optimised native code, Somnia achieves execution speeds close to hand-written C++ contracts, facilitating the execution of millions of transactions per second on a single core.
* [Faster and predictable database performance](https://docs.somnia.network/concepts/somnia-blockchain/somnias-icedb)
- Somnia has a custom database called IceDB. It employs performance reports for predictable read and write performance as well as a custom database architecture that enables average read/write operations 15-100 nanoseconds with built in snapshotting.
* [Advanced Compression Techniques](https://docs.somnia.network/concepts/somnia-blockchain/advanced-compression-techniques)
- The Somnia data chain architecture is designed to enable streaming compression in order to maximise data throughput. Somnia combines this with [BLS signaturearrow-up-right](https://www.ietf.org/archive/id/draft-irtf-cfrg-bls-signature-05.html)
aggregation in order to achieve extremely high compression ratios, allowing for massive transaction data throughput. This allows theoretical performance above other preported [“limits due to bandwidth”arrow-up-right](https://revelointel.com/lightspeed-how-monad-is-superscaling-the-evm-with-keone-and-kevin-g/)
.
[PreviousProblemchevron-left](https://docs.somnia.network/concepts/litepaper/problem)
[NextMultiStream Consensuschevron-right](https://docs.somnia.network/concepts/somnia-blockchain/multistream-consensus)
Last updated 1 month ago
---
# MultiStream Consensus | Concepts | Somnia Docs

Image showing conesus algorithm
In Somnia, every validator publishes their own blockchain, their data chain. This innovation was inspired by the 2024 whitepaper “[Autobahn: Seamless high speed BFTarrow-up-right](https://arxiv.org/pdf/2401.10369)
”. Every data chain is completely independent, and each block in a data chain contains a blob of bytes. Only the owning validator ever adds blocks to their data chain, and there are no safety mechanisms in place to avoid them forking their data chain or proposing invalid blocks. In other words, the data chains have no consensus mechanism at all.
Somnia then includes a **consensus chain**, with each consensus block including the current head of every data chain. This chain uses a modified PBFT consensus algorithm and is a typical proof of stake consensus setup. The consensus chain, including the tip of each data chain, provides full security against validators forking their own data chain. Each consensus block then semantically includes all of the transactions in all of the data chains whose tip it advanced. A deterministic pseudorandom ordering of these data chains then provides a single globally ordered stream of bytes to be executed across all data chains.
This setup completely decouples the production and distribution of new data (advancements of the data chains) with the consensus algorithm. It has a number of crucial benefits described below in the Advanced Compression Techniques section (e.g. streaming compression), which let this data frontend reach almost a gigabit per second of published transaction data.
[PreviousOverviewchevron-left](https://docs.somnia.network/concepts/somnia-blockchain/overview)
[NextAccelerated Sequential Executionchevron-right](https://docs.somnia.network/concepts/somnia-blockchain/accelerated-sequential-execution)
---
# Somnia's IceDB | Concepts | Somnia Docs
Somnia also includes its own database called IceDB. It has three key properties:
1\. Deterministic performance
2\. In-memory cache with read promotions
3\. Built-in snapshotting
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/somnias-icedb#deterministic-performance)
**Deterministic Performance**
--------------------------------------------------------------------------------------------------------------------------------------------
Most blockchains use an embedded database like [LevelsDBarrow-up-right](https://github.com/google/leveldb)
or [RocksDBarrow-up-right](https://rocksdb.org/)
. These databases are often optimised for write throughput and eventually flush all written data to disk to maintain a steady RAM load.
This means that reads from these databases can hit RAM or many different places on disk. Depending on where the value is stored, the latency difference is enormous, differing by up to 1000x.
So, how much gas should a user be charged for a read? Should we assume the worst-case scenario that every read hits disk multiple times? Or should we assume some amount will hit memory? The former drastically limits the speed of your blockchain, and the latter allows an attacker to critically slow down your chain.
Could we time the read on each node and charge the user based on how long the request took? Unfortunately not, as this would not be deterministic across nodes (LevelsDB, RocksDB, etc., make no attempt to store data in deterministic places). Therefore, we can not change the gas based on this information.
Somnia's IceDb has been built from the ground up to have fully deterministic performance. Every read and write to IceDb returns the result and a "performance report". This report details exactly how many cold cache lines were read from RAM, and exactly how many disk pages were read from the SSD.
Due to this information being deterministic, we can charge the user based on the actual load they put on the system. Reads that accessed frequent data (and therefore hit RAM) use less gas, and we can then fit more into the block.
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/somnias-icedb#improved-read-write-cache)
**Improved read/write cache**
--------------------------------------------------------------------------------------------------------------------------------------------
As mentioned above, databases that persist to the disk often have an in-memory cache which attempts to store frequently accessed data in RAM, to serve frequent data with a much lower latency.
Most databases tend to optimise for either read or write. With Somnia, we have created a cache that can do both. This enables the average read/writes of IceDB to be between 15-100 nanoseconds.
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/somnias-icedb#built-in-snapshotting)
**Built-in Snapshotting**
------------------------------------------------------------------------------------------------------------------------------------
Blockchains require all nodes to periodically agree on exactly which state is in the blockchain after a given block.
Most blockchains use a data structure such as a Merkle tree to retrieve a single-state hash that encapsulates all of the data in the blockchain. Merkle trees allow this hash to be updated with every write with reasonable performance while allowing proofs of a particular piece of state to be generated for parties without access to the database.
Most blockchains then store each node in this Merkle tree as standard keys into their database. This makes reads or writes from this tree very expensive, as every single node can result in massive latencies from these embedded databases.
The reality, however, is the data structure that these databases use under the hood (a log structured merge tree) already lends itself to a tree formation, with vast sections stored immutably on disk. IceDB utilises this underlying immutable structure to support first class state snapshots, without needing the user space execution engine to store a Merkle tree using the databases' key value abstraction.
This massively accelerates the performance of IceDB, and reduces the amount of overhead for each value.
[PreviousAccelerated Sequential Executionchevron-left](https://docs.somnia.network/concepts/somnia-blockchain/accelerated-sequential-execution)
[NextAdvanced Compression Techniqueschevron-right](https://docs.somnia.network/concepts/somnia-blockchain/advanced-compression-techniques)
* [Deterministic Performance](https://docs.somnia.network/concepts/somnia-blockchain/somnias-icedb#deterministic-performance)
* [Improved read/write cache](https://docs.somnia.network/concepts/somnia-blockchain/somnias-icedb#improved-read-write-cache)
* [Built-in Snapshotting](https://docs.somnia.network/concepts/somnia-blockchain/somnias-icedb#built-in-snapshotting)
---
# Problem | Concepts | Somnia Docs
Web3 has created a new era of decentralised finance, democratising financial access. However, it has fallen short in generating mass consumer applications and remains largely centred around finance. We believe blockchain technology is a cornerstone for the future of new, open Internet applications.
Today, there are limits to what you can build on-chain. Many factors constrain this, from the cost of running applications to the fundamental performance limitations of existing blockchains. We believe that new technologies can unlock a new class of real-time applications that normally must be built on Web2 foundations. Enabling these systems to be built on-chain will allow the free movement of businesses and users between online platforms, creating what we call a true virtual society.
We are building Somnia, a fast and cost-effective EVM-based blockchain, to achieve this. Somnia is a layer-one blockchain with full EVM compatibility, capable of processing over 1,000,000 transactions per second with sub-second finality and low fees. This will unlock a new wave of on-chain applications. Initially, we are focusing on gaming, metaverse, and social experiences. The use cases will likely extend far beyond these sectors. We are not even 100% certain of what can be built with the technology we are creating.
[hashtag](https://docs.somnia.network/concepts/litepaper/problem#the-current-state)
The Current State
----------------------------------------------------------------------------------------------------------
Although many blockchains support the EVM bytecode standard, they all leave much to be desired from a throughput perspective. Below are transactions per second (TPS) figures for a typical UniswapV3 transaction (Dex swaps per second (DPS)), which is 130k gas. As you can see, most chains offer up to ~200 DPS, or ~20M DPS per day. These numbers were taken from [this articlearrow-up-right](https://medium.com/dragonfly-research/the-amm-test-a-no-bs-look-at-l1-performance-4c8c2129d581)
:
Average DPS
Max DPS
Finality
ETH
9.19
18.38
66s
Polygon
47.67
95.33
Variable
AVAX
31.65
175.68
3.7s
BNB
194.6
194.6
75s
These are somewhat similar to real world observed numbers ([taken from Chaininspectarrow-up-right](https://chainspect.app/dashboard)
). Note that Uniswaps are a more complex user interaction and are composed of many transactions. Thus these numbers will be higher than above.
Name
Max Recorded TPS
Block Time
Time to finality
Base
293
2s
16m
BNB Chain
1731
3s
7.5s
Polygon
429
2.22s
4m 16s
Arbitrum
944
0.25s
16m
Ethereum
62.34
12.08
16m
Optimism
67.41
2s
16m
Avalanche
92.74
2.03s
0s
More recent benchmarks look as gas per second.

These numbers are taken from [this articlearrow-up-right](https://www.paradigm.xyz/2024/04/reth-perf)
. To translate this into DPS, it’s about 6.5 DPS for every 1mg/s. So even the fastest chain on this list has a theoretical limit of 650 DPS (about 3700 TPS).
If you go outside of the EVM ecosystem, you can do a lot better. Note that this TPS is not the same as above, as the tests differ.
TPS
Finality
Aptos
30,000 ([citearrow-up-right](https://aptoslabs.medium.com/previewnet-ensuring-scalability-and-reliability-of-the-aptos-network-48f0d210e8fe)
)
0.9s ([citearrow-up-right](https://twitter.com/Aptos/status/1632801717937922052)
)
SUI
11,000 – 297,000 ([citearrow-up-right](https://blog.sui.io/sui-performance-update/)
)
480m/s ([citearrow-up-right](https://blog.sui.io/sui-performance-update/)
)
Solana
1608 ([citearrow-up-right](https://chainspect.app/dashboard)
)
12.8s ([citearrow-up-right](https://chainspect.app/dashboard)
)
Aptos and SUI's real-world live tests have not hit these levels yet. These numbers are based on their benchmarks. The Solana numbers differ from those on the Solana webpage as we are using the real-world observed numbers. Using the same methodology as the article above, Solana’s numbers are significantly worse at 273.34 swaps per second.
Despite this performance increase, we believe the EVM is important. There is already a rich ecosystem of developers and content around the EVM. Not being able to access that ecosystem seems like a missed opportunity. These chains had to make that tradeoff to create the needed performance. But what if we could have similar performance on the EVM?
The bottlenecks we see today with approaches on blockchain implemented with the EVM today are:
* **Execution speed** – The rate at which smart contract code can be executed, and block creation can occur.
* **Storage** — Retrieval of storing historical data of a chain. Ethereum [EIP-4844arrow-up-right](https://www.eip4844.com/)
was a recent upgrade that significantly improved the cost of storage for ETH and other L2s. However, we still need improvements in reading and writing data to blockchains.
* **Bandwidth** — The amount of bandwidth needed to send data between nodes on the network when running at high transaction levels.
[PreviousSomnia-Missionchevron-left](https://docs.somnia.network/concepts/litepaper/somnia-mission)
[NextOverviewchevron-right](https://docs.somnia.network/concepts/somnia-blockchain/overview)
Last updated 6 months ago
---
# Accelerated Sequential Execution | Concepts | Somnia Docs
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/accelerated-sequential-execution#the-problem-with-parallel-execution)
**The Problem With Parallel Execution**
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
A large number of modern blockchains have attempted to scale their execution using parallelism. This means they execute transactions which are unrelated, on different cores. This can work well when the transactions in a block are unrelated, but breaks down when those transactions modify the same state.
The reality, however, is that load spikes mostly happen when there is some event which has caused the spike. This implies a massive correlation between the transactions, which is not normally there in day to day execution.
For example:
One of Ethereum's biggest load spikes was the Otherside Otherdeed mint. When this happened, the vast majority of all transactions in each block were all modifying the same state ([as they were minting Otherdeedsarrow-up-right](https://decrypt.co/99219/otherside-nft-mint-burned-more-157m-ethereum)
). Parallel execution would not have worked here.
A DEX will contain trading on many unrelated asset pairs throughout each day, but their real load spikes will come from volatility on a specific asset pair. These trades will all modify the same state, meaning parallel execution would not have helped.

Parallel works well for many individual apps or accounts, swapping tokens

When you have a hot path with many threads touching the same state parallel breaks down
In other words, **parallel execution breaks down exactly when you need it.**
This observation is why Somnia has opted to make a single core go extremely fast **instead of relying on parallel execution.**

So, how does Somnia make a single core go this fast?
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/accelerated-sequential-execution#evm-compilation)
**EVM Compilation**
-------------------------------------------------------------------------------------------------------------------------------------------
Somnia executes EVM transactions. It scales by reaching a very high single-core speed instead of attempting to parallelise over multiple cores.
The EVM is a relatively simple stack-based architecture. Due to the implicit stack, stack-based bytecodes are often naturally smaller in size than register-based bytecodes, but they contain many redundant operations.
There are broadly two ways to execute a VM's bytecode, interpreted or native (which itself can be split into JIT or AOT compilation). The former effectively simulates the VM itself in software, while the latter translates the bytecode to native instructions, which your CPU then executes directly.
Ethereum and most EVM blockchains run an interpreted VM to execute EVM bytecode. This is often a relatively naive implementation which keeps its own stack, loops through each operation, and looks up the functionality of each operation in a lookup table. This is very slow compared to native execution.
Further, Solidity and other Ethereum compilers often optimise for bytecode size over gas. For example, they will include code to generate large constants instead of including them inline. This ends up including a large amount of runtime computation that can actually be statically resolved at compile time.
To exploit these redundancies, Somnia includes its own EVM compiler, which translates EVM bytecode to x86. This approaches near-native speed (native speed being the equivalent functionality handwritten in C++). In benchmarks, this can execute ERC-20 transfers in hundreds of nanoseconds, achieving millions of TPS on a single core.
However, there is no free lunch as the compilation process is relatively expensive. For this reason, you would only do this on contracts that are called frequently, falling back to standard interpreted EVM on the rest.
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/accelerated-sequential-execution#hardware-level-parallelism)
**Hardware Level Parallelism**
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
As mentioned above, "parallel execution", attempting to run unrelated transactions on different cores, breaks down when you need it the most because it cannot handle correlated transactions, which are much more common during load spikes. This type of parallelism is also known as "software parallelism".
For this reason, Somnia does not attempt to parallelise unrelated transactions. It does, however, enable the CPU to hardware parallelise each individual transaction.
This harnesses the single core parallelism available in modern CPUs to speed up every individual transaction, meaning this technique also works for transactions which modify the same state, helping during the load spikes as well.
So, what is hardware-level parallelism?

_Diagram showing the difference between sequential execution (left) and hardware parralised execution (right)_
Modern CPU cores give the appearance that they execute assembly instructions in order, implementing control flow primitives by jumping around the assembly. The reality is that they are actually executing these instructions completely out of order, and often in parallel with each other.
For example, when reading a value from memory, the CPU will run ahead and work on future computation while waiting for the result. This is all completely invisible to the developer. This can cause massive speedups. Consider the case of an ERC-20 token swap. The program, at a high level, completes the following steps in the first half of it's execution:
1\. Hash the sender account.
2\. Lookup the sender balance using this hash.
3\. Hash the receiver account.
4\. Lookup the receiver balance using this hash.
5\. ...
Let's say the speed to hash an account is 150ns, and the speed to lookup the sender balance is 100ns (a single RAM read).
If the CPU ran these steps in serial, this would take 150 + 100 + 150 + 100 = 500ns. However, in practice, if these steps are compiled to native code, your CPU hardware would execute 1 and 2 completely in parallel to 3 and 4, causing the execution speed to be 250ns, double the speed.
The problem is, interpreting the EVM stops your CPU core from executing assembly in parallel. To really harness the available silicon in your CPU, you need to execute native assembly. Somnia's compiler and database are built to enable hardware-level parallelism of smart contracts.
[PreviousMultiStream Consensuschevron-left](https://docs.somnia.network/concepts/somnia-blockchain/multistream-consensus)
[NextSomnia's IceDBchevron-right](https://docs.somnia.network/concepts/somnia-blockchain/somnias-icedb)
* [The Problem With Parallel Execution](https://docs.somnia.network/concepts/somnia-blockchain/accelerated-sequential-execution#the-problem-with-parallel-execution)
* [EVM Compilation](https://docs.somnia.network/concepts/somnia-blockchain/accelerated-sequential-execution#evm-compilation)
* [Hardware Level Parallelism](https://docs.somnia.network/concepts/somnia-blockchain/accelerated-sequential-execution#hardware-level-parallelism)
---
# Use Cases | Concepts | Somnia Docs
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/use-cases#gaming-fully-on-chain-forever-evolving-games)
**Gaming - Fully On-Chain, Forever Evolving Games**
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Somnia enables fully on-chain games where creators can easily build, modify, and extend games. Developers can create games that live forever on-chain, allowing players to own their in-game assets, modding systems, and worlds that grow continuously without centralized control.
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/use-cases#socialfi-true-ownership-for-social-media-accounts)
**SocialFi - True Ownership for Social Media Accounts**
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Somnia powers full on-chain social media platforms where users own their accounts and data. Creators aren’t locked into one platform and can freely port their content and followers between ecosystems, ensuring freedom and control.
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/use-cases#metaverse-building-interoperable-virtual-socieities)
**Metaverse - Building Interoperable Virtual Socieities**
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Somnia is the backbone for metaverse applications where entire economies and ecosystems are built with on-chain ownership and logic. Developers can create worlds with seamless interoperability, allowing assets, avatars, and experiences to cross over between multiple virtual environments.
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/use-cases#defi-fully-on-chain-limit-order-books-lobs)
**DeFi - Fully On-Chain Limit Order Books (LOBs)**
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Somnia’s performance enables fully on-chain limit order books (LOBs), offering price discovery and order matching similar to centralized exchanges but with full transparency and self-custody. This innovation brings more efficiency and fairness to decentralized finance (DeFi).
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/use-cases#real-time-applications-powering-real-time-large-scale-applications)
**Real-Time Applications - Powering Real-Time, Large-Scale Applications**
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Somnia’s ultra-fast processing and sub-second finality are designed for real-time, mass-consumer applications. This goes beyond the categories above to places we have not imagined, Ultimately any web2 style application can now be built on web3 rails, offering the best of both worlds. We don’t even know all the possibilities yet and would love for builders to help us discover what is now possible when limits are removed.
[PreviousSecuritychevron-left](https://docs.somnia.network/concepts/somnia-blockchain/security)
[NextOn-Chain Reactivitychevron-right](https://docs.somnia.network/concepts/somnia-blockchain/on-chain-reactivity)
* [Gaming - Fully On-Chain, Forever Evolving Games](https://docs.somnia.network/concepts/somnia-blockchain/use-cases#gaming-fully-on-chain-forever-evolving-games)
* [SocialFi - True Ownership for Social Media Accounts](https://docs.somnia.network/concepts/somnia-blockchain/use-cases#socialfi-true-ownership-for-social-media-accounts)
* [Metaverse - Building Interoperable Virtual Socieities](https://docs.somnia.network/concepts/somnia-blockchain/use-cases#metaverse-building-interoperable-virtual-socieities)
* [DeFi - Fully On-Chain Limit Order Books (LOBs)](https://docs.somnia.network/concepts/somnia-blockchain/use-cases#defi-fully-on-chain-limit-order-books-lobs)
* [Real-Time Applications - Powering Real-Time, Large-Scale Applications](https://docs.somnia.network/concepts/somnia-blockchain/use-cases#real-time-applications-powering-real-time-large-scale-applications)
---
# On-Chain Reactivity | Concepts | Somnia Docs
circle-exclamation
**Somnia Reactivity is currently only available on TESTNET**
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/on-chain-reactivity#the-missing-link)
The Missing Link
----------------------------------------------------------------------------------------------------------------------------
Blockchains have historically had a major limitation: they are passive. They are excellent ledgers, but they are deaf and mute until a user kicks them into action with a transaction. A smart contract cannot "wake up" when a price changes, specific data shows up or when a specific event occurs.
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/on-chain-reactivity#the-usability)
The Usability
----------------------------------------------------------------------------------------------------------------------
Imagine the world where you can instantly react to events happening on blockchain:
* **DeFi**: Automated responses to price changes, liquidations, or interest accruals. For instance, a lending protocol might need to adjust collateral requirements instantly based on market volatility.
* **Gaming**: Real-time updates for player achievements, in-game economies, or multiplayer interactions. Imagine a blockchain game where earning an NFT trophy automatically triggers rewards or level-ups without off-chain servers.
* **Business Logic**: Supply chain tracking where an item's arrival (logged as an event) auto-initiates payments or audits. Or in insurance, a smart contract that pays out claims upon verified on-chain data like weather reports.
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/on-chain-reactivity#the-setup)
The Setup
--------------------------------------------------------------------------------------------------------------
The blockchain is ledger of transaction modifying the common state. Some of these transaction might emit a log, which is defined as an observable event:
spinner
Traditionally the developer would need to create an infrastructure that constantly monitors the blockchain state and event through RPC calls. Then based on the logic the custom app would create a transaction and post it for the inclusion at the next available block:
spinner
As expected this created a large overhead for a typical development team. Additionally it did not meet the requirements of decentralisation, trustlessness, immediate reaction and reliable event handling.
This is why we saw a plethora of services known as indexers or Web3 hooks:
spinner
They alleviate a lot of issues related to the infrastructure, at the same time they add new issues related to the domain-specific configuration or language. Still the key requirements are still not met:
* indexers are centralised and trustfull
* the event handling trasaction can only be included in the next available block
* it is not MEV-resistant
* it's only as reliable as the indexer provider
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/on-chain-reactivity#the-solution)
The Solution
--------------------------------------------------------------------------------------------------------------------
Enter **Somnia Native On-Chain Reactivity**.
This isn't just an incremental improvement; it's a paradigm shift that enables real-time, trustless and decentralised reactions without relying on external systems:
spinner
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/on-chain-reactivity#the-benefits)
The Benefits
--------------------------------------------------------------------------------------------------------------------
Once an event is defined, it's handling is happening automatically by blockchain nodes:
* **Real-Time**: Reaction included in the same block.
* **Decentralised**: As decentralised as the blockchain itself.
* **Trustlessness**: No trusted party required.
* **MEV**: Fully MEV-resistant due to deterministic inclusion of event handlers
* **Developer-Friendly**: Familiar paradigms (e.g., subscriptions akin to event listeners in traditional programming) and interface reduce complexity.
* **Cost Efficiency**: Optimized gas usage minimize expenses compared to infrastucture required for constant off-chain monitoring.
* **Security and Resilience**: Backed by the blockchain itself. Reduces attack surfaces by eliminating external dependencies; nested events allow complex chains without intermediaries.
* **Scalability**: Supports high-throughput apps, as seen in Somnia's 1M+ TPS capability, making it ideal for mass-adoption scenarios.
* * *
###
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/on-chain-reactivity#comparison-table)
Comparison Table
Aspect
Traditional RPC
Indexer/Hooks
Somnia Reactivity
**Timing**
Next block
Next block
Same block
**Decentralization**
Developer infra
Centralized service
Fully decentralized
**Trust Model**
Self-hosted
Trusted third party
Trustless
**MEV Resistance**
❌
❌
✅
**Infrastructure**
High complexity
Medium complexity
Zero external infra
**Reliability**
Self-managed
Service-dependent
Blockchain-native
[PreviousUse Caseschevron-left](https://docs.somnia.network/concepts/somnia-blockchain/use-cases)
[NextOverviewchevron-right](https://docs.somnia.network/concepts/tokenomics/overview)
Last updated 1 month ago
* [The Missing Link](https://docs.somnia.network/concepts/somnia-blockchain/on-chain-reactivity#the-missing-link)
* [The Usability](https://docs.somnia.network/concepts/somnia-blockchain/on-chain-reactivity#the-usability)
* [The Setup](https://docs.somnia.network/concepts/somnia-blockchain/on-chain-reactivity#the-setup)
* [The Solution](https://docs.somnia.network/concepts/somnia-blockchain/on-chain-reactivity#the-solution)
* [The Benefits](https://docs.somnia.network/concepts/somnia-blockchain/on-chain-reactivity#the-benefits)
* [Comparison Table](https://docs.somnia.network/concepts/somnia-blockchain/on-chain-reactivity#comparison-table)
---
# Security | Concepts | Somnia Docs
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/security#decentralisation)
**Decentralisation**
---------------------------------------------------------------------------------------------------------------------
Somnia philosophically believes in having sufficiently decentralised services, not maximally decentralised. What this means is that you have enough decentralisation of infrastructure to enable the good properties of decentralisation (increased security, censorship resistance, no single owner/counterparty) whilst not trading off to degrade performance significantly (all decentralisation will inherently decrease performance [see blockchain trilemaarrow-up-right](https://ieeexplore.ieee.org/abstract/document/10549891)
).
For Somnia, the main validators of the network will be targeting hardware specs between a Solana and Aptos node. This will allow a large group of participants to join the network but not have sub-par hardware and connectivity. This will ensure the high level of performance needed for real-time mass-consumer applications. There will initially be 100 globally distributed validator nodes. We expect this to grow as the network matures. We also incentivize decentralisation and global footprint for the chain through our tokenomics.
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/security#cuthbert)
Cuthbert
-------------------------------------------------------------------------------------------------
Cuthbert is a separate implementation of Somnia's execution and database, using third party libraries wherever possible, and without any optimisations. Somnia validators automatically run every transaction through both Somnia and Cuthbert, and they will stop voting or executing if they ever detect a divergence between the two. This means that if the Somnia client has a bug or an issue in its execution or database, that bug would also have to be present in the separate Cuthbert implementation if this bug were to go undetected. Cuthbert will eventually be phased out of Somnia as the system becomes more mature.
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/security#securing-the-network)
**Securing The Network**
-----------------------------------------------------------------------------------------------------------------------------
As stated in the introduction the network is secured by validators staking tokens to participate in the network. This is a PoS network similar to other major blockchain networks (e.g. ETH). Node providers are subject to slashing if they act maliciously against the network. This will be further explored in our tokenomics.
[PreviousAdvanced Compression Techniqueschevron-left](https://docs.somnia.network/concepts/somnia-blockchain/advanced-compression-techniques)
[NextUse Caseschevron-right](https://docs.somnia.network/concepts/somnia-blockchain/use-cases)
Last updated 7 months ago
* [Decentralisation](https://docs.somnia.network/concepts/somnia-blockchain/security#decentralisation)
* [Cuthbert](https://docs.somnia.network/concepts/somnia-blockchain/security#cuthbert)
* [Securing The Network](https://docs.somnia.network/concepts/somnia-blockchain/security#securing-the-network)
---
# Advanced Compression Techniques | Concepts | Somnia Docs
When you have a lot of transactions, a lot of data needs to go between nodes

Once you are getting to the world of 100,000’s or even millions of transactions per second you start creating a lot of data.
A standard ERC-20 transfer is about 200 bytes when you consider all its parts. Now, imagine we are doing 1 million ERC-20 swaps per second. That’s 190 MBytes/s or 1.5 GBits/s. It's not going to pass over the public Internet, so you are either going to have a very centralised chain or limit your transaction rate.
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/advanced-compression-techniques#power-law-distribution)
**Power law distribution**
--------------------------------------------------------------------------------------------------------------------------------------------------------

Image showing distribution of the power law
However, there is a lot of redundant information in those bytes. The number of bits theoretically required to send a piece of information is the logarithm of its probability of occuring. In practice, the probability distribution of which account is making a transaction, or which contract is being executed, or the arguments to the method being called, is very sharp (often a power law). This means that a minority of those accounts or contracts are highly likely to occur in the data to be sent. For example, if a particular contract was being called by 10% of transactions, its address can be encoded in 3.3 bits. That is a 48x compression ratio on the uncompressed 20 byte address.
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/advanced-compression-techniques#streaming-compression)
**Streaming Compression**
------------------------------------------------------------------------------------------------------------------------------------------------------
This leads to the question of how we can effectively compress the transaction data moving between nodes. There are broadly two forms of compression:
* Block compression
* Streaming compression
Block compression is given a single block of data, and compresses in a way where the receiver only needs that block of data to decompress it. This is the typical form of compression, such as zip or tar files. It is very convenient because the compressor doesn't need to make assumptions about what other information the client has, or the order that information was sent.
Streaming compression is able to assume that the sender and receiver both share an identical history of the data that was compressed and decompressed, and it uses this assumption to build a large amount of internal implicit information which never needs to be sent over the wire. It can say, "Use the address from 3.456 megabytes ago". For this reason, it achieves much better compression ratios than block compression. The downside, though, is that the sender and receiver must share an identical stream of data, and the 'implicit' data cannot be moved across machines or processes without using a lot of bandwidth, meaning the same process must compress the full stream of data (there are ways around this depending on the compression algorithm but it often requires a large amount of CPU time).
This poses a problem for a blockchain, which typically has each block proposed by a different machine. It forces blockchains to use block-based compression, if anything, significantly impacting the compression ratios they can achieve.
Somnia has a consensus and data availability algorithm designed to support streaming-based compression. Each validator is responsible for publishing their own stream of data to their own blockchain. These are the data chains introduced earlier in this section. The fact that data chains use the same process for publishing this stream unlocks the ability for streaming compression.
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/advanced-compression-techniques#hashes-and-signatures)
**Hashes and Signatures**
------------------------------------------------------------------------------------------------------------------------------------------------------
A lot of data in Ethereum transactions have a tight power law distribution, making them very compressible. There are, however two important exceptions: hashes and signatures. By definition, these completely change with a uniform distribution if any bit in the transaction is different, making them completely uncompressable (no two executed Ethereum transactions are identical due to nonces, which avoid replay attacks).
Transaction hashes are easy: they are, again by definition, reproducible based on the transaction data itself, so they can simply not be sent (a receiving client is required to recalculate them anyway).
Signatures are more challenging. They are required to be sent alongside each transaction. Due to them being uncompressable, this would heavily limit the compression ratios we are able to achieve on transaction data.
However, we can aggregate signatures if we use the BLS signature scheme. BLS signatures can aggregate any number of BLS signatures into a single signature, effectively achieving a constant size for any number of transaction signatures.
Somnia uses BLS signature aggregation for signature verification speed and, crucially, because it enables a far better compression ratio.

Image explaining BLS signature scheme
This aggregate cryptography is an optional mechanism to submit batches of transactions, where the cost to verify the signatures of all transactions in the batch is similar to the cost of verifying just one transaction. Using large batches this reduces the cost of signature verification by many orders of magnitude.
[hashtag](https://docs.somnia.network/concepts/somnia-blockchain/advanced-compression-techniques#bandwidth-symmetry)
**Bandwidth Symmetry**
------------------------------------------------------------------------------------------------------------------------------------------------
In a typical blockchain, one validator is responsible for proposing each block and therefore, publishing the transaction data for that 'slot' of time to all of its peers. If the blockchain throughput is X bytes per second, and there are B blocks per second and N peers, the leader must send N \* X / B bytes in 1 / B seconds, requiring an upload speed of N \* X bytes per second (some blockchains will fan out in a tree architecture, which still requires the branching factor multiplied by X ).
In comparison, due to Somnia having all peers publish their own shard of data, each peer is responsible for publishing X / (N \* B) bytes in 1 / B seconds, requiring an upload speed of X bytes per second. They also need to download everybody else's data for each block, requiring a download of N \* (X / N) bytes per second, which is just X.
Note that no less data is being sent overall, but the bandwidth profile has been amortised to be symmetrical across all peers at all times. No peer ever needs to upload at a faster rate than the bandwidth of the blockchain itself, but they need to do this constantly instead of only when they propose a block. This enables the overall blockchain throughput to reach much closer to the bandwidth of the peers themselves.
[PreviousSomnia's IceDBchevron-left](https://docs.somnia.network/concepts/somnia-blockchain/somnias-icedb)
[NextSecuritychevron-right](https://docs.somnia.network/concepts/somnia-blockchain/security)
* [Power law distribution](https://docs.somnia.network/concepts/somnia-blockchain/advanced-compression-techniques#power-law-distribution)
* [Streaming Compression](https://docs.somnia.network/concepts/somnia-blockchain/advanced-compression-techniques#streaming-compression)
* [Hashes and Signatures](https://docs.somnia.network/concepts/somnia-blockchain/advanced-compression-techniques#hashes-and-signatures)
* [Bandwidth Symmetry](https://docs.somnia.network/concepts/somnia-blockchain/advanced-compression-techniques#bandwidth-symmetry)
---
# Allocation and unlocks | Concepts | Somnia Docs
[hashtag](https://docs.somnia.network/concepts/tokenomics/allocation-and-unlocks#allocations)
Allocations
--------------------------------------------------------------------------------------------------------------
ALLOCATION
% OF TOKENS
NO. OF TOKENS
% AT TGE
CLIFF (MNTHS)
VESTING (MNTHS)
1\. Team
11%
110,000,000
0%
12
48
2\. Launch Partners
15%
150,000,000
0%
12
48
2\. Investors
15.15%
151,500,000
0%
12
36
3\. Advisors
3.58%
35,800,000
0%
12
36
4\. Ecosystem
27.345%
273,450,000
5.075%
0
48
5\. Community
27.925%
279,250,000
10.945%
0
36
TOTAL %
100%
1,000,000,000
16.02%
* Team - Allocation to early team members and founders
* Launch partners - Allocation to early contributors to the Somnia ecosystem
* Investors - Allocation to seed investors
* Advisors - Allocation to key Advisors for Somnia
* Ecosystem - Allocation to be used to develop the Somnia ecosystem and fund the foundation
* Community - Allocation for users who contributed to the Somnia ecosystem, validator rewards and liquidity.
[hashtag](https://docs.somnia.network/concepts/tokenomics/allocation-and-unlocks#unlock-schedule)
Unlock Schedule
----------------------------------------------------------------------------------------------------------------------
The SOMI token unlock schedule chart below illustrates the gradual release of tokens over a period of 48 months post-launch. The circulating supply is segmented into five categories, each contributing to the overall supply incrementally:
Community (Light blue): The largest allocation, represented by the light area. This will be used for community initiatives, including liquidity and airdrops. This will have 10.945% unlocked a TGE then a increased unlock at M1 and M2, then monthly vesting linear for 36 months.
Ecosystem (Orange): The ecosystem allocation, depicted in orange, reflects the gradual and consistent release of tokens intended to fuel ecosystem development and partnerships. This allocation has linear linear 48 months vesting. 5.075% will be unlocked at TGE
Investors (Yellow): Represented by the yellow area, investor tokens start release at month 12 and vest monthly afterwards for 36 months.
Advisors (Green): Key advisors to the project have a 12-month cliff with a 36-month linear vest.
Launch Partners (Red): The red area illustrates the allocation for launch partners. This allocation has a 12-month cliff with a 48-month linear vest. These are allocated to early contributors to the Somnia ecosystem (eg Improbable).
Team (Blue): The team allocation is shown in blue. This has a 12-month cliff with a 48-month linear vest. This gradual increase ensures alignment with the project's long-term goals and incentivizes sustained contribution.
The chart demonstrates the planned and strategic distribution of SOMI tokens, ensuring balanced growth and support for various stakeholders in the ecosystem.

An interactive chart can be found here:
[https://unlocks.somnia.network/unlocks.somnia.networkchevron-right](https://unlocks.somnia.network/)
[PreviousGas Feeschevron-left](https://docs.somnia.network/concepts/tokenomics/gas-fees)
[NextTokens Governancechevron-right](https://docs.somnia.network/concepts/tokenomics/tokens-governance)
Last updated 6 months ago
* [Allocations](https://docs.somnia.network/concepts/tokenomics/allocation-and-unlocks#allocations)
* [Unlock Schedule](https://docs.somnia.network/concepts/tokenomics/allocation-and-unlocks#unlock-schedule)
---
# Overview | Concepts | Somnia Docs
The Somnia token (SOMI) is the Native token of the Somnia blockchain, with a fixed supply of 1,000,000,000 tokens.
[hashtag](https://docs.somnia.network/concepts/tokenomics/overview#token-utility)
Token Utility
----------------------------------------------------------------------------------------------------
SOMI is a delegated proof of stake token (dPoS) it is intended for:
* Staking functions
* Validator staking - To provide validator nodes for the Somnia blockchain, Tokens must be staked.
* Delegated staking - Tokens can be delegated to Node Providers to cover their staking costs.
* Payment methods
* Gas Fees - To use the blockchain, gas fees will be paid in the SOMI token.
* Governance: The Token will eventually be used to inform how decisions regarding the Network and its components are made. The details of this are to be determined.
[PreviousOn-Chain Reactivitychevron-left](https://docs.somnia.network/concepts/somnia-blockchain/on-chain-reactivity)
[NextToken Staking and Delegationchevron-right](https://docs.somnia.network/concepts/tokenomics/token-staking-and-delegation)
Last updated 6 months ago
---
# Token Staking and Delegation | Concepts | Somnia Docs
There are four main entities that play a part in the tokenomics of the Network: Application Owners, Validators, Content Creators and Token Holders.
Validators are required to time-lock a fixed amount of Tokens at a designated blockchain address in order to secure their place on the Somnia Network/provide validator nodes as described in the Staking section below.
Token Holders can choose to participate in staking, in order to receive rewards from the Fees and/or from the Treasury. These rewards are only available to “staked” Tokens and not to Tokens generally held by Token Holders.
This section explores the staking and delegation options under discussion with the Network.
[hashtag](https://docs.somnia.network/concepts/tokenomics/token-staking-and-delegation#staking-validators)
Staking: Validators
-----------------------------------------------------------------------------------------------------------------------------------
* In the Somnia Blockchain, validators must stake Tokens to provide nodes for computation to the network.
* Validators must provide a certain level of hardware in order to participate in the network.
* The amount of Tokens required to provide a validation node is 5,000,000 SOMI Tokens.
* They will be rewarded from gas fees and treasury-based incentives.
When Validators cover their staking requirement fully, they earn 100% of the rewards that their service generates. However, Validators can also choose to only partially cover their locking requirements. In this scenario, Validators can delegate the provision of the remaining Token requirement to third-party Token Holders. These third-party Token Holders will be given a percentage of the validator's rewards. This is set by the validator (the delegation rate).
Validators can choose to delegate part of the Tokens they are required to fulfill their locking requirements into either the General Validator Pool, or into a Validator-Specific Pool.
###
[hashtag](https://docs.somnia.network/concepts/tokenomics/token-staking-and-delegation#delegated-staking-specific-pool-validators)
**Delegated Staking Specific Pool: Validators**
* Validators can choose to enable the Tokens required to provide a specific Validator’s node to other Token Holders.
* Token holders can choose to provide their Tokens to Validators in exchange for a percentage share of the validator's rewards.
* The validator sets this percentage called the delegation rate.
* When providing their Tokens to Validators, Token Holders must wait 28 days before unstaking, essentially locking their Tokens.
* They can choose to emergency unstake at the cost of 50% of their staked Tokens. The forfeited Tokens will then flow back to the Treasury.
###
[hashtag](https://docs.somnia.network/concepts/tokenomics/token-staking-and-delegation#delegated-staking-general-pool-validators)
**Delegated Staking General Pool: Validators**
* Token Holders can choose to delegate to the general Validator pool.
* This will allocate their Tokens to all validators currently offering delegated staking.
* The Token Holders will receive a percentage of rewards across all Validators.
* This spreads the risk of delegation at the cost of potentially less yield.
* When staking in the general pool there is no locking period.
* The general pool will cover understaked validators first.
**What if a validator can’t meet the minimum staking requirements?**
In the event that a validator fails to meet the minimum staking requirements, a cooldown period will be provided to the validator to fulfil the staking obligations, typically lasting for one month.
**What if a validator has more tokens staked than needed?**
Through the general pool and the delegation mechanism, it is possible for a validator to overstake. This means that more Tokens are staked than are required to operate the validator being supplied to the network. In this case, it means there is no time lock to unlock such additional Tokens for that validator. Any user can instantly unlock until the required level of Tokens is reached. As a result of this, the reward split will be further diluted but at the benefit of increased liquidity.
[hashtag](https://docs.somnia.network/concepts/tokenomics/token-staking-and-delegation#staking-rewards)
**Staking rewards**
--------------------------------------------------------------------------------------------------------------------------------
* 50% of all gas fees are distributed to validators as rewards
* Validators can offer to share these rewards with delegated stakers.
* They do this by setting a delegation rate percentage
* Example
* Validator has a delegation rate of 80%
* The validator gets 100 SOMI in rewards for the Epoch
* You are delegating 1M out of the 5M tokens delegated to the validator. This means you have a staking ratio of 0.2 (20%)
* The formula to calulate your rewards for the epoch is:
epochrewards∗delegationrate∗stakingratioepoch rewards \* delegation rate \* staking ratioepochrewards∗delegationrate∗stakingratio
so in this case:
100(epochrewards)∗0.8(delegationrate)∗0.2(stakingratio)\=16tokens100 (epoch rewards) \* 0.8 (delegation rate) \* 0.2 (staking ratio) = 16 tokens100(epochrewards)∗0.8(delegationrate)∗0.2(stakingratio)\=16tokens
[PreviousOverviewchevron-left](https://docs.somnia.network/concepts/tokenomics/overview)
[NextGas Feeschevron-right](https://docs.somnia.network/concepts/tokenomics/gas-fees)
Last updated 6 months ago
* [Staking: Validators](https://docs.somnia.network/concepts/tokenomics/token-staking-and-delegation#staking-validators)
* [Delegated Staking Specific Pool: Validators](https://docs.somnia.network/concepts/tokenomics/token-staking-and-delegation#delegated-staking-specific-pool-validators)
* [Delegated Staking General Pool: Validators](https://docs.somnia.network/concepts/tokenomics/token-staking-and-delegation#delegated-staking-general-pool-validators)
* [Staking rewards](https://docs.somnia.network/concepts/tokenomics/token-staking-and-delegation#staking-rewards)
---
# Conclusion | Concepts | Somnia Docs
We believe in the vision of a world computer. A world where computing is accessible and usable by anyone with an internet connection, where data is shared across a global network that enables composable and interoperable applications. Ethereum started this movement and has provided the backbone for finance. We want to carry that flag for all application classes that can scale and compete with the large-scale web2 market while giving users and builders freedom, ownership and agency. A free and open internet.
[PreviousTokens Governancechevron-left](https://docs.somnia.network/concepts/tokenomics/tokens-governance)
[NextLEGALchevron-right](https://docs.somnia.network/concepts/miscellaneous/legal)
Last updated 1 month ago
---
# LEGAL | Concepts | Somnia Docs
[LEGAL DISCLAIMERchevron-right](https://docs.somnia.network/concepts/miscellaneous/legal/legal-disclaimer)
[AML Compliancechevron-right](https://docs.somnia.network/concepts/miscellaneous/legal/aml-compliance)
[Airdrop Policychevron-right](https://docs.somnia.network/concepts/miscellaneous/legal/airdrop-policy)
[Governancechevron-right](https://docs.somnia.network/concepts/miscellaneous/legal/governance)
[MiCAR Whitepaperchevron-right](https://docs.somnia.network/concepts/miscellaneous/legal/micar-whitepaper)
[PreviousConclusionchevron-left](https://docs.somnia.network/concepts/conclusion)
[NextLEGAL DISCLAIMERchevron-right](https://docs.somnia.network/concepts/miscellaneous/legal/legal-disclaimer)
---
# LEGAL DISCLAIMER | Concepts | Somnia Docs
####
[hashtag](https://docs.somnia.network/concepts/miscellaneous/legal/legal-disclaimer#please-read-the-entirety-of-this-legal-disclaimer-section-carefully.-nothing-herein-constitutes-lega)
**PLEASE READ THE ENTIRETY OF THIS "LEGAL DISCLAIMER" SECTION CAREFULLY. NOTHING HEREIN CONSTITUTES LEGAL, FINANCIAL, BUSINESS OR TAX ADVICE AND YOU ARE STRONGLY ADVISED TO CONSULT YOUR OWN LEGAL, FINANCIAL, TAX OR OTHER PROFESSIONAL ADVISOR(S) BEFORE ENGAGING IN ANY ACTIVITY IN CONNECTION HEREWITH. NEITHER SOMNIA TOKENCO. LTD. (THE COMPANY), ANY OF THE PROJECT CONTRIBUTORS (THE SOMNIA TEAM) WHO HAVE WORKED ON SOMNIA (AS DEFINED HEREIN) OR PROJECT TO DEVELOP SOMNIA IN ANY WAY WHATSOEVER, ANY DISTRIBUTOR AND/OR VENDOR OF SOMI TOKENS (OR SUCH OTHER RE-NAMED OR SUCCESSOR TICKER CODE OR NAME OF SUCH TOKENS) (THE DISTRIBUTOR), NOR ANY SERVICE PROVIDER SHALL BE LIABLE FOR ANY KIND OF DIRECT OR INDIRECT DAMAGE OR LOSS WHATSOEVER WHICH YOU MAY SUFFER IN CONNECTION WITH ACCESSING THE PAPER, DECK OR MATERIAL RELATING TO SOMI (THE TOKEN DOCUMENTATION) AVAILABLE ON THE WEBSITE AT HTTPS://SOMNIA.NETWORK/ (THE WEBSITE, INCLUDING ANY SUB-DOMAINS THEREON) OR ANY OTHER WEBSITES OR MATERIALS PUBLISHED OR COMMUNICATED BY THE COMPANY OR ITS REPRESENTATIVES FROM TIME TO TIME.**
* * *
**Project purpose:** You agree that you are acquiring SOMI to participate in Somnia and to obtain services on the ecosystem thereon. The Company, the Distributor and their respective affiliates would develop and contribute to the underlying source code for Somnia. The Company is acting solely as an arms’ length third party in relation to the SOMI distribution, and not in the capacity as a financial advisor or fiduciary of any person with regard to the distribution of SOMI.
**Nature of the Token Documentation:** The Token Documentation is a conceptual paper that articulates some of the main design principles and ideas for the creation of a digital token to be known as SOMI. The Token Documentation and the Website are intended for general informational purposes only and do not constitute a prospectus, an offer document, an offer of securities, a solicitation for investment, any offer to sell any product, item, or asset (whether digital or otherwise), or any offer to engage in business with any external individual or entity provided in said documentation. The information herein may not be exhaustive and does not imply any element of, or solicit in any way, a legally-binding or contractual relationship. There is no assurance as to the accuracy or completeness of such information and no representation, warranty or undertaking is or purported to be provided as to the accuracy or completeness of such information. Where the Token Documentation or the Website includes information that has been obtained from third party sources, the Company, the Distributor, their respective affiliates and/or the Somnia team have not independently verified the accuracy or completeness of such information. Further, you acknowledge that the project development roadmap, platform/network functionality are subject to change and that the Token Documentation or the Website may become outdated as a result; and neither the Company nor the Distributor is under any obligation to update or correct this document in connection therewith.
**Validity of Token Documentation and Website:** Nothing in the Token Documentation or the Website constitutes any offer by the Company, the Distributor, or the Somnia team to sell any SOMI (as defined herein) nor shall it or any part of it nor the fact of its presentation form the basis of, or be relied upon in connection with, any contract or investment decision. Nothing contained in the Token Documentation or the Website is or may be relied upon as a promise, representation or undertaking as to the future performance of Somnia. The agreement between the Distributor (or any third party) and you, in relation to any distribution or transfer of SOMI, is to be governed only by the separate terms and conditions of such agreement.
The information set out in the Token Documentation and the Website is for community discussion only and is not legally binding. No person is bound to enter into any contract or binding legal commitment in relation to the acquisition of SOMI, and no digital asset or other form of payment is to be accepted on the basis of the Token Documentation or the Website. The agreement for distribution of SOMI and/or continued holding of SOMI shall be governed by a separate set of Terms and Conditions or Token Distribution Agreement (as the case may be) setting out the terms of such distribution and/or continued holding of SOMI (the Terms and Conditions), which shall be separately provided to you or made available on the Website. The Terms and Conditions must be read together with the Token Documentation. In the event of any inconsistencies between the Terms and Conditions and the Token Documentation or the Website, the Terms and Conditions shall prevail.
**Deemed Representations and Warranties:** By accessing the Token Documentation or the Website (or any part thereof), you shall be deemed to represent and warrant to the Company, the Distributor, their respective affiliates, and the Somnia team as follows:
1. in any decision to acquire any SOMI, you have not relied and shall not rely on any statement set out in the Token Documentation or the Website;
2. you shall at your own expense ensure compliance with all laws, regulatory requirements and restrictions applicable to you (as the case may be);
3. you acknowledge, understand and agree that SOMI may have no value, there is no guarantee or representation of value or liquidity for SOMI, and SOMI is not an investment product nor is it intended for any speculative investment whatsoever;
4. none of the Company, the Distributor, their respective affiliates, and/or the Somnia team shall be responsible for or liable for the value of SOMI, the transferability and/or liquidity of SOMI and/or the availability of any market for SOMI through third parties or otherwise; and
5. you acknowledge, understand and agree that you are not eligible to participate in the distribution of SOMI if you are a citizen, national, resident (tax or otherwise), domiciliary and/or green card or permanent visa holder of a geographic area or country (i) where it is likely that the distribution of SOMI would be construed as the sale of a security (howsoever named), financial service or investment product and/or (ii) where participation in token distributions is prohibited by applicable law, decree, regulation, treaty, or administrative act (including without limitation the United States of America, Canada, and the People's Republic of China); and to this effect you agree to provide all such identity verification document when requested in order for the relevant checks to be carried out.
The Company, the Distributor and the Somnia team do not and do not purport to make, and hereby disclaims, all representations, warranties or undertaking to any entity or person (including without limitation warranties as to the accuracy, completeness, timeliness, or reliability of the contents of the Token Documentation or the Website, or any other materials published by the Company or the Distributor). To the maximum extent permitted by law, the Company, the Distributor, their respective affiliates and service providers shall not be liable for any indirect, special, incidental, consequential or other losses of any kind, in tort, contract or otherwise (including, without limitation, any liability arising from default or negligence on the part of any of them, or any loss of revenue, income or profits, and loss of use or data) arising from the use of the Token Documentation or the Website, or any other materials published, or its contents (including without limitation any errors or omissions) or otherwise arising in connection with the same. Prospective acquirors of SOMI should carefully consider and evaluate all risks and uncertainties (including financial and legal risks and uncertainties) associated with the distribution of SOMI, the Company, the Distributor and the Somnia team.
**SOMI Token:** SOMI are designed to be utilised, and that is the goal of the SOMI distribution. In particular, it is highlighted that SOMI:
1. does not have any tangible or physical manifestation, and does not have any intrinsic value/pricing (nor does any person make any representation or give any commitment as to its value);
2. is non-refundable, not redeemable for any assets of any entity or organisation, and cannot be exchanged for cash (or its equivalent value in any other digital asset) or any payment obligation by the Company, the Distributor or any of their respective affiliates;
3. does not represent or confer on the token holder any right of any form with respect to the Company, the Distributor (or any of their respective affiliates), or their revenues or assets, including without limitation any right to receive future dividends, revenue, shares, ownership right or stake, share or security, any voting, distribution, redemption, liquidation, proprietary (including all forms of intellectual property or licence rights), right to receive accounts, financial statements or other financial data, the right to requisition or participate in shareholder meetings, the right to nominate a director, or other financial or legal rights or equivalent rights, or intellectual property rights or any other form of participation in or relating to Somnia, the Company, the Distributor and/or their service providers;
4. is not intended to represent any rights under a contract for differences or under any other contract the purpose or intended purpose of which is to secure a profit or avoid a loss;
5. is not intended to be a representation of money (including electronic money), payment instrument, security, commodity, bond, debt instrument, unit in a collective investment or managed investment scheme or any other kind of financial instrument or investment;
6. is not a loan to the Company, the Distributor or any of their respective affiliates, is not intended to represent a debt owed by the Company, the Distributor or any of their respective affiliates, and there is no expectation of profit nor interest payment; and
7. does not provide the token holder with any ownership or other interest in the Company, the Distributor or any of their respective affiliates.
Notwithstanding the SOMI distribution, users have no economic or legal right over or beneficial interest in the assets of the Company, the Distributor, or any of their affiliates after the token distribution.
For the avoidance of doubt, neither the Company nor the Distributor deals in, or is in the business of buying or selling any virtual asset or digital payment token (including SOMI). Any sale or distribution of tokens would be performed during a restricted initial period solely for the purpose of obtaining project development funds, raising market/brand awareness, as well as community building and social engagement; this is not conducted with any element of repetitiveness or regularity which would constitute a business.
To the extent a secondary market or exchange for trading SOMI does develop, it would be run and operated wholly independently of the Company, the Distributor, the distribution of SOMI and Somnia. Neither the Company nor the Distributor will create such secondary markets nor will either entity act as an exchange for SOMI.
**Informational purposes only:** The information set out herein is only conceptual, and describes the future development goals for Somnia to be developed. In particular, the project roadmap in the Token Documentation is being shared in order to outline some of the plans of the Somnia team, and is provided solely for INFORMATIONAL PURPOSES and does not constitute any binding commitment. Please do not rely on this information in deciding whether to participate in the token distribution because ultimately, the development, release, and timing of any products, features or functionality remains at the sole discretion of the Company, the Distributor or their respective affiliates, and is subject to change. Further, the Token Documentation or the Website may be amended or replaced from time to time. There are no obligations to update the Token Documentation or the Website, or to provide recipients with access to any information beyond what is provided herein.
**Regulatory approval:** No regulatory authority has examined or approved, whether formally or informally, any of the information set out in the Token Documentation or the Website. No such action or assurance has been or will be taken under the laws, regulatory requirements or rules of any jurisdiction. The publication, distribution or dissemination of the Token Documentation or the Website does not imply that the applicable laws, regulatory requirements or rules have been complied with.
**Cautionary Note on forward-looking statements:** All statements contained herein, statements made in press releases or in any place accessible by the public and oral statements that may be made by the Company, the Distributor and/or the Somnia team, may constitute forward-looking statements (including statements regarding the intent, belief or current expectations with respect to market conditions, business strategy and plans, financial condition, specific provisions and risk management practices). You are cautioned not to place undue reliance on these forward-looking statements given that these statements involve known and unknown risks, uncertainties and other factors that may cause the actual future results to be materially different from that described by such forward-looking statements, and no independent third party has reviewed the reasonableness of any such statements or assumptions. These forward-looking statements are applicable only as of the date indicated in the Token Documentation, and the Company, the Distributor as well as the Somnia team expressly disclaim any responsibility (whether express or implied) to release any revisions to these forward-looking statements to reflect events after such date.
**References to companies and platforms:** The use of any company and/or platform names or trademarks herein (save for those which relate to the Company, the Distributor or their respective affiliates) does not imply any affiliation with, or endorsement by, any third party. References in the Token Documentation or the Website to specific companies and platforms are for illustrative purposes only.
**English language:** The Token Documentation and the Website may be translated into a language other than English for reference purpose only and in the event of conflict or ambiguity between the English language version and translated versions of the Token Documentation or the Website, the English language versions shall prevail. You acknowledge that you have read and understood the English language version of the Token Documentation and the Website.
**No Distribution:** No part of the Token Documentation or the Website is to be copied, reproduced, distributed or disseminated in any way without the prior written consent of the Company or the Distributor. By attending any presentation on this Token Documentation or by accepting any hard or soft copy of the Token Documentation, you agree to be bound by the foregoing limitations.
[PreviousLEGALchevron-left](https://docs.somnia.network/concepts/miscellaneous/legal)
[NextAML Compliancechevron-right](https://docs.somnia.network/concepts/miscellaneous/legal/aml-compliance)
Last updated 7 months ago
---
# Tokens Governance | Concepts | Somnia Docs
The Somnia governance is still in it’s early phases and will evolve as the project evolves. This document outlines our current thinking.
[hashtag](https://docs.somnia.network/concepts/tokenomics/tokens-governance#somnia-token-governance-overview)
Somnia Token Governance Overview
---------------------------------------------------------------------------------------------------------------------------------------------------
The Somnia governance system has 5 different groups:
* Token house - Token owners that primarily govern allocation of foundation and community tokens
* Validator council - Validators in control of hard forks, gas economics and network upgrades.
* Developer council - Key developers that oversee the technology roadmap
* User assembly - Key users who are used as a check and balance to other groups
* Foundation board - Initially in control of the treasury and deployment of new code to the network. Eventually used as a conduit for the other participants and emergency overrides.
[hashtag](https://docs.somnia.network/concepts/tokenomics/tokens-governance#token-house-processes)
Token house processes
-----------------------------------------------------------------------------------------------------------------------------
The token house primarily looks over allocation of the foundation's funds. This is done via proposals. Proposals can be created by any token owner. They are then approved by a majority vote of other token holders.
During the bootstrap phase, the token house will be able to propose new allocation of foundation funds. This could include:
* Allocation of grants to new project
* Allocation of community tokens to new incentive program
* Allocation of budget for new marketing program for the year
During the Transition phase, token holders will be able to vote on proposals. The foundation board will still have the ultimate decision on allocating funds.
During the mature phase the token holders will have the ability to propose and approve allocation of funds. This can be vetoed by the user assembly or foundation board in extreme cases. In general, the board should enact the token holders approved proposals.
[hashtag](https://docs.somnia.network/concepts/tokenomics/tokens-governance#progressive-decentralisation-roadmap)
Progressive decentralisation roadmap
-----------------------------------------------------------------------------------------------------------------------------------------------------------
Phase
Duration
Overview
Bootstrap
0–6 months post‑mainnet
Foundation board in control. Other groups formed without any control.
Transition
6–24 months
Introduction of all governance groups. Beginning of proposal process. Ultimate control still with foundation.
Mature
Year 2 onward
Control is delegated to relevant groups for different decision making. Foundation does have capability for emergency overrides in extreme cases (eg emergency code roll out)
[PreviousAllocation and unlockschevron-left](https://docs.somnia.network/concepts/tokenomics/allocation-and-unlocks)
[NextConclusionchevron-right](https://docs.somnia.network/concepts/conclusion)
Last updated 7 months ago
* [Somnia Token Governance Overview](https://docs.somnia.network/concepts/tokenomics/tokens-governance#somnia-token-governance-overview)
* [Token house processes](https://docs.somnia.network/concepts/tokenomics/tokens-governance#token-house-processes)
* [Progressive decentralisation roadmap](https://docs.somnia.network/concepts/tokenomics/tokens-governance#progressive-decentralisation-roadmap)
---
# Gas Fees | Concepts | Somnia Docs
Somnia's gas pricing model is designed to balance affordability, predictability, security and sustainability, ensuring an optimal experience for users and developers. Gas fees help prevent network congestion and malicious attacks, compensate validators, efficiently allocate resources and incentivise developers to build within the ecosystem. Users pay fees in SOMI tokens, aligning with network economics and long-term viability.
Somnia's model was built based on Ethereum. Most Operation Codes remain the same, however, some required adjustment for two key reasons. First, storage-related opcodes needed to be right-sized since Somnia’s unit gas fees are significantly lower than Ethereum’s, preventing disproportionately cheap storage costs. Second, certain opcodes had gas prices misaligned with the actual compute resources required to execute them, requiring recalibration to maintain a balanced and secure execution environment.
[hashtag](https://docs.somnia.network/concepts/tokenomics/gas-fees#pricing-model)
**Pricing Model**
--------------------------------------------------------------------------------------------------------
Somnia’s model follows Ethereum’s structure: Total Fee = Gas Usage x Gas Unit Price
Gas Usage
Total gas units consumed by a transaction, determined by the following formula:
Gas Usage = Base Gas + Transaction Data + Opcodes
1. Base Gas: All EOA transactions will be subject to a base gas fee of 21,000. This is denoted as a Gtransaction and is, in practice, the cheapest possible transaction cost on the Somnia Network.
2. Transaction Data: Any additional data that is included in a transaction apart from the recipient address, value to be sent and gas parameters is included in this field. Similar to ETH, data is stored in blocks of 32 bytes. Zero-byte and non-zero-byte data costs 160 gas.
3. Opcodes: Operations that can be executed on Smart Contracts. The full Opcodes table can be found [herearrow-up-right](https://docs.google.com/spreadsheets/d/1D0AfQD5zxmDtrWshnHOEdRwVpjR-09tGToReYXOLV3Q/edit?usp=sharing)
.
4. Gas Unit Price: The price per gas unit, dynamically adjusted based on account consumption.
1. Somnia applies a dynamic discount curve for applications that achieve higher transaction volumes. As transaction throughput increases, the effective cost per gas unit decreases, incentivizing developers to scale their applications within the ecosystem.
###
[hashtag](https://docs.somnia.network/concepts/tokenomics/gas-fees#dynamic-pricing-and-discounts)
**Dynamic Pricing & Discounts**
circle-info
This feature of the gas model will be rolled out later this year
Somnia’s compute gas pricing scales with transaction volume:
* Base Price: $0.00000000616 per gas unit.
* Min gas price: $0.000000000616 per gas unit (at 400 TPS).
* Volume Discounts: Gas unit price decreases up to 90% for high-TPS applications.
* Step function, decreasing at 0.1, 1, 10, 100 and 400 TPS
* Sustained low price from 400 TPS
* Measurement timeframe: average TPS in 1 hour.

P(T) = price per transaction after discount
P0 = initial (base) price per transaction
T = account cumulative transactions in 1 hour
###
[hashtag](https://docs.somnia.network/concepts/tokenomics/gas-fees#storage-fees-single-store-32-bytes)
**Storage Fees (Single STORE - 32 Bytes)**
circle-info
This feature of the gas model will be rolled out later this year
Somnia introduces the concept of transient state. This means that a developer can choose not to store data permanently on Somnia. This was explicitly introduced for entertainment products which have a lot of transient states, for instance, the position of a player in a video game.
* Transient & Permanent Storage: Users choose between temporary and permanent storage.
* Tiered Pricing: Costs scale with duration (e.g., 90% discount for 1-hour storage, no discount for indefinite storage).
Create SSTORE (32 bytes)
Gas Amount
1 hour
20,000
1 day
40,000
1 month
60,000
1 year
80,000
Indefinite
200,000
TPS Level
Gas Price ($)
Cost Min Ops (21k)
Discount vs Base (%)
0.0
$5.49E-09
$0.00012
0%
0.1
$4.39E-09
$0.00009
20%
1.0
$3.29E-09
$0.00007
40%
10.0
$2.20E-09
$0.00005
60%
100.0
$1.10E-09
$0.00002
80%
400.0
$5.49E-10
$0.00001
90%
###
[hashtag](https://docs.somnia.network/concepts/tokenomics/gas-fees#price-increase-function)
**Price Increase Function**
To ensure smooth network operation, validators can adjust the base fee price through voting, based on block execution time. This dynamically links gas prices to real network utilisation, increasing the base fee as the network gets congested.
If a block takes longer than 95ms to execute, validators can vote to double the base fee. If execution is faster than 95ms, they can vote to halve it. However, the base fee cannot fall below the minimum of 21,000 gas. Voting cycles occur every second (10 blocks). Here are the key implementation details:
* Price Adjustment Threshold: 95ms.
* Price Increase / Decrease: 2x increase, 50% decrease
* Minimum Base Fee: 21k
* Voting Cycle: 1 sec / 10 blocks
###
[hashtag](https://docs.somnia.network/concepts/tokenomics/gas-fees#gas-fee-distribution)
**Gas fee distribution**
Gas fees spent to use the network are partially distributed to the validators and partially burnt.
50% of all fees are distributed to all validators. This is distributed based on the amount of tokens a validator has staked. It is distributed if the validator was in the working set for the whole epoch.
50% of all fees are burnt. This means the Somnia token is deflationary, as the network is used more, the total supply will decrease.
###
[hashtag](https://docs.somnia.network/concepts/tokenomics/gas-fees#additional-features)
**Additional Features**
* Tipping: Not included initially due to high network efficiency; may be introduced later.
[PreviousToken Staking and Delegationchevron-left](https://docs.somnia.network/concepts/tokenomics/token-staking-and-delegation)
[NextAllocation and unlockschevron-right](https://docs.somnia.network/concepts/tokenomics/allocation-and-unlocks)
Last updated 6 months ago
* [Pricing Model](https://docs.somnia.network/concepts/tokenomics/gas-fees#pricing-model)
* [Dynamic Pricing & Discounts](https://docs.somnia.network/concepts/tokenomics/gas-fees#dynamic-pricing-and-discounts)
* [Storage Fees (Single STORE - 32 Bytes)](https://docs.somnia.network/concepts/tokenomics/gas-fees#storage-fees-single-store-32-bytes)
* [Price Increase Function](https://docs.somnia.network/concepts/tokenomics/gas-fees#price-increase-function)
* [Gas fee distribution](https://docs.somnia.network/concepts/tokenomics/gas-fees#gas-fee-distribution)
* [Additional Features](https://docs.somnia.network/concepts/tokenomics/gas-fees#additional-features)
---
# Connect Your Wallet To Mainnet | Somnia Docs
circle-check
Somnia Mainnet is LIVE. Visit the [Network Information Page](https://docs.somnia.network/developer/network-info#network-details)
for Network details.
1. You can create your [MetaMask Wallet arrow-up-right](https://support.metamask.io/getting-started/getting-started-with-metamask/)
from here. If you already have a wallet you may skip this step
circle-check
**Developers who are deploying Smart Contracts and need Somnia Test Tokens, STT**. Please join the [Discordarrow-up-right](https://discord.com/invite/somnia)
. Go to the `#dev-chat` channel, tag the Somnia DevRel, `@emma_odia`and request Test Tokens. You can also use the Faucet: [https://testnet.somnia.network/arrow-up-right](https://testnet.somnia.network/)
You can also email `[[email protected]](https://docs.somnia.network/cdn-cgi/l/email-protection) ` with a brief description of what you are building and your GitHub profile.
1. Visit [Chainlistarrow-up-right](https://chainlist.org/?search=somnia)
and Click on "Connect Wallet":

1. First, Connect your Wallet. You may use any desired wallet or Metamask (preferred)

1. Click on "Add to Metamask" to add Somnia Mainnet to your wallet

[hashtag](https://docs.somnia.network/get-started/connect-your-wallet-to-mainnet#testnet)
Testnet
------------------------------------------------------------------------------------------------------

[PreviousGetting Started for Mainnetchevron-left](https://docs.somnia.network/get-started/getting-started-for-mainnet)
[NextBridging Infochevron-right](https://docs.somnia.network/get-started/bridging-info)
Last updated 1 month ago
---
# Governance | Concepts | Somnia Docs
**Corporate Framework:** Somnia operates through a Cayman Islands Foundation structure with British Virgin Islands subsidiary entities. This structure ensures regulatory compliance and operational efficiency across jurisdictions.
**Board of Directors:** Four-member Board of Directors provides strategic oversight and holds primary decision-making authority over key business and financial matters. The Board meets quarterly to ensure effective governance and strategic direction.
**Supervision:** A Cayman-based legal Supervisor provides independent oversight to the Board of Directors, ensuring governance best practices and regulatory compliance.
**Corporate Administration:** Professional corporate secretarial services maintain all corporate records and facilitate Board meetings in accordance with regulatory requirements.
**Compliance:** All entities within our structure maintain comprehensive AML/CFT compliance programs, ensuring adherence to applicable international standards and regulatory frameworks across all jurisdictions of operation.
[PreviousAirdrop Policychevron-left](https://docs.somnia.network/concepts/miscellaneous/legal/airdrop-policy)
[NextMiCAR Whitepaperchevron-right](https://docs.somnia.network/concepts/miscellaneous/legal/micar-whitepaper)
Last updated 7 months ago
---
# Testnet STT Tokens | Somnia Docs
circle-exclamation
**Somnia Testnet is meant for developers to build and deploy the first versions of their applications, and there may be bugs.** If you have a question or face an issues, please:
* Share your feedback at `[[email protected]](https://docs.somnia.network/cdn-cgi/l/email-protection) `
* Talk to our team in our [Discordarrow-up-right](https://discord.com/invite/Somnia)
1. To Proceed with this step, you should [connect your wallet](https://docs.somnia.network/get-started/connect-your-wallet-to-mainnet)
with Somnia Testnet
2. To Request for Somnia Testnet Token, click on the **Get STT** button
circle-check
**Developers who are deploying Smart Contracts and need Somnia Test Tokens, STT**. Please join the [Discordarrow-up-right](https://discord.com/invite/somnia)
. Go to the `#dev-chat` channel, tag the Somnia DevRel, `@emma_odia`and request Test Tokens. You can also use the Faucet: [https://testnet.somnia.network/arrow-up-right](https://testnet.somnia.network/)
You can also email `[[email protected]](https://docs.somnia.network/cdn-cgi/l/email-protection) ` with a brief description of what you are building and your GitHub profile.

1. Click on "Get STT"

1. You should have it in your wallet

1. You can Select Somnia Testnet and See your tokens

###
[hashtag](https://docs.somnia.network/get-started/testnet-stt-tokens#try-sending-tokens)
Try Sending Tokens
1. Click on Send Tokens

1. Send STT tokens to your Desired Wallet address or a random address

1. Check our Activity Section in Metamask
[PreviousBridging Infochevron-left](https://docs.somnia.network/get-started/bridging-info)
[NextNetwork Infochevron-right](https://docs.somnia.network/developer/network-info)
Last updated 1 month ago
---
# Getting Started for Mainnet | Somnia Docs
[hashtag](https://docs.somnia.network/get-started/getting-started-for-mainnet#get-somi-tokens)
Get SOMI Tokens
-------------------------------------------------------------------------------------------------------------------
Developers and Non-Developers can purchase SOMI Tokens for interacting on Mainnet from the list of exchanges below:
CEX
DEX
[Binancearrow-up-right](https://www.binance.com/en/trade/SOMI_USDT)
Quickswap
[Gate.ioarrow-up-right](https://www.gate.com/price/somnia-somi)
[Bitgetarrow-up-right](https://www.bitgetapp.com/price/somnia)
[hashtag](https://docs.somnia.network/get-started/getting-started-for-mainnet#bridge-to-somnia)
Bridge to Somnia
---------------------------------------------------------------------------------------------------------------------
Developers and Non-Developers can also Bridge their Stablecoins from other Networks to Somnia using LayerZero's STARGATE: [https://stargate.finance/bridgearrow-up-right](https://stargate.finance/bridge)
Welcome to Somnia Mainnet. Below is a checklist for you to confirm the migration from Testnet to Mainnet.
[hashtag](https://docs.somnia.network/get-started/getting-started-for-mainnet#for-non-developers)
For Non-Developers
-------------------------------------------------------------------------------------------------------------------------
* check
Ensure that you have added the Somnia Mainnet Network to your Wallet. You can use [ChainListarrow-up-right](https://chainlist.org/?search=somnia)
.
* check
Get SOMI Tokens from a list of Exchanges.
[hashtag](https://docs.somnia.network/get-started/getting-started-for-mainnet#for-developers)
For Developers
-----------------------------------------------------------------------------------------------------------------
Conduct the same checks as non-developers, and in addition, the following.
* check
Add Somnia to the list of Networks in your configuration files for your Smart Contracts
* check
Using Hardhat:
* check
Using Forge Deployment:
* check
See the Page for the list of infrastructure and Dev Tooling available to you on Somnia.
[PreviousIntroductionchevron-left](https://docs.somnia.network/)
[NextConnect Your Wallet To Mainnetchevron-right](https://docs.somnia.network/get-started/connect-your-wallet-to-mainnet)
Last updated 1 month ago
* [Get SOMI Tokens](https://docs.somnia.network/get-started/getting-started-for-mainnet#get-somi-tokens)
* [Bridge to Somnia](https://docs.somnia.network/get-started/getting-started-for-mainnet#bridge-to-somnia)
* [For Non-Developers](https://docs.somnia.network/get-started/getting-started-for-mainnet#for-non-developers)
* [For Developers](https://docs.somnia.network/get-started/getting-started-for-mainnet#for-developers)
Copy
module.exports = {
// ...
networks: {
somnia: {
url: "https://api.infra.mainnet.somnia.network",
accounts: ["0xPRIVATE_KEY"], // put dev menomonic or PK here,
},
},
// ...
};
Copy
forge create --rpc-url https://api.infra.mainnet.somnia.network --private-key PRIVATE_KEY src/Example.sol:Example
---
# Audits | Concepts | Somnia Docs
Smart Contract Report
file-pdf
2MB
[Hacken\_Somnia\_\[SCA\] Somnia \_ Somnia-Contracts \_ Aug2025\_P-2025-1779\_3\_20250829 10\_27 (1).pdf](https://1819673643-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FhEUf5l6MCgn0iJsSVgmd%2Fuploads%2F3zJWvDihw9doxo45FB9X%2FHacken_Somnia_%5BSCA%5D%20Somnia%20_%20Somnia-Contracts%20_%20Aug2025_P-2025-1779_3_20250829%2010_27%20(1).pdf?alt=media&token=be4ef041-ca9d-4ad7-87b0-dbbbc60f7115)
PDF
downloadDownload[arrow-up-right-from-squareOpen](https://1819673643-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FhEUf5l6MCgn0iJsSVgmd%2Fuploads%2F3zJWvDihw9doxo45FB9X%2FHacken_Somnia_%5BSCA%5D%20Somnia%20_%20Somnia-Contracts%20_%20Aug2025_P-2025-1779_3_20250829%2010_27%20(1).pdf?alt=media&token=be4ef041-ca9d-4ad7-87b0-dbbbc60f7115)
L1 Audit Report
file-pdf
2MB
[Hacken\_Somnia\_\[L1\] Somnia \_ Somnia Network \_ May2025\_P-2025-1650\_3\_20250704 13\_58 (3).pdf](https://1819673643-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FhEUf5l6MCgn0iJsSVgmd%2Fuploads%2F9Fg9QhXC54Drc9TXK5nc%2FHacken_Somnia_%5BL1%5D%20Somnia%20_%20Somnia%20Network%20_%20May2025_P-2025-1650_3_20250704%2013_58%20(3).pdf?alt=media&token=f743c224-24ff-42a1-9086-90f12a87c018)
PDF
downloadDownload[arrow-up-right-from-squareOpen](https://1819673643-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FhEUf5l6MCgn0iJsSVgmd%2Fuploads%2F9Fg9QhXC54Drc9TXK5nc%2FHacken_Somnia_%5BL1%5D%20Somnia%20_%20Somnia%20Network%20_%20May2025_P-2025-1650_3_20250704%2013_58%20(3).pdf?alt=media&token=f743c224-24ff-42a1-9086-90f12a87c018)
[PreviousMiCAR Whitepaperchevron-left](https://docs.somnia.network/concepts/miscellaneous/legal/micar-whitepaper)
Last updated 6 months ago
---
# MiCAR Whitepaper | Concepts | Somnia Docs
file-pdf
488KB
[Somnia\_White Paper.pdf](https://1819673643-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FhEUf5l6MCgn0iJsSVgmd%2Fuploads%2FLE7UxEOlt7nyVSWk0ikg%2FSomnia_White%20Paper.pdf?alt=media&token=3cdcb956-6f9b-43ed-8b43-60bdc9e4b2ba)
PDF
downloadDownload[arrow-up-right-from-squareOpen](https://1819673643-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FhEUf5l6MCgn0iJsSVgmd%2Fuploads%2FLE7UxEOlt7nyVSWk0ikg%2FSomnia_White%20Paper.pdf?alt=media&token=3cdcb956-6f9b-43ed-8b43-60bdc9e4b2ba)
[PreviousGovernancechevron-left](https://docs.somnia.network/concepts/miscellaneous/legal/governance)
[NextAuditschevron-right](https://docs.somnia.network/concepts/miscellaneous/audits)
Last updated 6 months ago
---
# SOMI coin | Somnia Docs
SOMI is the native coin of the Somnia Network. It is a currency used to pay for transactions, similar to Ether (ETH) on Ethereum and other EVM networks.
SOMI is denominated in Wei, the base unit and smallest value that can be expressed in the network. Below is a table showing different denomination and comparison to falimiar terms:
Unit
Wei Value
Exp
Ethereum synonym
Somi (SOMI)
1,000,000,000,000,000,000
1e18
Ether (ETH)
milliSomi
1,000,000,000,000,000,
1e15
microSomi
1,000,000,000,000
1e12
nanoSomi
1,000,000,000
1e9
gWei
Wei
1
1
Wei
[PreviousNetwork Overview (Mainnet / Testnet)chevron-left](https://docs.somnia.network/developer/network-info/network-overview-mainnet-testnet)
[NextSomnia Reactivitychevron-right](https://docs.somnia.network/developer/reactivity)
Last updated 27 days ago
---
# Network Overview (Mainnet / Testnet) | Somnia Docs
Somnia provides two distinct environments for developers and users: **Mainnet** and **Testnet (Shannon)**. Both serve different purposes in the ecosystem, and knowing when to use which is essential for building and deploying applications effectively.
* * *
###
[hashtag](https://docs.somnia.network/developer/network-info/network-overview-mainnet-testnet#somnia-mainnet)
Somnia Mainnet
The **Mainnet** is the official production blockchain of Somnia. All transactions on this chain are **final and irreversible** and require **SOMI tokens** as gas.
####
[hashtag](https://docs.somnia.network/developer/network-info/network-overview-mainnet-testnet#key-characteristics)
Key Characteristics
* Real-value environment secured by Somnia’s validator set.
* Integrated with wallets, explorers, bridges, and infrastructure providers.
* Permanent and immutable transaction history.
* Designed for live dApps, end-users, and production-ready deployments.
####
[hashtag](https://docs.somnia.network/developer/network-info/network-overview-mainnet-testnet#when-to-use-mainnet)
When to Use Mainnet
* Deploying **audited and tested smart contracts**.
* Running dApps with **real users and assets**.
* Managing liquidity, staking, governance, or NFT projects.
* Partner integrations requiring **security and finality**.
####
[hashtag](https://docs.somnia.network/developer/network-info/network-overview-mainnet-testnet#example)
Example
Deploy to Somnia Mainnet
Copy
# Deploying a contract to Somnia Mainnet
npx hardhat run scripts/deploy.js --network somnia_mainnet
* * *
###
[hashtag](https://docs.somnia.network/developer/network-info/network-overview-mainnet-testnet#somnia-testnet-shannon)
Somnia Testnet (Shannon)
The **Testnet** is a sandbox environment that mirrors mainnet behavior but uses **STT test tokens** with no real-world value. It allows safe experimentation and rapid iteration without financial risk.
####
[hashtag](https://docs.somnia.network/developer/network-info/network-overview-mainnet-testnet#key-characteristics-1)
Key Characteristics
* Transactions use **STT tokens**, available via the faucet.
* Close-to-mainnet parameters for realistic testing.
* Safe for prototyping, debugging, and QA.
* Commonly used in workshops, hackathons, and developer onboarding.
####
[hashtag](https://docs.somnia.network/developer/network-info/network-overview-mainnet-testnet#when-to-use-testnet)
When to Use Testnet
* Learning how to connect and deploy on Somnia.
* Prototyping features or building MVPs.
* Debugging smart contracts or dApp flows.
* Preparing for audits and production deployment.
####
[hashtag](https://docs.somnia.network/developer/network-info/network-overview-mainnet-testnet#example-1)
Example
* * *
###
[hashtag](https://docs.somnia.network/developer/network-info/network-overview-mainnet-testnet#quick-comparison)
Quick Comparison
Feature
Mainnet (Production)
Testnet (Shannon)
Currency
SOMI (real value)
STT (valueless, faucet)
Purpose
Production deployments
Development & testing
Transactions
Permanent and irreversible
Experimental and disposable
Typical Use
Live dApps, DeFi, staking, NFT
Prototyping, QA, education
Risk
Financial impact possible
No financial risk
* * *
###
[hashtag](https://docs.somnia.network/developer/network-info/network-overview-mainnet-testnet#best-practices)
Best Practices
circle-info
* Start on Testnet: Validate contracts and flows on Shannon before mainnet.
* Audit before launch: Ensure contracts are reviewed and secure.
* Use separate configs: Keep `.env` files distinct for testnet and mainnet.
* Stay updated: Follow official announcements for upgrades and changes.
circle-check
Tip: Treat **Testnet** as your safe playground and **Mainnet** as your production stage. Every project should pass through Testnet before moving to Mainnet.
[PreviousNetwork Infochevron-left](https://docs.somnia.network/developer/network-info)
[NextSOMI coinchevron-right](https://docs.somnia.network/developer/network-info/somi-coin)
Last updated 1 month ago
* [Somnia Mainnet](https://docs.somnia.network/developer/network-info/network-overview-mainnet-testnet#somnia-mainnet)
* [Somnia Testnet (Shannon)](https://docs.somnia.network/developer/network-info/network-overview-mainnet-testnet#somnia-testnet-shannon)
* [Quick Comparison](https://docs.somnia.network/developer/network-info/network-overview-mainnet-testnet#quick-comparison)
* [Best Practices](https://docs.somnia.network/developer/network-info/network-overview-mainnet-testnet#best-practices)
Deploy to Somnia Testnet (Shannon)
Copy
# Deploying a contract to Somnia Testnet
npx hardhat run scripts/deploy.js --network somnia_testnet
---
# Network Info | Somnia Docs
circle-check
**The Somnia Mainnet is LIVE**
circle-info
**Developers who deploy Smart Contracts on Mainnet require Somnia Tokens, SOMI.** It is a real-world utility token that can be purchased on a list of CEXs and DEXs [here](https://docs.somnia.network/get-started/getting-started-for-mainnet#get-somi-tokens)
.
circle-check
**Developers who are deploying Smart Contracts and need Somnia Test Tokens, STT**. Please join the [Discordarrow-up-right](https://discord.com/invite/somnia)
. Go to the `#dev-chat` channel, tag the Somnia DevRel, `@emma_odia`and request Test Tokens. You can also use the Faucet: [https://testnet.somnia.network/arrow-up-right](https://testnet.somnia.network/)
You can also email `[[email protected]](https://docs.somnia.network/cdn-cgi/l/email-protection) ` with a brief description of what you are building and your GitHub profile.
Network
Mainnet
Testnet
Chain ID
5031
50312
Block Explorer
[https://explorer.somnia.networkarrow-up-right](https://explorer.somnia.network/)
[https://shannon-explorer.somnia.network/arrow-up-right](https://shannon-explorer.somnia.network/)
Symbol
SOMI
STT
RPC
[https://api.infra.mainnet.somnia.network/arrow-up-right](https://api.infra.mainnet.somnia.network/)
[https://dream-rpc.somnia.network/arrow-up-right](https://vsf-rpc.somnia.network/)
MultiCallV3
[0x5e44F178E8cF9B2F5409B6f18ce936aB817C5a11arrow-up-right](https://explorer.somnia.network/address/0x5e44F178E8cF9B2F5409B6f18ce936aB817C5a11)
[0x841b8199E6d3Db3C6f264f6C2bd8848b3cA64223arrow-up-right](https://shannon-explorer.somnia.network/address/0x841b8199E6d3Db3C6f264f6C2bd8848b3cA64223)
EntryPoint v0.7
[0x0000000071727De22E5E9d8BAf0edAc6f37da032arrow-up-right](https://shannon-explorer.somnia.network/address/0x0000000071727De22E5E9d8BAf0edAc6f37da032)
Factory Address
[0x4be0ddfebca9a5a4a617dee4dece99e7c862dcebarrow-up-right](https://shannon-explorer.somnia.network/address/0x4bE0ddfebcA9A5A4a617dee4DeCe99E7c862dceb)
Alternative Testnet Block Explorer
[https://somnia-testnet.socialscan.io/arrow-up-right](https://somnia-testnet.socialscan.io/)
Testnet Faucet
[https://testnet.somnia.network/arrow-up-right](https://testnet.somnia.network/)
Stakely Mainnet Faucet
[https://stakely.io/faucet/somnia-somiarrow-up-right](https://stakely.io/faucet/somnia-somi)
CreateX
0xD13C575ED5378fd18B100Bd87D5765d9A747358B
0x535822d4b86b2372FBE4fd9d1468318F04A2A640
[hashtag](https://docs.somnia.network/developer/network-info#rpc-providers)
RPC Providers
----------------------------------------------------------------------------------------------
RPCs
Ankr
[https://www.ankr.com/rpc/somniaarrow-up-right](https://www.ankr.com/rpc/somnia/)
Public Node
[https://somnia.publicnode.comarrow-up-right](https://somnia.publicnode.com/)
Stakely
[https://somnia-json-rpc.stakely.ioarrow-up-right](https://somnia-json-rpc.stakely.io/)
Validation Cloud
[https://www.validationcloud.io/somniaarrow-up-right](https://www.validationcloud.io/somnia)
[hashtag](https://docs.somnia.network/developer/network-info#faucet-providers)
Faucet Providers
----------------------------------------------------------------------------------------------------
Google Cloud Faucet
[https://cloud.google.com/application/web3/faucet/somnia/shannonarrow-up-right](https://cloud.google.com/application/web3/faucet/somnia/shannon)
Stakely
[https://stakely.io/faucet/somnia-testnet-sttarrow-up-right](https://stakely.io/faucet/somnia-testnet-stt)
Thirdweb Faucet
[https://thirdweb.com/somnia-shannon-testnetarrow-up-right](https://thirdweb.com/somnia-shannon-testnet)
####
[hashtag](https://docs.somnia.network/developer/network-info#greater-than-join-the-exclusive-developer-community)
\> [Join the Exclusive Developer Communityarrow-up-right](https://somnia.fillout.com/t/vPjjEWFLbQus)
####
[hashtag](https://docs.somnia.network/developer/network-info#greater-than-apply-for-the-somnia-network-grants-program)
\> [Apply for the Somnia Network Grants Programarrow-up-right](https://somnia.fillout.com/t/5WqTW4iFxCus)
[hashtag](https://docs.somnia.network/developer/network-info#community)
Community
--------------------------------------------------------------------------------------
* [Join us on Discordarrow-up-right](https://discord.com/invite/Somnia)
to chat with devs and raise issues
* [Follow us on Twitterarrow-up-right](https://twitter.com/Somnia_Network)
for updates
[hashtag](https://docs.somnia.network/developer/network-info#solidity-resources)
Solidity Resources
--------------------------------------------------------------------------------------------------------
* [Ethereum Developer arrow-up-right](https://learnweb3.io/degrees/ethereum-developer-degree/)
by Learn Web3
* [Solidity by Examplearrow-up-right](https://solidity-by-example.org/)
* [CryptoZombiesarrow-up-right](https://cryptozombies.io/en/course)
* [Cookbook.devarrow-up-right](https://www.cookbook.dev/search?q=cookbook&categories=Contracts&sort=popular&filter=&page=1)
* [Alchemy Unversityarrow-up-right](https://www.alchemy.com/university/courses/solidity)
[hashtag](https://docs.somnia.network/developer/network-info#toolkit)
Toolkit
----------------------------------------------------------------------------------
* [Hardhat Toolkitarrow-up-right](https://hardhat.org/docs)
- Solidity development framework paired with a JavaScript testing framework
* [Foundry Toolkitarrow-up-right](https://book.getfoundry.sh/)
- Solidity framework for both development and testing.
* [Remixarrow-up-right](https://remix.ethereum.org/#lang=en&optimize=false&runs=200&evmVersion=null)
- Solidity IDE with interactive features
[PreviousTestnet STT Tokenschevron-left](https://docs.somnia.network/get-started/testnet-stt-tokens)
[NextNetwork Overview (Mainnet / Testnet)chevron-right](https://docs.somnia.network/developer/network-info/network-overview-mainnet-testnet)
Last updated 1 month ago
* [RPC Providers](https://docs.somnia.network/developer/network-info#rpc-providers)
* [Faucet Providers](https://docs.somnia.network/developer/network-info#faucet-providers)
* [Community](https://docs.somnia.network/developer/network-info#community)
* [Solidity Resources](https://docs.somnia.network/developer/network-info#solidity-resources)
* [Toolkit](https://docs.somnia.network/developer/network-info#toolkit)
---
# Tooling | Somnia Docs
circle-exclamation
**Somnia Reactivity is currently only available on TESTNET**
Dive into the following sections to get a better understanding of the Somnia Reactivity tooling
[Comparisonchevron-right](https://docs.somnia.network/developer/reactivity/tooling/comparison)
[Off-Chain (TypeScript)chevron-right](https://docs.somnia.network/developer/reactivity/tooling/off-chain-typescript)
[On-chain (Solidity)chevron-right](https://docs.somnia.network/developer/reactivity/tooling/on-chain-solidity)
[Subscription managementchevron-right](https://docs.somnia.network/developer/reactivity/tooling/subscription-management)
[PreviousNo, this is not like regular EVM event subscriptionschevron-left](https://docs.somnia.network/developer/reactivity/no-this-is-not-like-regular-evm-event-subscriptions)
[NextComparisonchevron-right](https://docs.somnia.network/developer/reactivity/tooling/comparison)
Last updated 1 month ago
---
# Bridging Info | Somnia Docs
Somnia supports cross-chain asset transfers through two official bridge partners: Relay and Stargate Finance. These bridges enable you to move tokens between Somnia and other blockchain networks while maintaining security and minimizing fees. Whether you're bridging stablecoins, ETH, or other supported assets, this guide will walk you through the complete process.
[hashtag](https://docs.somnia.network/get-started/bridging-info#prerequisites)
Prerequisites
-------------------------------------------------------------------------------------------------
* **Wallet Setup**: MetaMask, WalletConnect-compatible wallet, or hardware wallet
* **Network Configuration**: Somnia network added to your wallet
* **Funded Wallet**: Source chain tokens to cover bridge fees and gas costs
* **Basic Understanding**: Familiarity with blockchain transactions and gas fees
[hashtag](https://docs.somnia.network/get-started/bridging-info#supported-bridges)
Supported Bridges
---------------------------------------------------------------------------------------------------------
###
[hashtag](https://docs.somnia.network/get-started/bridging-info#relay-bridge)
Relay Bridge
Relay is a multichain payments network that has served 5M+ users and processed $5B+ in volume across 85+ networks. It offers instant cross-chain transactions with 99.9% uptime and payments-grade reliability.
**Key Features**:
* Instant Execution: Cross-chain transfers in 1-10 seconds
* 75+ Networks: Extensive blockchain support including Somnia
* Predictable Fees: Transparent fee structure with no hidden costs
* Enterprise Grade: 99.9% uptime with automatic redundancy systems
**Supported Assets**: ETH, USDC, USDT, and other major tokens **Website**: [relay.link/bridgearrow-up-right](https://relay.link/bridge)
###
[hashtag](https://docs.somnia.network/get-started/bridging-info#stargate-finance)
Stargate Finance
Stargate is a fully composable cross-chain bridge built on LayerZero that connects 50+ blockchains. It's the first bridge to solve the "bridging trilemma" by providing instant guaranteed finality, native assets, and unified liquidity.
**Key Features**:
* Native Assets: Transfer native tokens without wrapped intermediates
* Instant Finality: Guaranteed transaction completion
* Unified Liquidity: Shared liquidity pools across all chains
* LayerZero Powered: Built on robust omnichain infrastructure
**Supported Assets**: USDC, USDT, ETH, BTC, and LayerZero OFTs **Website**: [stargate.financearrow-up-right](https://stargate.finance/)
[hashtag](https://docs.somnia.network/get-started/bridging-info#using-relay-bridge)
Using Relay Bridge
-----------------------------------------------------------------------------------------------------------
1
####
[hashtag](https://docs.somnia.network/get-started/bridging-info#access-relay-bridge)
Access Relay Bridge
Navigate to [relay.link/bridgearrow-up-right](https://relay.link/bridge)
in your web browser.
2
####
[hashtag](https://docs.somnia.network/get-started/bridging-info#connect-your-wallet)
Connect Your Wallet
Click "Connect Wallet" and select your preferred wallet provider. Approve the connection when prompted.
3
####
[hashtag](https://docs.somnia.network/get-started/bridging-info#configure-bridge-transaction)
Configure Bridge Transaction
* Source Chain: Select the blockchain you're bridging FROM
* Destination Chain: Select Somnia (or your target chain)
* Token: Choose the asset you want to bridge
* Amount: Enter the amount to transfer\\

4
####
[hashtag](https://docs.somnia.network/get-started/bridging-info#review-transaction-details)
Review Transaction Details
Carefully review:
* Bridge Fee: Relay's service fee
* Gas Fee: Network transaction cost
* Estimated Time: Usually 1-10 seconds
* Recipient Address: Verify destination address(Your address or if you want to bridge to a different wallet.

5
####
[hashtag](https://docs.somnia.network/get-started/bridging-info#execute-bridge-transaction)
Execute Bridge Transaction
Click "Bridge" and confirm the transaction in your wallet. The process typically involves:
* Approval Transaction: Authorize Relay to spend your tokens (if required)
* Bridge Transaction: Execute the cross-chain transfer
* Confirmation: Receive tokens on destination chain
6
####
[hashtag](https://docs.somnia.network/get-started/bridging-info#verify-completion)
Verify Completion
Check your wallet balance on the destination chain. Relay provides real-time status updates during the bridging process.
[hashtag](https://docs.somnia.network/get-started/bridging-info#using-stargate-finance)
Using Stargate Finance
-------------------------------------------------------------------------------------------------------------------
1
####
[hashtag](https://docs.somnia.network/get-started/bridging-info#access-stargate-bridge)
Access Stargate Bridge
Visit [stargate.financearrow-up-right](https://stargate.finance/)
and click "Bridge" or "Transfer".
2
####
[hashtag](https://docs.somnia.network/get-started/bridging-info#connect-wallet-and-select-networks)
Connect Wallet & Select Networks
Connect your wallet and configure:
* From: Source blockchain
* To: Somnia (or destination chain)
* Asset: Select supported token (USDC, USDT, ETH, etc.) 
3
####
[hashtag](https://docs.somnia.network/get-started/bridging-info#enter-transfer-details)
Enter Transfer Details
* Amount: Specify transfer amount
* Recipient: Destination wallet address (defaults to your connected wallet)
Only applicable if you are bridging to another wallet address.\\

* Slippage: Set acceptable slippage tolerance (usually 0.1-0.5%)
4
####
[hashtag](https://docs.somnia.network/get-started/bridging-info#review-pool-information)
Review Pool Information
Stargate displays:
* Available Liquidity: Pool depth on destination chain
* Bridge Fee: Protocol fee structure
* Gas Estimate: Transaction costs
* Route: Cross-chain path details
5
####
[hashtag](https://docs.somnia.network/get-started/bridging-info#execute-transfer)
Execute Transfer
Confirm transaction details and sign with your wallet. 3 Stargate's ΔBridge technology ensures native asset delivery without wrapped tokens.
6
####
[hashtag](https://docs.somnia.network/get-started/bridging-info#monitor-transaction)
Monitor Transaction
Track your transfer through:
* Stargate Interface: Real-time status updates
* LayerZero Scan: Cross-chain transaction [explorerarrow-up-right](https://layerzeroscan.com/)
* Destination Explorer: Confirm arrival on target chain
[hashtag](https://docs.somnia.network/get-started/bridging-info#troubleshooting-common-issues)
Troubleshooting Common Issues
---------------------------------------------------------------------------------------------------------------------------------
####
[hashtag](https://docs.somnia.network/get-started/bridging-info#transaction-stuck-or-pending)
Transaction Stuck or Pending
**Symptoms**: Bridge transaction shows "pending" for extended period
1
Check Network Status: Verify source and destination chain health
2
Gas Price Issues: Increase gas price if transaction is stuck
3
Bridge Congestion: Wait for network congestion to clear
4
Contact Support: Reach out to bridge support with transaction hash
####
[hashtag](https://docs.somnia.network/get-started/bridging-info#insufficient-liquidity)
Insufficient Liquidity
**Symptoms**: Bridge shows "insufficient liquidity" error
1
Reduce Amount: Try bridging smaller amounts
2
Wait for Rebalancing: Liquidity pools rebalance automatically
3
Alternative Routes: Use different bridge or route
4
Split Transactions: Break large transfers into smaller ones
####
[hashtag](https://docs.somnia.network/get-started/bridging-info#wrong-network-or-address)
Wrong Network or Address
**Symptoms**: Tokens didn't arrive at expected destination
1
Verify Network: Ensure correct destination network selected
2
Check Address: Confirm recipient address accuracy
3
Network Switch: Switch wallet to destination network
4
Recovery Process: Contact bridge support for recovery options
####
[hashtag](https://docs.somnia.network/get-started/bridging-info#high-fees-or-slippage)
High Fees or Slippage
**Symptoms**: Unexpected high costs or poor exchange rates
1
Timing: Bridge during low network congestion periods
2
Route Optimization: Compare different bridge options
3
Amount Adjustment: Larger amounts often have better rates
4
Alternative Bridges: Consider other bridge providers
[hashtag](https://docs.somnia.network/get-started/bridging-info#verification-and-testing)
Verification And Testing
-----------------------------------------------------------------------------------------------------------------------
####
[hashtag](https://docs.somnia.network/get-started/bridging-info#test-bridge-functionality)
Test Bridge Functionality
1
Connect wallet to both source and destination networks
2
Bridge small test amount (e.g., $10-50)
3
Verify tokens arrive within expected timeframe
4
Check token balances on both chains
5
Test bridge interface responsiveness
####
[hashtag](https://docs.somnia.network/get-started/bridging-info#confirm-successful-bridge)
Confirm Successful Bridge
1
Source Chain: Confirm tokens debited from source wallet
2
Bridge Status: Check bridge interface for completion status
3
Destination Chain: Verify tokens credited to destination wallet
4
Balance Check: Ensure correct amounts received
You've successfully learned how to bridge assets to and from Somnia using both Relay and Stargate Finance. These official bridge partners provide secure, fast, and reliable cross-chain transfers with different strengths:
* **Relay**: Best for fast payments and swaps across 75+ networks
* **Stargate**: Ideal for DeFi composability with native asset transfers
Both bridges support Somnia's high-performance infrastructure, enabling seamless integration with the broader multi-chain ecosystem.
_For technical support, contact the respective bridge providers or Somnia community channels._
[PreviousConnect Your Wallet To Mainnetchevron-left](https://docs.somnia.network/get-started/connect-your-wallet-to-mainnet)
[NextTestnet STT Tokenschevron-right](https://docs.somnia.network/get-started/testnet-stt-tokens)
Last updated 1 month ago
* [Prerequisites](https://docs.somnia.network/get-started/bridging-info#prerequisites)
* [Supported Bridges](https://docs.somnia.network/get-started/bridging-info#supported-bridges)
* [Relay Bridge](https://docs.somnia.network/get-started/bridging-info#relay-bridge)
* [Stargate Finance](https://docs.somnia.network/get-started/bridging-info#stargate-finance)
* [Using Relay Bridge](https://docs.somnia.network/get-started/bridging-info#using-relay-bridge)
* [Using Stargate Finance](https://docs.somnia.network/get-started/bridging-info#using-stargate-finance)
* [Troubleshooting Common Issues](https://docs.somnia.network/get-started/bridging-info#troubleshooting-common-issues)
* [Verification And Testing](https://docs.somnia.network/get-started/bridging-info#verification-and-testing)
---
# Somnia Reactivity | Somnia Docs
circle-exclamation
**Reactivity is currently only available on TESTNET**
Somnia Reactivity is a native toolkit for building event-driven dApps on the Somnia chain. Events and blockchain state is pushed directly to your TypeScript or Solidity apps in one atomic notification without polling.
####
[hashtag](https://docs.somnia.network/developer/reactivity#key-benefits)
Key Benefits
* **Real-Time Efficiency**: Notifications include events + state from the same block, slashing RPC calls and latency.
* **Cross-Environment**: Seamless for off-chain (WebSocket) and on-chain (EVM invocations).
* **Scalable Subscriptions**: Customizable for filters, guarantees, and coalescing to fit your app's needs.
TL;DR: Build reactive dApps that respond instantly to on-chain activity, reducing complexity and costs vs. traditional EVM setups.
[PreviousSOMI coinchevron-left](https://docs.somnia.network/developer/network-info/somi-coin)
[NextWhat is Reactivity?chevron-right](https://docs.somnia.network/developer/reactivity/what-is-reactivity)
Last updated 1 month ago
---
# Subscriptions: The Core Primitive | Somnia Docs
Subscriptions are configurable listeners that define what events to watch and how to deliver notifications. They're the foundation of reactivity—create one, and the chain does the rest.
####
[hashtag](https://docs.somnia.network/developer/reactivity/subscriptions-the-core-primitive#key-features)
Key Features
* **Filters**: Wildcard (\*) for all events, or specify emitters, topics.
* **On-chain**
* **Costs**: Minimum 32 SOM balance to cover handler invocation costs on-chain (validators execute handlers) + small amount of gas (~21K) to create each subscription
* **Options**:
* isGuaranteed: Eventual delivery with some block inclusion distance (true/false).
* isCoalesced: Batch multiple events into one notification within a block.
* Handler Gas params: priorityFeePerGas, maxFeePerGas, gasLimit
* **Off-chain**
* **Costs**: Cost of running the Somnia node or paying an RPC provider
[PreviousQuickstartchevron-left](https://docs.somnia.network/developer/reactivity/quickstart)
[NextPush vs Pull: An Architectural Shiftchevron-right](https://docs.somnia.network/developer/reactivity/push-vs-pull-an-architectural-shift)
Last updated 1 month ago
---
# Somnia Data Streams | Somnia Docs
Read, write, and react to structured data or events broadcast on-chain. With a sharp focus on reusability and composability, applications can interoperate and coalesce around commonly agreed-upon data structures that are built around the native reactivity offered by the protocol suite.
[PreviousAPI Referencechevron-left](https://docs.somnia.network/developer/reactivity/api-reference)
[NextWhat is Somnia Data Streams?chevron-right](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams)
Last updated 1 month ago
---
# What is Reactivity? | Somnia Docs
Reactivity is Somnia's event-driven paradigm for dApps. It pushes notifications—combining emitted events and related blockchain state—to subscribers in real-time, enabling "reactive" logic without polling.
circle-exclamation
**Reactivity is currently only available on TESTNET**
####
[hashtag](https://docs.somnia.network/developer/reactivity/what-is-reactivity#core-concepts)
Core Concepts
* **Events**: Triggers from smart contracts (e.g., Transfer, Approval).
* **State**: View calls for contract data fetched at the event's block height.
* **Push Delivery**: Chain validators / nodes handle notifications, invoking handlers or WebSocket callbacks directly.
* **Subscribers**: Off-chain apps (TypeScript) or on-chain contracts (Solidity).
This shifts dApps from reactive querying to proactive responses, like a pub/sub system baked into the blockchain.
[PreviousSomnia Reactivitychevron-left](https://docs.somnia.network/developer/reactivity)
[NextQuickstartchevron-right](https://docs.somnia.network/developer/reactivity/quickstart)
Last updated 1 month ago
---
# State Consistency Guarantees | Somnia Docs
Somnia ensures notifications deliver events and state that are consistent—sourced from the exact same block. This eliminates race conditions common in pull models.
####
[hashtag](https://docs.somnia.network/developer/reactivity/state-consistency-guarantees#how-it-works)
How It Works
* **Atomic Delivery**: Event + state (via ETH calls) processed in one validator-executed bundle.
* **Guarantees**:
* Non-coalesced: One notification per event.
* Coalesced: Batched, but state reflects the latest in the batch.
####
[hashtag](https://docs.somnia.network/developer/reactivity/state-consistency-guarantees#example-impact)
Example Impact
In a DeFi app, a "Transfer" event pushes the new balance immediately—no extra balanceOf call needed.
This makes dApps more reliable and easier to reason about.
[PreviousSystem Eventschevron-left](https://docs.somnia.network/developer/reactivity/system-events)
[NextNo, this is not like regular EVM event subscriptionschevron-right](https://docs.somnia.network/developer/reactivity/no-this-is-not-like-regular-evm-event-subscriptions)
Last updated 2 months ago
---
# AML Compliance | Concepts | Somnia Docs
**Our Commitment:** Somnia ServiceCo. Ltd., Somnia TokenCo. Ltd., and Somnia NetworkCo. Ltd. (collectively, the "Companies") are committed to maintaining the highest standards of compliance with anti-money laundering (AML), counter-terrorism financing (CFT), and sanctions laws. We implement robust policies and procedures to prevent our services from being misused for illicit purposes.
**Policy Overview:** As companies incorporated in the British Virgin Islands, we voluntarily adopt comprehensive AML/CFT compliance measures as a matter of best practice, implementing policies based on international standards and regulatory frameworks.
* * *
###
[hashtag](https://docs.somnia.network/concepts/miscellaneous/legal/aml-compliance#key-principles)
KEY PRINCIPLES
**Risk-Based Approach:** We employ a comprehensive risk-based approach to identify, assess, and mitigate money laundering, terrorist financing, and proliferation financing risks across our operations, including:
* Customer due diligence assessments
* Geographic risk evaluation
* Product and service risk analysis
* Transaction monitoring
**Know Your Customer (KYC):** We conduct thorough customer identification and verification procedures for all clients, including:
* Identity verification
* Beneficial ownership identification
* Sanctions screening
* Enhanced due diligence for high-risk customers
**Ongoing Monitoring:** We maintain continuous monitoring of business relationships and transactions to:
* Detect unusual or suspicious activities
* Ensure consistency with customer profiles
* Update customer information regularly
* Screen against sanctions lists
###
[hashtag](https://docs.somnia.network/concepts/miscellaneous/legal/aml-compliance#compliance-framework)
COMPLIANCE FRAMEWORK
**Prohibited Activities:** We do not engage in business relationships or transactions with:
* Individuals or entities on sanctions lists
* Politically Exposed Persons (PEPs) without enhanced due diligence
* Customers from high-risk jurisdictions without appropriate controls
* Any party involved in money laundering, terrorist financing, or other illicit activities
**Enhanced Due Diligence:** We apply enhanced due diligence measures for:
* High-risk jurisdictions (including but not limited to Burma, Iran, North Korea)
* Politically Exposed Persons and their associates
* Complex corporate structures
* Unusual transaction patterns
**Record Keeping:** We maintain comprehensive records of:
* Customer identification and verification documents
* Transaction records
* Compliance documentation
* Suspicious activity reports
All records are retained for a minimum of five years in secure, confidential systems.
###
[hashtag](https://docs.somnia.network/concepts/miscellaneous/legal/aml-compliance#reporting)
REPORTING
**Suspicious Activity:** We have established procedures for identifying and reporting suspicious activities to relevant authorities. All employees are trained to recognize red flags and report concerns through appropriate channels.
**Regulatory Cooperation:** We maintain full cooperation with regulatory authorities and law enforcement agencies, responding promptly to information requests and regulatory inquiries.
###
[hashtag](https://docs.somnia.network/concepts/miscellaneous/legal/aml-compliance#training-and-awareness)
TRAINING AND AWARENESS
All relevant personnel receive regular AML/CFT training to ensure:
* Understanding of legal obligations
* Recognition of suspicious activities
* Proper implementation of compliance procedures
* Awareness of emerging risks and threats
###
[hashtag](https://docs.somnia.network/concepts/miscellaneous/legal/aml-compliance#contact)
CONTACT
For compliance-related inquiries or to report suspicious activities:
**Money Laundering Reporting Officer (MLRO):** Campbells Regulatory Services Limited Email: [\[email protected\]](https://docs.somnia.network/cdn-cgi/l/email-protection)
###
[hashtag](https://docs.somnia.network/concepts/miscellaneous/legal/aml-compliance#regulatory-framework)
REGULATORY FRAMEWORK
Our compliance program is designed to meet the requirements of applicable laws and regulations, including:
* BVI Anti-Money Laundering Regulations
* BVI Anti-Money Laundering and Terrorist Financing Code of Practice
* International sanctions regimes
* FATF Recommendations
* * *
_We regularly review and update our AML/CFT policies and procedures to ensure they remain effective and compliant with evolving regulatory requirements and industry best practices._
_This policy is effective as of the date of publication and is subject to periodic review and updates. For the most current version, please visit this page from time to time._
[PreviousLEGAL DISCLAIMERchevron-left](https://docs.somnia.network/concepts/miscellaneous/legal/legal-disclaimer)
[NextAirdrop Policychevron-right](https://docs.somnia.network/concepts/miscellaneous/legal/airdrop-policy)
Last updated 7 months ago
* [KEY PRINCIPLES](https://docs.somnia.network/concepts/miscellaneous/legal/aml-compliance#key-principles)
* [COMPLIANCE FRAMEWORK](https://docs.somnia.network/concepts/miscellaneous/legal/aml-compliance#compliance-framework)
* [REPORTING](https://docs.somnia.network/concepts/miscellaneous/legal/aml-compliance#reporting)
* [TRAINING AND AWARENESS](https://docs.somnia.network/concepts/miscellaneous/legal/aml-compliance#training-and-awareness)
* [CONTACT](https://docs.somnia.network/concepts/miscellaneous/legal/aml-compliance#contact)
* [REGULATORY FRAMEWORK](https://docs.somnia.network/concepts/miscellaneous/legal/aml-compliance#regulatory-framework)
---
# Push vs Pull: An Architectural Shift | Somnia Docs
Traditional EVM dApps "pull" data via polling (e.g., repeated getLogs or state rpc queries), leading to inefficiency and high rpc costs. Somnia Reactivity's "push" model notifies you proactively, transforming app architecture.
####
[hashtag](https://docs.somnia.network/developer/reactivity/push-vs-pull-an-architectural-shift#highlights)
Highlights
Aspect
Pull (Traditional)
Push (Somnia Reactivity)
Data Fetch
Poll RPCs periodically
Passive notifications
Latency
Seconds to minutes (poll interval)
Near-instant (block time)
RPC Calls
High (loops, retries)
Minimal (one sub setup)
Complexity
Manage loops, error handling
Simple callback/handler
Use Cases
Basic event listening
Real-time reactions, auto-updates
####
[hashtag](https://docs.somnia.network/developer/reactivity/push-vs-pull-an-architectural-shift#why-it-matters)
Why It Matters
* **Simplified Front-Ends**: No more `setInterval` for balances—push updates UIs directly.
* **Efficient Indexers**: Push to DBs instead of scanning blocks.
* **Cost Savings**: Avoid redundant queries.
Let the chain push changes to you and build realtime blockchain applications
[PreviousSubscriptions: The Core Primitivechevron-left](https://docs.somnia.network/developer/reactivity/subscriptions-the-core-primitive)
[NextSystem Eventschevron-right](https://docs.somnia.network/developer/reactivity/system-events)
Last updated 1 month ago
---
# Quickstart | Somnia Docs
circle-exclamation
**Reactivity is currently only available on TESTNET**
###
[hashtag](https://docs.somnia.network/developer/reactivity/quickstart#off-chain-typescript)
Off-chain (TypeScript)
####
[hashtag](https://docs.somnia.network/developer/reactivity/quickstart#sdk-installation)
📦 SDK Installation
Copy
npm i @somnia-chain/reactivity
####
[hashtag](https://docs.somnia.network/developer/reactivity/quickstart#plugging-into-the-sdk)
🔌 Plugging into the SDK
You'll need `viem` installed for the public and or wallet client. Install it with `npm i viem`.
Copy
import { createPublicClient, createWalletClient, http, defineChain } from 'viem'
import { SDK } from '@somnia-chain/reactivity'
// Example: Public client (required for reading data)
const chain = defineChain() // see viem docs for defining a chain
const publicClient = createPublicClient({
chain,
transport: http(),
})
// Optional: Wallet client for writes
const walletClient = createWalletClient({
account,
chain,
transport: http(),
})
const sdk = new SDK({
public: publicClient,
wallet: walletClient, // Omit if not executing transactions on-chain
})
####
[hashtag](https://docs.somnia.network/developer/reactivity/quickstart#activating-websocket-reactivity-subscriptions)
📡 Activating Websocket Reactivity Subscriptions
Use WebSocket subscriptions for real-time updates to contract event and state updates atomically. Define params and subscribe — the SDK handles the rest via WebSockets.
###
[hashtag](https://docs.somnia.network/developer/reactivity/quickstart#on-chain-solidity-handlers)
On-chain (Solidity handlers)
Developers can build Solidity smart contracts that get invoked when other contracts emit events—allowing smart contracts to "react" to what's happening on-chain.
In order to achieve this, we need two things:
1. A Somnia event handler smart contract (standard Solidity syntax).
2. A valid subscription with funds to pay for Solidity handler invocations. Creators of on-chain subscriptions are required to hold minimum balances (currently 32 SOM) that pay for handler invocations executed by validators.
####
[hashtag](https://docs.somnia.network/developer/reactivity/quickstart#creating-the-handler-smart-contract)
Creating the Handler Smart Contract
Very basic contract with the `@somnia-chain/reactivity-contracts` npm package installed
Once the handler is complete, deploy it using Foundry or Hardhat, and note the address — this will be required for creating a subscription.
####
[hashtag](https://docs.somnia.network/developer/reactivity/quickstart#setting-up-an-on-chain-subscription-using-the-sdk)
Setting Up an On-Chain Subscription (Using the SDK)
The following uses the TypeScript SDK to create and pay for a subscription that will invoke a handler contract for events emitted by other smart contracts. Another approach would be for the subscribing smart contract to directly hold the required SOM balance and have the logic for creating the subscription baked into one place, but that may not always be optimal.
[PreviousWhat is Reactivity?chevron-left](https://docs.somnia.network/developer/reactivity/what-is-reactivity)
[NextSubscriptions: The Core Primitivechevron-right](https://docs.somnia.network/developer/reactivity/subscriptions-the-core-primitive)
Last updated 23 hours ago
* [Off-chain (TypeScript)](https://docs.somnia.network/developer/reactivity/quickstart#off-chain-typescript)
* [On-chain (Solidity handlers)](https://docs.somnia.network/developer/reactivity/quickstart#on-chain-solidity-handlers)
Copy
import { SDK, SubscriptionInitParams, SubscriptionCallback } from '@somnia-chain/reactivity'
const initParams: SubscriptionInitParams = {
ethCalls: [], // State to read when events are emitted
onData: (data: SubscriptionCallback) => console.log('Received:', data),
}
const subscription = await sdk.subscribe(initParams)
Copy
pragma solidity ^0.8.20;
import { SomniaEventHandler } from "@somnia-chain/reactivity-contracts/contracts/SomniaEventHandler.sol";
contract ExampleEventHandler is SomniaEventHandler {
function _onEvent(
address emitter,
bytes32[] calldata eventTopics,
bytes calldata data
) internal override {
// Execute your logic here
// Be careful about emitting events to avoid infinite loops
}
}
Copy
import { SDK } from '@somnia-chain/reactivity';
import { parseGwei } from 'viem';
// Initialize the SDK
const sdk = new SDK({
public: publicClient,
wallet: walletClient,
})
// Create a Solidity subscription
// This is an example of a wildcard subscription to all events
// We do not need to supply SOM—the chain ensures min balance
await sdk.createSoliditySubscription({
handlerContractAddress: '0x123...',
priorityFeePerGas: parseGwei('2'), // 2 gwei — minimum recommended for validators to process
maxFeePerGas: parseGwei('10'), // 10 gwei — max you're willing to pay (base + priority)
gasLimit: 2_000_000n, // Minimum recommended for state changes, increase for complex logic
isGuaranteed: true,
isCoalesced: false,
});
---
# Airdrop Policy | Concepts | Somnia Docs
This Airdrop Policy ("Policy") governs the distribution of SOMI tokens ("Tokens") by Somnia TokenCo Ltd. ("Company," "we," "us," or "our") to eligible participants ("Participants", “you”, “your”) through our token airdrop program ("Airdrop"). By participating in the Airdrop, you agree to be bound by this Policy and our Terms of Service.
* * *
###
[hashtag](https://docs.somnia.network/concepts/miscellaneous/legal/airdrop-policy#disclaimer-this-policy-governs-your-participation-in-the-airdrop-and-constitutes-a-legally-binding-a)
**DISCLAIMER: THIS POLICY GOVERNS YOUR PARTICIPATION IN THE AIRDROP AND CONSTITUTES A LEGALLY BINDING AGREEMENT BETWEEN YOU AND US. PLEASE READ THIS POLICY CAREFULLY. YOUR PARTICIPATION IN THE AIRDROP SHALL CONSTITUTE YOUR ACCEPTANCE TO THIS AIRDROP POLICY IN ITS ENTIRETY.**
* * *
**By participating in the Airdrop, you acknowledge that:**
1. Somnia assumes no responsibility for your Participation and the consequences thereof;
2. Somnia does not offer any professional advice or recommendations regarding the Airdrop or tokens. The information provided is for informational purpose only, nothing in this Policy shall be construed as being professional advice;
3. This Policy does not establish any fiduciary relationship between Somnia and Participants. Somnia disclaims any fiduciary duty or liability to the maximum extent permitted by law;
4. You are making an independent decision in respect of your participation and your participation is at your own risk; and
5. It is your responsibility to seek professional legal, tax or other advice before participating in the Airdrop.
###
[hashtag](https://docs.somnia.network/concepts/miscellaneous/legal/airdrop-policy#eligibility)
**ELIGIBILITY**
**Who can participate:**
1. Participants must be at least 18 years of age and not a Prohibited Person. A “Prohibited Person” is any person or entity that is (a) the subject of any economic or trade sanctions administered or enforced by any governmental authority, including any person designated on any list of prohibited or restricted parties by any governmental authority, such as the European Union (“EU”) Consolidated List of Persons, the United Kingdom (“UK”) Consolidated List of Financial Sanctions Targets, the United States (“U.S.”) Treasury Department’s list of Specially Designated Nationals, and the U.S. Department of Commerce Denied Persons or Entity Lists; (b) located in, incorporated in, or otherwise organized or established in, or resident of, any country, territory, or jurisdiction that is the subject of comprehensive country-wide or regional economic sanctions or embargoes or has been designated as “terrorist supporting” by the United Nations (“UN”) or any governmental authority of the EU, UK, or the U.S., including the Office of Foreign Assets Control (“OFAC”) of the U.S. Treasury Department or the Office of Financial Sanctions (“OFSI”) of HM Treasury of the UK (each such country, territory, or jurisdiction, a “Prohibited Geo”); (c) owned or controlled by such persons or entities described in (a)-(b); or (d) accessing the Airdrop on behalf of persons or entities described in (a)-(c). You agree not to use virtual private networks or other tools to circumvent these restrictions. Any such deliberate circumvention, or attempted circumvention, of our controls may permanently disqualify you from participation in the airdrop, as determined in our discretion.
2. Participants must hold an EVM-compatible wallet.
3. Participants are required to fulfill all the verification steps provided in this Policy or as may be communicated by us from time to time.
4. This Airdrop is not available to residents of the United Kingdom (UK) or United States of America (USA). Participants must not use virtual private networks or similar technology to bypass geographic restrictions.
5. Participants are responsible for ensuring compliance with the local laws and regulations in their jurisdiction.
6. If you are participating on behalf of an entity, you represent that you have full authority and proper authorization to bind that entity to this Policy.
**Account verification requirements:**
* Participants must connect their EVM-compatible wallet to our platform to verify eligibility.
* The authenticity of Participants shall be verified to ensure that the Participant is not a bot or using an automated account and is a legal person.
###
[hashtag](https://docs.somnia.network/concepts/miscellaneous/legal/airdrop-policy#claiming)
CLAIMING
To be able to claim the Airdrop, you will need to complete the account verification requirements provided above. Failure to complete the authorisation may be considered as forfeiture of Tokens.
Participants must claim their tokens within the specified timeframe. Unclaimed tokens after the deadline will be forfeited. The claiming deadline will be announced separately.
Somnia reserves the right to determine, at its sole discretion, both your eligibility to receive Airdrop and the specific number of Tokens allocated to each Participant based on their previous contributions.
> Somnia is under no obligation to communicate eligibility requirements to participants or potential participants before, during, or after the Airdrop claim period begins. The Participant shall ensure their eligibility to claim in accordance with this Policy.
You agree and acknowledge that you may not pursue any legal claims against Somnia or its representatives if any Airdrop Tokens remain unclaimed due to:
1. technical glitches beyond Somnia’s control;
2. issues with smart contract;
3. inability to pay gas fees;
4. wallet incompatibilities;
5. loss of access to a wallet or the key;
6. any other circumstances beyond Somnia’s control.
###
[hashtag](https://docs.somnia.network/concepts/miscellaneous/legal/airdrop-policy#technical-requirements)
**TECHNICAL REQUIREMENTS**
**Wallet and Network Requirements:** For a successful Airdrop claim, the Participant shall abide by the following requirements:
1. The Participant must have an EVM-compatible wallet
2. Somnia chain must be added to the wallet
3. SOMI token contract must be added to the wallet
4. Stable internet connection is required for the claiming process
**Gas Fees and Transaction Costs:**
* Participants are responsible for all gas fees and transaction costs associated with claiming and managing their tokens;
* You must ensure that there is sufficient funds in your wallet to cover any such transaction fees.
###
[hashtag](https://docs.somnia.network/concepts/miscellaneous/legal/airdrop-policy#anti-fraud-and-security-measures)
ANTI FRAUD AND SECURITY MEASURES
**Prohibited Activities and Disqualifications:** Including but not limited to, the following acts are prohibited and may result in disqualification of the Participant from the Airdrop:
* Use of bots, automated scripts, or any artificial means to participate;
* Creation or use of multiple accounts to increase allocation;
* Any form of fraud, manipulation, or abuse of the airdrop system
* Violation of our terms of service or community guidelines
**Enforcement:** Somnia reserves the sole and absolute right to investigate any Participant or their wallets that appear to be malicious, fraudulent, operated by bots, abusing the system, or is found to be conducting any prohibited activities provided above.
Somnia reserves the sole and absolute right to disqualify and/or blacklist any Participant or potential Participant and their wallet it deems ineligible for the Airdrop. Disqualification decisions shall be final and not subject to appeal.
###
[hashtag](https://docs.somnia.network/concepts/miscellaneous/legal/airdrop-policy#other-terms-and-conditions)
**OTHER TERMS AND CONDITIONS**
**Token Value Disclaimer**
> The airdropped tokens may have no monetary value. There is no guarantee that these tokens will have any market value, utility, or liquidity. The Participant must participate at their own risk. We make no representations about the future market value of the Token
**Tax and Regulatory Responsibilities:** You are responsible for any taxes or reporting obligations arising from the receipt of these Tokens and are fully responsible for compliance with all applicable laws, regulations, and tax requirements in your jurisdiction. All information provided in connection with the Airdrop or otherwise provided by Somnia is for informational purposes only and is not and should not be construed as professional tax or legal advice regarding the Airdrop or Tokens. Participants must consult their own tax or legal advisor.
**Not an investment advice, DYOR:** The Token is not intended to constitute securities of any form or any other form of investment in any jurisdiction. This Policy is for informational purposes only and does not constitute a prospectus, offer document of any sort, or investment solicitation. No financial regulatory authority has examined or approved of this Policy.
**Program Modifications:** Somnia reserves the absolute right to cancel, modify, suspend, or terminate the airdrop program at any time without prior notice and without any liability. We may change the eligibility criteria, distribution amounts, unlock conditions, or any other aspects of the program. Somnia is not liable to provide any compensation to the Participant for program changes or cancellations.
**Privacy and Data:** All data collection and processing related to this Airdrop is governed by our Privacy Policy available at: [https://somnia.network/privacy-policyarrow-up-right](https://somnia.network/privacy-policy)
**User Disclaimer:** The Airdrop is provided on an “as is'” and “as available” basis. Somnia expressly disclaims all warranties of any kind, whether express, implied, or statutory, including the implied warranties of merchantability, fitness for a particular purpose, title, and non-infringement. We do not guarantee that the Airdrop will be uninterrupted, timely, secure, error-free, or meet your specific requirements.
You agree and acknowledge that your participation in the Airdrop does not violate any applicable laws, including without limitation, applicable economic and trade sanctions and export control laws and regulations, such as those administered and enforced by the EU, OFSI, OFAC, the U.S. Department of State, the U.S. Department of Commerce, the UN Security Council, and other relevant authorities. You also agree that you will not sell, assign or transfer control of any Tokens you receive in the Airdrop to a person or wallet address that would violate these terms if claimed directly by such person or wallet.
**Limitation of Liability:** Our liability shall be limited to the fullest extent permitted by law. You release us and our team from any liability related to the Airdrop or Token use, including lost funds, third-party wallet security breaches, market value fluctuations, technical errors, or geo-blocking.
**Indemnification:** The Participant agrees to defend, indemnify and hold harmless, Somnia and its representatives against any claims, losses, or legal costs arising from your Participation in the Airdrop program or breach of this Policy.
**Dispute Disclaimer:** In no event shall Somnia or any Somnia person be held liable in connection with or for any claims, losses, damages, or other liabilities, whether in contract, tort, or otherwise, arising out of or in connection with the airdrop or the receipt of any tokens. By participating in this airdrop, you agree to resolve all disputes by individual arbitration and expressly waive your right to engage in class actions, class arbitration and representative actions.
**Final Terms:** By participating in this airdrop, you acknowledge that you have read, understood, and agree to be bound by this Policy. You participate entirely at your own risk and acknowledge that Somnia makes no representations or warranties regarding the tokens or their future value. You represent and warrant that all information provided during the Airdrop process is true, accurate, and complete.
We reserve the right to modify, add, or remove any section of this Policy from time to time or terminate the Airdrop or this Policy at any time with or without notice. It is your responsibility to keep yourself updated with the changes to this Policy. Continued participation shall constitute your acceptance of any modifications.
[PreviousAML Compliancechevron-left](https://docs.somnia.network/concepts/miscellaneous/legal/aml-compliance)
[NextGovernancechevron-right](https://docs.somnia.network/concepts/miscellaneous/legal/governance)
Last updated 7 months ago
* [DISCLAIMER: THIS POLICY GOVERNS YOUR PARTICIPATION IN THE AIRDROP AND CONSTITUTES A LEGALLY BINDING AGREEMENT BETWEEN YOU AND US. PLEASE READ THIS POLICY CAREFULLY. YOUR PARTICIPATION IN THE AIRDROP SHALL CONSTITUTE YOUR ACCEPTANCE TO THIS AIRDROP POLICY IN ITS ENTIRETY.](https://docs.somnia.network/concepts/miscellaneous/legal/airdrop-policy#disclaimer-this-policy-governs-your-participation-in-the-airdrop-and-constitutes-a-legally-binding-a)
* [ELIGIBILITY](https://docs.somnia.network/concepts/miscellaneous/legal/airdrop-policy#eligibility)
* [CLAIMING](https://docs.somnia.network/concepts/miscellaneous/legal/airdrop-policy#claiming)
* [TECHNICAL REQUIREMENTS](https://docs.somnia.network/concepts/miscellaneous/legal/airdrop-policy#technical-requirements)
* [ANTI FRAUD AND SECURITY MEASURES](https://docs.somnia.network/concepts/miscellaneous/legal/airdrop-policy#anti-fraud-and-security-measures)
* [OTHER TERMS AND CONDITIONS](https://docs.somnia.network/concepts/miscellaneous/legal/airdrop-policy#other-terms-and-conditions)
---
# Comparison | Somnia Docs
Somnia Reactivity supports two subscription modes: off-chain (via WebSocket in TypeScript) for flexible, external app integration, and on-chain (via Solidity handlers) for automated, trustless blockchain reactions. Choose based on your dApp's needs—off-chain for UIs/backends, on-chain for DeFi/automation.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tooling/comparison#comparison-table)
Comparison Table
Aspect
Off-Chain (WebSocket/TypeScript)
On-Chain (Solidity/EVM)
Delivery Mechanism
WebSocket push to your app/server
Direct EVM invocation of handler contract
Execution Environment
Off-chain (Node.js, browser)
On-chain (Somnia validators execute)
Gas / Costs
None per notification (pay for node or rpc provider)
Pays gas per invocation (from min 32 SOM balance)
Latency
Near-real-time (block time + network)
Block time (atomic with chain state)
State Access
Include ETH view calls in sub; flexible queries
Limited to handler logic; no external calls;Solidity contract does its own external view calls
Use Cases
Front-ends (live UIs), backends (DB updates), integrations
DeFi (auto-compound), NFTs (reactive mints), oracles
Reliability Options
Can specify callback only executed when state changes
Can specify if you want delivery regardless if block inclusion distance > 0, can also coalece
Setup Complexity
Install SDK, subscribe callback
Deploy handler contract, create/fund sub
Security
App-level (e.g., auth WebSockets)
Blockchain-level (reentrancy risks in handlers)
Scalability
Handles high volume off-chain
Limited by gas/block; use coalescing for batches
####
[hashtag](https://docs.somnia.network/developer/reactivity/tooling/comparison#when-to-use-off-chain-subscriptions)
When to Use Off-Chain Subscriptions
* **Pros**: No gas per event, easy integration with web apps, full access to external data/APIs.
* **Cons**: Relies on your app being online
* **Example**: Real-time dashboard updating on Transfer events without chain writes.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tooling/comparison#when-to-use-on-chain-subscriptions)
When to Use On-Chain Subscriptions
* **Pros**: Fully decentralized reactions; executes automatically on-chain.
* **Cons**: Gas costs accumulate; potential for loops if handlers emit events.
* **Example**: Smart contract that auto-swaps tokens on price oracle updates.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tooling/comparison#hybrid-approaches)
Hybrid Approaches
Combine both: Use off-chain for monitoring/UI, trigger on-chain actions via transactions when needed.
[PreviousToolingchevron-left](https://docs.somnia.network/developer/reactivity/tooling)
[NextOff-Chain (TypeScript)chevron-right](https://docs.somnia.network/developer/reactivity/tooling/off-chain-typescript)
Last updated 1 month ago
---
# Concepts | Somnia Docs
[Understanding Schemas, Schema IDs, Data IDs, and Publisherchevron-right](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher)
[Extending and composing data schemaschevron-right](https://docs.somnia.network/developer/data-streams/concepts/extending-and-composing-data-schemas)
[Somnia Data vs Event Streamschevron-right](https://docs.somnia.network/developer/data-streams/concepts/somnia-data-vs-event-streams)
[Intersection with Somnia Reactivitychevron-right](https://docs.somnia.network/developer/data-streams/concepts/intersection-with-somnia-reactivity)
[Data Provenance and Verification in Streamschevron-right](https://docs.somnia.network/developer/data-streams/concepts/data-provenance-and-verification-in-streams)
[PreviousQuickstartchevron-left](https://docs.somnia.network/developer/data-streams/quickstart)
[NextUnderstanding Schemas, Schema IDs, Data IDs, and Publisherchevron-right](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher)
---
# Extending and composing data schemas | Somnia Docs
New schemas can extend other schemas by setting a parent schema ID. Remember, you can take any raw schema string and compute a schema ID from it. When registering a new schema that builds upon and extends another, you would specify the raw schema string for the new schema as well as specifying the optional parent schema ID. The parent schema ID will be critical later for deserialising data written to chain. For schemas that do not extend other schemas (when nothing is available), then one does not need to specify a parent schema ID or can optionally specify the zero value for the bytes32 solidity type. For maximum composability, all schemas should be public.
###
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/extending-and-composing-data-schemas#extension-in-practice-example-1)
Extension in practice (Example 1)
Copy
import { SDK } from "@somnia-chain/streams"
const sdk = new SDK({
public: getPublicClient(),
wallet: getWalletClient(),
})
// The parent schema here will be the GPS schema from the quick start guide
const gpsSchema = `uint64 timestamp, int32 latitude, int32 longitude, int32 altitude, uint32 accuracy, bytes32 entityId, uint256 nonce`
const parentSchemaId = await sdk.streams.computeSchemaId(gpsSchema)
// Lets extend the gps schema and add F1 data since every car will have a gps position
const formulaOneSchema = `uint256 driverNumber`
// We can also extend the gps schema for FR data i.e. aircraft identifier
const flightRadarSchema = `bytes32 ICAO24`
await sdk.streams.registerDataSchemas([\
{ schemaName: "gps", schema: gpsSchema },\
{ schemaName: "f1", schema: formulaOneSchema, parentSchemaId }, // F1 extends GPS\
{ schemaName: "FR", schema: flightRadarSchema, parentSchemaId },// FR extends GPS\
])
The typescript code shows how two new schemas re-use the GPS schema in order to append an additional field
###
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/extending-and-composing-data-schemas#extension-in-practice-example-2)
Extension in practice (Example 2)
Versioned schemas
Client's that are reading data associated with the derived schemas, use the SDK to get the fully decoded data since data is retrieved by schema ID (See `getByKey` from the quick start guide). Essentially the SDK does a number of the following pseudo steps:
1. Fetch schema and recursively fetch parent schema until the end of the chain is reached
2. Join all schemas together seperated by comma
3. Spin up the decoder and pass through the raw data stored on-chain
4. Return the decoded data to the caller
[PreviousUnderstanding Schemas, Schema IDs, Data IDs, and Publisherchevron-left](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher)
[NextSomnia Data vs Event Streamschevron-right](https://docs.somnia.network/developer/data-streams/concepts/somnia-data-vs-event-streams)
Last updated 1 month ago
* [Extension in practice (Example 1)](https://docs.somnia.network/developer/data-streams/concepts/extending-and-composing-data-schemas#extension-in-practice-example-1)
* [Extension in practice (Example 2)](https://docs.somnia.network/developer/data-streams/concepts/extending-and-composing-data-schemas#extension-in-practice-example-2)
Copy
import { SDK } from "@somnia-chain/streams"
const sdk = new SDK({
public: getPublicClient(),
wallet: getWalletClient(),
})
const versionSchema = `uint16 version`
const parentSchemaId = await sdk.streams.computeSchemaId(versionSchema)
// Now lets register a person schema with expectation there will be many versions of the person schema
const personSchema = `uint8 age`
await sdk.streams.registerDataSchemas([\
{ schemaName: "version", schema: versionSchema },\
{ schemaName: "person", schema: personSchema, parentSchemaId }\
])
---
# No, this is not like regular EVM event subscriptions | Somnia Docs
Think event subscriptions are old news? On Ethereum or other EVM chains, they're just events, no state, and no on-chain reactions. Somnia's push subscriptions deliver state along side event data, something other EVMs cannot offer.
####
[hashtag](https://docs.somnia.network/developer/reactivity/no-this-is-not-like-regular-evm-event-subscriptions#chain-comparison)
Chain Comparison
* **Other Chains**: `eth_subscribe` gives events only—you still pull state separately, risking inconsistency.
* **Somnia**: Pushes event + state atomically; invokes Solidity handlers directly.
####
[hashtag](https://docs.somnia.network/developer/reactivity/no-this-is-not-like-regular-evm-event-subscriptions#code-comparison)
Code Comparison
**Ethereum (Pull)**:
Copy
web3.eth.subscribe('logs', { address: '0x...' }, (err, log) => {
// Now pull state manually
contract.methods.balanceOf(...).call();
});
**Somnia (Push)**:
Copy
sdk.subscribe({ ethCalls: ['balanceOf'], onData: (data) => {
// Event + state delivered with `data`
});
[PreviousState Consistency Guaranteeschevron-left](https://docs.somnia.network/developer/reactivity/state-consistency-guarantees)
[NextToolingchevron-right](https://docs.somnia.network/developer/reactivity/tooling)
Last updated 1 month ago
---
# What is Somnia Data Streams? | Somnia Docs
Somnia Data Streams is a structured data layer for EVM chains. Somnia Data streams enable developers to build applications that both emit EVM event logs and write data to the Somnia chain without Solidity. This means developers do not need to know Solidity to build applications using Somnia Data Streams.
Somnia Data streams allow parsing schema data into contract storage, where developers define a schema (a typed, ordered layout of fields), then publish and subscribe to data that conforms to that schema.
Think of reading data from Streams as an emitted event, but with an SDK: publishers write strongly-typed records; subscribers read them by schema and publisher, and decode to rich objects.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#why-streams)
Why Streams?
-----------------------------------------------------------------------------------------------------------------------
Traditional approaches each have trade-offs:
* Contract events are great for signaling, but untyped at the app level (you still write your own ABI and decoders across projects). Events are also hard to stitch into reusable data models.
* Custom contract storage is powerful but heavyweight, and you maintain the whole schema logic, CRUD, indexing patterns, and migrations.
* Off-chain DB and proofs are flexible but brittle; either centralized or require extra machinery.
* Oracles are useful for external data, but not a generic modeling layer for app-originated data.
Streams solves this by standardizing:
1. Schemas (the “data ABI”)
2. Publish/Subscribe primitives (SDK, not boilerplate contracts)
3. Deterministic IDs (schemaId, dataId) and provenance (publisher address)
This results in interoperable, verifiable, composable data with minimal app code.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#when-to-use-streams)
When to Use Streams
--------------------------------------------------------------------------------------------------------------------------------------
Use Streams when you need:
* Typed, shareable data across apps (chat messages, GPS, player stats, telemetry)
* Multiple publishers writing the same kind of record
* A standard decode flow with minimal custom indexing
* You need instant push to clients (Streams also works well with polling; you can add WS if desired)
Avoid Streams if:
* You need complex transactional logic/state machines tightly bound to contract invariants (build a contract)
* You must store large blobs (store off-chain, publish references/URIs in Streams)
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#definition-of-terms)
Definition of Terms
--------------------------------------------------------------------------------------------------------------------------------------
* **Schema**: a canonical string describing fields in order, e.g. uint64 timestamp, bytes32 roomId, string content, string senderName, address sender The exact string determines a schemaId (hash).
* **Publisher**: The signer that writes data. EOA or Smart Contract that writes data under a schema. Readers trust provenance by address.
* **Subscriber**: reader that fetches all records for a (schemaId, publisher) pair.
* **Data ID (dataId)**: developer-chosen 32-byte key per record (helps with lookups, dedup, pagination). Pick dataIds with predictable structure to enable point lookups or pagination seeds. E.g:
* Game: toHex('matchId-index', { size: 32 })
* Chat: toHex('room-timestamp', { size: 32 })
* GPS: toHex('device-epoch', { size: 32 })
* **Encoder**: converts typed values ⇄ bytes according to the schema.
* **schemaId**: computed from the schema string. Treat it like a contract address for data shape.
####
[hashtag](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#data-flow)
Data flow
You can have multiple publishers writing under the same schema; subscribers can aggregate them if desired.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#the-schema-your-data-abi)
The Schema: Your “Data ABI”
---------------------------------------------------------------------------------------------------------------------------------------------------
A schema is a compact, ordered list of typed fields. The exact string determines the computed `schemaId` Even whitespace and order matter.
Design guidance
* Put stable fields first (e.g., timestamp, entityId, type).
* Prefer fixed-width ints (e.g., uint64 for timestamps).
* Use bytes32 for keys/IDs (room, device, etc.).
* Use string for human-readable info (names, messages), but keep it short for gas efficiency.
* * *
###
[hashtag](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#data-writing-patterns)
Data Writing Patterns
* Single Publisher One server wallet publishes; User Interaces can read the schema using `getByKey` , `getAtIndex` , `getAllPublisherDataForSchema`.
* Multi-Publisher Many devices publish under a shared schema. Your API aggregates across a list of publisher addresses.
* Derived Views Build REST endpoints that query Streams and derive higher-level views (e.g., “latest per room”).
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#quickstart-in-5-minutes)
Quickstart in 5 Minutes
----------------------------------------------------------------------------------------------------------------------------------------------
You’ll register a schema, publish one message, and read it back — just to feel the flow.
###
[hashtag](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#install)
Install
###
[hashtag](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#set-up-env)
Set up Env
###
[hashtag](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#define-chain)
Define Chain
###
[hashtag](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#define-client)
Define Client
###
[hashtag](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#schema)
Schema
####
[hashtag](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#register-schema-optional-but-recommended)
Register Schema (optional but recommended)
chevron-rightscripts/register.ts[hashtag](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#scripts-register.ts)
###
[hashtag](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#publish-write)
Publish (Write)
###
[hashtag](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#read-data)
Read Data
That’s your first end-to-end loop.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#faqs)
FAQs
--------------------------------------------------------------------------------------------------------
Q: Do I need to register a schema? A: Registration is optional but recommended. You can publish to an unregistered schema (readers just need the exact string to decode). Registration helps discoverability and tooling.
Q: Can I change a schema later? A: Changing order or types yields a new schemaId. Plan for versioning (run v1 + v2 together).
Q: How do I page data? A: Use structured dataIds, or build a thin index off-chain that records block numbers / tx hashes per record.
Q: How does Streams differ from subgraphs? A: Streams defines how you write/read structured records with an SDK. Subgraphs (or other indexers) sit on top to query across many publishers, paginate, and filter efficiently.
Q: How do I handle large payloads? A: Store the payload elsewhere (IPFS, Arweave, S3) and put the URI + hash in Streams. Optionally encrypt off-chain.
[PreviousSomnia Data Streamschevron-left](https://docs.somnia.network/developer/data-streams)
[NextQuickstartchevron-right](https://docs.somnia.network/developer/data-streams/quickstart)
Last updated 1 month ago
* [Why Streams?](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#why-streams)
* [When to Use Streams](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#when-to-use-streams)
* [Definition of Terms](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#definition-of-terms)
* [The Schema: Your “Data ABI”](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#the-schema-your-data-abi)
* [Data Writing Patterns](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#data-writing-patterns)
* [Quickstart in 5 Minutes](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#quickstart-in-5-minutes)
* [Install](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#install)
* [Set up Env](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#set-up-env)
* [Define Chain](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#define-chain)
* [Define Client](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#define-client)
* [Schema](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#schema)
* [Publish (Write)](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#publish-write)
* [Read Data](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#read-data)
* [FAQs](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams#faqs)
Copy
+-------------+ publishData(payload) +--------------------+
| Publisher | --------------------------------> | Somnia Streams L1 |
| (wallet) | | (on-chain data) |
+-------------+ +--------------------+
^ |
| getAllPublisherDataForSchema
| v
+-------------+ +-----------+
| Subscriber | <------------------------------------ | Reader |
| (frontend) | | (SDK) |
+-------------+ +-----------+
Copy
npm i @somnia-chain/streams viem
Copy
RPC_URL=https://dream-rpc.somnia.network
PRIVATE_KEY=0xYOUR_FUNDED_PRIVATE_KEY
Copy
// lib/chain.ts
import { defineChain } from 'viem'
export const somniaTestnet = defineChain({
id: 50312, name: 'Somnia Testnet', network: 'somnia-testnet',
nativeCurrency: { name: 'STT', symbol: 'STT', decimals: 18 },
rpcUrls: { default: { http: ['https://dream-rpc.somnia.network'] }, public: { http: ['https://dream-rpc.somnia.network'] } },
} as const)
Copy
// lib/clients.ts
import { createPublicClient, createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { somniaTestnet } from './chain'
const RPC = process.env.RPC_URL as string
const PK = process.env.PRIVATE_KEY as `0x${string}`
export const publicClient = createPublicClient({ chain: somniaTestnet, transport: http(RPC) })
export const walletClient = createWalletClient({ account: privateKeyToAccount(PK), chain: somniaTestnet, transport: http(RPC) })
Copy
// lib/schema.ts
export const chatSchema =
'uint64 timestamp, bytes32 roomId, string content, string senderName, address sender'
Copy
import 'dotenv/config'
import { SDK, zeroBytes32 } from '@somnia-chain/streams'
import { publicClient, walletClient } from '../lib/clients'
import { chatSchema } from '../lib/schema'
import { waitForTransactionReceipt } from 'viem/actions'
async function main() {
const sdk = new SDK({ public: publicClient, wallet: walletClient })
const id = await sdk.streams.computeSchemaId(chatSchema)
const exists = await sdk.streams.isSchemaRegistered(id)
if (!exists) {
const tx = await sdk.streams.registerSchema(chatSchema, zeroBytes32)
await waitForTransactionReceipt(publicClient, { hash: tx })
}
console.log('schemaId:', id)
}
main()
Copy
// scripts/publish-one.ts
import 'dotenv/config'
import { SDK, SchemaEncoder } from '@somnia-chain/streams'
import { publicClient, walletClient } from '../lib/clients'
import { chatSchema } from '../lib/schema'
import { toHex, type Hex } from 'viem'
import { waitForTransactionReceipt } from 'viem/actions'
async function main() {
const sdk = new SDK({ public: publicClient, wallet: walletClient })
const schemaId = await sdk.streams.computeSchemaId(chatSchema)
const enc = new SchemaEncoder(chatSchema)
const payload: Hex = enc.encodeData([\
{ name: 'timestamp', value: Date.now().toString(), type: 'uint64' },\
{ name: 'roomId', value: toHex('general', { size: 32 }), type: 'bytes32' },\
{ name: 'content', value: 'Hello Somnia!', type: 'string' },\
{ name: 'senderName', value: 'Alice', type: 'string' },\
{ name: 'sender', value: walletClient.account!.address, type: 'address' },\
])
const dataId = toHex(`general-${Date.now()}`, { size: 32 })
const tx = await sdk.streams.setAndEmitEvents(
[{ id: dataId, schemaId, data }],
[{ id: CHAT_EVENT_ID, argumentTopics: topics.slice(1), data: eventData }]
)
if (!tx) throw new Error('Failed to setAndEmitEvents')
await waitForTransactionReceipt(getPublicHttpClient(), { hash: tx })
return { txHash: tx }
}
main()
Copy
// scripts/read-all.ts
import 'dotenv/config'
import { SDK } from '@somnia-chain/streams'
import { publicClient } from '../lib/clients'
import { chatSchema } from '../lib/schema'
import { toHex } from 'viem'
type Field = { name: string; type: string; value: any }
const val = (f: Field) => f?.value?.value ?? f?.value
async function main() {
const sdk = new SDK({ public: publicClient })
const schemaId = await sdk.streams.computeSchemaId(chatSchema)
const publisher = process.env.PUBLISHER as `0x${string}` || '0xYOUR_PUBLISHER_ADDR'
const rows = (await sdk.streams.getAllPublisherDataForSchema(schemaId, publisher)) as Field[][]
const want = toHex('general', { size: 32 }).toLowerCase()
for (const r of rows || []) {
const ts = Number(val(r[0]))
const ms = String(ts).length <= 10 ? ts * 1000 : ts
if (String(val(r[1])).toLowerCase() !== want) continue
console.log({
time: new Date(ms).toLocaleString(),
content: String(val(r[2])),
senderName: String(val(r[3])),
sender: String(val(r[4])),
})
}
}
main()
---
# FAQs & Troubleshooting | Somnia Docs
###
[hashtag](https://docs.somnia.network/developer/reactivity/faqs-and-troubleshooting#faqs)
FAQs
circle-exclamation
**Somnia Reactivity is currently only available on TESTNET**
####
[hashtag](https://docs.somnia.network/developer/reactivity/faqs-and-troubleshooting#how-do-i-fetch-historical-data)
How Do I Fetch Historical Data?
To get event and state data from before an application subscription was created there are a few approaches:
* Use a traditional indexer or tooling such as a subgraph
* Build a custom indexer which starts at an earlier block and persists data received from Somnia reactivity into a DB that can be queried from the chain
* Directly query historical data from the chain from within your application (generally inefficient)
###
[hashtag](https://docs.somnia.network/developer/reactivity/faqs-and-troubleshooting#troubleshooting)
Troubleshooting
####
[hashtag](https://docs.somnia.network/developer/reactivity/faqs-and-troubleshooting#issues-starting-websocket-subscriptions)
Issues starting websocket subscriptions
Two core reasons for this:
1. The chain definition for the Somnia testnet or mainnet does not contain a wss url
2. The wss url does not support the reactivity feature set or the rpc provider is having issues
####
[hashtag](https://docs.somnia.network/developer/reactivity/faqs-and-troubleshooting#too-many-active-websocket-subscriptions)
Too many active websocket subscriptions
Often seen in React applications where `useEffect` is not being used correctly or not used at all to start an event subscription leading to attempts to create a subscription every time the page renders
####
[hashtag](https://docs.somnia.network/developer/reactivity/faqs-and-troubleshooting#solidity-handler-not-being-invoked)
Solidity handler not being invoked
Three core reasons for this:
1. Invalid implementation of `SomniaEventHandler` interface
2. No active subscription
3. Insufficient subscription balance
[PreviousGas Configurationchevron-left](https://docs.somnia.network/developer/reactivity/gas-configuration)
[NextAPI Referencechevron-right](https://docs.somnia.network/developer/reactivity/api-reference)
Last updated 1 month ago
* [FAQs](https://docs.somnia.network/developer/reactivity/faqs-and-troubleshooting#faqs)
* [Troubleshooting](https://docs.somnia.network/developer/reactivity/faqs-and-troubleshooting#troubleshooting)
---
# Tutorials | Somnia Docs
[“Hello World” Appchevron-right](https://docs.somnia.network/developer/data-streams/tutorials/hello-world-app)
[Build Your First Schemachevron-right](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema)
[Streams Case Study: Formula 1chevron-right](https://docs.somnia.network/developer/data-streams/tutorials/streams-case-study-formula-1)
[READ Stream Data from a UI (Next.js Example)chevron-right](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example)
[Integrate Chainlink Oracleschevron-right](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles)
[Working with Multiple Publishers in a Shared Streamchevron-right](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream)
[The DApp Publisher Proxy Patternchevron-right](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern)
[Build a Minimal On-Chain Chat Appchevron-right](https://docs.somnia.network/developer/data-streams/tutorials/build-a-minimal-on-chain-chat-app)
[Build a Realtime On-Chain Gamechevron-right](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game)
[PreviousSDK Methods Guidechevron-left](https://docs.somnia.network/developer/data-streams/sdk-methods-guide)
[Next“Hello World” Appchevron-right](https://docs.somnia.network/developer/data-streams/tutorials/hello-world-app)
---
# Quickstart | Somnia Docs
[hashtag](https://docs.somnia.network/developer/data-streams/quickstart#pre-requisites)
Pre-requisites
-----------------------------------------------------------------------------------------------------------
A typescript environment with [`viem`arrow-up-right](https://viem.sh/)
and [`@somnia-chain/streams`arrow-up-right](https://www.npmjs.com/package/@somnia-chain/streams)
installed
[hashtag](https://docs.somnia.network/developer/data-streams/quickstart#steps)
Steps
-----------------------------------------------------------------------------------------
###
[hashtag](https://docs.somnia.network/developer/data-streams/quickstart#id-1.-define-your-schema-as-a-string-and-plug-it-into-the-schema-encoder)
1\. Define your schema as a string and plug it into the schema encoder
Copy
import { SDK, zeroBytes32, SchemaEncoder } from "@somnia-chain/streams"
const gpsSchema = `uint64 timestamp, int32 latitude, int32 longitude, int32 altitude, uint32 accuracy, bytes32 entityId, uint256 nonce`
const schemaEncoder = new SchemaEncoder(gpsSchema)
`schemaEncoder` can now be used to encode data for broadcast and also decode data when reading it from Somnia Data Stream SDK.
###
[hashtag](https://docs.somnia.network/developer/data-streams/quickstart#id-2.-compute-your-unique-schema-identifier-from-the-schema)
2\. Compute your unique schema identifier from the schema
Copy
const sdk = new SDK({
public: getPublicClient(),
wallet: getWalletClient(),
})
const schemaId = await sdk.streams.computeSchemaId(gpsSchema)
console.log(`Schema ID ${schemaId}`)
All data broadcast with the Somnia Data Stream SDK write mechanism must be linked to a schema ID so that we know how to decode the data on read.
###
[hashtag](https://docs.somnia.network/developer/data-streams/quickstart#id-3.-encode-the-data-you-want-to-store-that-is-compatible-with-the-schema)
3\. Encode the data you want to store that is compatible with the schema
The value returned is a raw hex encoded bytes value that can be broadcast on-chain via the Somnia Data Stream SDK.
###
[hashtag](https://docs.somnia.network/developer/data-streams/quickstart#id-4.-publish-data-with-our-without-a-public-schema)
4\. Publish data (with our without a public schema)
`set` has the following parameter `dataStreams` which is a list of data points being written to chain `dataStreams` has the `DataStream[]` type:
###
[hashtag](https://docs.somnia.network/developer/data-streams/quickstart#id-5.-direct-data-read-without-reactivity)
5\. Direct data read without reactivity
This last step shows how you request data from Somnia data streams filtering on:
1. Schema ID
2. Address of the account that wrote the data to chain
1. This could be an EOA or another smart contract
The response from `getByKey` will be the data published but decoded for the specified schema.
Note: where the schema ID is associated with a public data schema that has been registered on-chain, the SDK will automatically decode the raw data published on-chain and return that decoded data removing the need for the decoder. If the schema is not public, the schema decoder will be required outside of the SDK and you will instead get raw bytes from the chain. Example:
Further filters can be applied client side to the data in order to filter for specifics within the data. GitBook also allows you to set up a bi-directional sync with an existing repository on GitHub or GitLab. Setting up Git Sync allows you and your team to write content in GitBook or in code, and never have to worry about your content becoming out of sync.
[PreviousWhat is Somnia Data Streams?chevron-left](https://docs.somnia.network/developer/data-streams/what-is-somnia-data-streams)
[NextConceptschevron-right](https://docs.somnia.network/developer/data-streams/concepts)
Last updated 1 month ago
* [Pre-requisites](https://docs.somnia.network/developer/data-streams/quickstart#pre-requisites)
* [Steps](https://docs.somnia.network/developer/data-streams/quickstart#steps)
* [1\. Define your schema as a string and plug it into the schema encoder](https://docs.somnia.network/developer/data-streams/quickstart#id-1.-define-your-schema-as-a-string-and-plug-it-into-the-schema-encoder)
* [2\. Compute your unique schema identifier from the schema](https://docs.somnia.network/developer/data-streams/quickstart#id-2.-compute-your-unique-schema-identifier-from-the-schema)
* [3\. Encode the data you want to store that is compatible with the schema](https://docs.somnia.network/developer/data-streams/quickstart#id-3.-encode-the-data-you-want-to-store-that-is-compatible-with-the-schema)
* [4\. Publish data (with our without a public schema)](https://docs.somnia.network/developer/data-streams/quickstart#id-4.-publish-data-with-our-without-a-public-schema)
* [5\. Direct data read without reactivity](https://docs.somnia.network/developer/data-streams/quickstart#id-5.-direct-data-read-without-reactivity)
Copy
const encodedData: Hex = schemaEncoder.encodeData([\
{ name: "timestamp", value: Date.now().toString(), type: "uint64" },\
{ name: "latitude", value: "51509865", type: "int32" },\
{ name: "longitude", value: "-0118092", type: "int32" },\
{ name: "altitude", value: "0", type: "int32" },\
{ name: "accuracy", value: "0", type: "uint32" },\
{ name: "entityId", value: zeroBytes32, type: "bytes32" }, // object providing GPS data\
{ name: "nonce", value: "0", type: "uint256" },\
])
Copy
const publishTxHash = await sdk.streams.set([{\
id: toHex("london", { size: 32 }),\
schemaId: computedGpsSchemaId,\
data: encodedData,\
}])
Copy
type Hex = `0x{string}`
type DataStream = {
id: Hex // Unique data key for the publisher
schemaId: Hex // Computed from the raw schema string
data: Hex // From step 3, raw bytes data formated as a hex string
}
Copy
const data = await sdk.streams.getByKey(
computedGpsSchemaId,
publisherWalletAddress,
dataKey
)
Copy
if (data) {
schemaEncoder.decode(data)
}
---
# System Events | Somnia Docs
There are two events that are generated by the system, this is represented in Solidity as:
Copy
event BlockTick(uint64 indexed blockNumber);
event BlockTick(uint64 indexed epochNumber, uint64 indexed blockNumber);
event Schedule(uint256 indexed timestampMillis);
You can subscribe to those events as any other. The system will generate those events for every block and match with any subscriptions.
circle-info
Remember to set the `emitter` field to `SOMNIA_REACTIVITY_PRECOMPILE_ADDRESS`. This will make sure that your handler will only respond to system events.
###
[hashtag](https://docs.somnia.network/developer/reactivity/system-events#block-tick-event)
Block Tick Event
If `blockNumber` is provided then this event will trigger at the specific block. Otherwise this will be triggered at every block, ~10 times per second.
This example will tick at every single block:
Copy
ISomniaReactivityPrecompile.SubscriptionData
memory subscriptionData = ISomniaReactivityPrecompile
.SubscriptionData({
eventTopics: [BlockTick.selector, bytes32(0), bytes32(0), bytes32(0)],
emitter: SomniaExtensions.SOMNIA_REACTIVITY_PRECOMPILE_ADDRESS,
handlerContractAddress: address(this),
handlerFunctionSelector: ISomniaEventHandler.onEvent.selector,
/*...*/
});
###
[hashtag](https://docs.somnia.network/developer/reactivity/system-events#epoch-tick-event)
Epoch Tick Event
If `epochNumber` is provided then this event will trigger at the beginning of the specific epoch. Otherwise this will be triggered at every epoch, roughly every ~5 minutes.
This example will tick at every single epoch:
###
[hashtag](https://docs.somnia.network/developer/reactivity/system-events#schedule-event)
Schedule Event
This event is useful for scheduling actions in the future. Few things to remember:
* The provided timestamp must be in the future, minimum is next second from the current block
* The subscription to `Schedule` is one-off and will be deleted after triggering
* The timestamp is expressed in milliseconds (see [https://currentmillis.com/arrow-up-right](https://currentmillis.com/)
for handy calculations)
his example will tick on Nov 11 2026 11:11:11.011 :
[PreviousPush vs Pull: An Architectural Shiftchevron-left](https://docs.somnia.network/developer/reactivity/push-vs-pull-an-architectural-shift)
[NextState Consistency Guaranteeschevron-right](https://docs.somnia.network/developer/reactivity/state-consistency-guarantees)
Last updated 1 day ago
* [Block Tick Event](https://docs.somnia.network/developer/reactivity/system-events#block-tick-event)
* [Epoch Tick Event](https://docs.somnia.network/developer/reactivity/system-events#epoch-tick-event)
* [Schedule Event](https://docs.somnia.network/developer/reactivity/system-events#schedule-event)
Copy
ISomniaReactivityPrecompile.SubscriptionData
memory subscriptionData = ISomniaReactivityPrecompile
.SubscriptionData({
eventTopics: [EpochTick.selector, bytes32(0), bytes32(0), bytes32(0)],
emitter: SomniaExtensions.SOMNIA_REACTIVITY_PRECOMPILE_ADDRESS,
handlerContractAddress: address(this),
handlerFunctionSelector: ISomniaEventHandler.onEvent.selector,
/*...*/
});
Copy
ISomniaReactivityPrecompile.SubscriptionData
memory subscriptionData = ISomniaReactivityPrecompile
.SubscriptionData({
eventTopics: [Schedule.selector, 1794395471011, bytes32(0), bytes32(0)],
emitter: SomniaExtensions.SOMNIA_REACTIVITY_PRECOMPILE_ADDRESS,
handlerContractAddress: address(this),
handlerFunctionSelector: ISomniaEventHandler.onEvent.selector,
/*...*/
});
---
# Building DApps | Somnia Docs
[Tokens and NFTschevron-right](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts)
[Wallet Integration and Authchevron-right](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth)
[OnRampschevron-right](https://docs.somnia.network/developer/building-dapps/onramps)
[Account Abstractionchevron-right](https://docs.somnia.network/developer/building-dapps/account-abstraction)
[Data Indexing and Queryingchevron-right](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying)
[Oracleschevron-right](https://docs.somnia.network/developer/building-dapps/oracles)
[Example Applicationschevron-right](https://docs.somnia.network/developer/building-dapps/example-applications)
[PreviousDebug Playbookchevron-left](https://docs.somnia.network/developer/development-frameworks/debug-playbook)
[NextTokens and NFTschevron-right](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts)
---
# On-chain (Solidity) | Somnia Docs
###
[hashtag](https://docs.somnia.network/developer/reactivity/tooling/on-chain-solidity#somnia-reactivity-precompile)
Somnia Reactivity Precompile
The Somnia Reactivity Precompile is located at address `0x0100`. It provides an interface for managing event subscriptions.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tooling/on-chain-solidity#interface)
Interface
The interface for the precompile is defined in `ISomniaReactivityPrecompile.sol`:
Copy
interface ISomniaReactivityPrecompile {
struct SubscriptionData {
bytes32[4] eventTopics; // Topic filter (0x0 for wildcard)
address origin; // Origin (tx.origin) filter (address(0) for wildcard)
address caller; // Caller (msg.sender) filter (address(0) for wildcard)
address emitter; // Contract emitting the event (address(0) for wildcard)
address handlerContractAddress; // Address of the contract to handle the event
bytes4 handlerFunctionSelector; // Function selector in the handler contract
uint64 priorityFeePerGas; // Extra fee to prioritize handling, in nanoSomi
uint64 maxFeePerGas; // Max fee willing to pay, in nanoSomi
uint64 gasLimit; // Maximum gas that will be provisioned per subscription callback
bool isGuaranteed; // If true, moves to next block if current is full
bool isCoalesced; // If true, multiple events can be coalesced
}
event SubscriptionCreated(uint64 indexed subscriptionId, address indexed owner, SubscriptionData subscriptionData);
event SubscriptionRemoved(uint64 indexed subscriptionId, address indexed owner);
function subscribe(SubscriptionData calldata subscriptionData) external returns (uint256 subscriptionId);
function unsubscribe(uint256 subscriptionId) external;
function getSubscriptionInfo(uint256 subscriptionId) external view returns (SubscriptionData memory subscriptionData, address owner);
}
[PreviousOff-Chain (TypeScript)chevron-left](https://docs.somnia.network/developer/reactivity/tooling/off-chain-typescript)
[NextSubscription managementchevron-right](https://docs.somnia.network/developer/reactivity/tooling/subscription-management)
Last updated 6 days ago
---
# Off-Chain (TypeScript) | Somnia Docs
###
[hashtag](https://docs.somnia.network/developer/reactivity/tooling/off-chain-typescript#overview)
Overview
Tooling is as follows:
* TypeScript SDK - Compatible in NodeJS, Browser, JavaScript and Typescript environments
* React library (future) - Native hooks and react APIs for getting started with subscriptions following React best practives
See the quick start guide for getting started with the SDK
[Quickstartchevron-right](https://docs.somnia.network/developer/reactivity/quickstart)
[PreviousComparisonchevron-left](https://docs.somnia.network/developer/reactivity/tooling/comparison)
[NextOn-chain (Solidity)chevron-right](https://docs.somnia.network/developer/reactivity/tooling/on-chain-solidity)
Last updated 2 months ago
---
# API Reference | Somnia Docs
circle-exclamation
**Somnia Reactivity is currently only available on TESTNET**
###
[hashtag](https://docs.somnia.network/developer/reactivity/api-reference#off-chain-typescript)
Off-chain (TypeScript)
####
[hashtag](https://docs.somnia.network/developer/reactivity/api-reference#websocket-subscription-initialization-params)
WebSocket Subscription Initialization Params
Copy
/**
* @property The notification result data containing information about the event topic, data and view results
*/
export type SubscriptionCallback = {
result: {
topics: Hex[],
data: Hex,
simulationResults: Hex[]
}
}
/**
* @property ethCalls Fixed set of ETH calls that must be executed before onData callback is triggered. Multicall3 is recommended. Can be an empty array
* @property context Event sourced selectors to be added to the data field of ETH calls, possible values: topic0, topic1, topic2, topic3, data and address
* @property onData Callback for a successful reactivity notification
* @property onError Callback for a failed attempt
* @property eventContractSources Alternative contract event source(s) (any on somnia) that will be emitting the logs specified by topicOverrides
* @property topicOverrides Optional filter for specifying topics of interest, otherwise wildcard filter is applied (all events watched)
* @property onlyPushChanges Whether the data should be pushed to the subscriber only if eth_call results are different from the previous
*/
export type WebsocketSubscriptionInitParams = {
ethCalls: EthCall[]
context?: string
onData: (data: SubscriptionCallback) => void
onError?: (error: Error) => void
eventContractSources?: Address[]
topicOverrides?: Hex[]
onlyPushChanges?: boolean
}
An object of type `WebsocketSubscriptionInitParams` is the only required argument to the `subscribe` function required to create a subscription to a Somnia node to become notified about event + state changes that take place on-chain
Example:
####
[hashtag](https://docs.somnia.network/developer/reactivity/api-reference#solidity-subscription-creation-from-sdk)
Solidity Subscription Creation from SDK
Example:
####
[hashtag](https://docs.somnia.network/developer/reactivity/api-reference#solidity-subscription-info-query-from-sdk)
Solidity subscription info query from SDK
Example:
[PreviousFAQs & Troubleshootingchevron-left](https://docs.somnia.network/developer/reactivity/faqs-and-troubleshooting)
[NextSomnia Data Streamschevron-right](https://docs.somnia.network/developer/data-streams)
Last updated 1 day ago
Copy
const subscription = await sdk.subscribe({
ethCalls: [], // State to read when events are emitted
onData: (data: SubscriptionCallback) => console.log('Received:', data),
})
Copy
/**
* @property eventTopics Optional event filters
* @property origin Optional tx.origin filter
* @property caller Optional msg.sender filter
* @property emitter Optional contract event emitter filter
* @property handlerContractAddress Contract that will handle subscription callback
* @property handlerFunctionSelector Optional override for specifying callback handler function
* @property priorityFeePerGas Additional priority fee that will be paid per gas consumed by callback
* @property maxFeePerGas Maximum fee per gas the subscriber is willing to pay (base fee + priority fee)
* @property gasLimit Maximum gas that will be provisioned per subscription callback
* @property isGuaranteed Whether event notification must be delivered regardless of block inclusion distance from emission
* @property isCoalesced Whether multiple events can be coalesced into a single handling call per block
*/
export type SoliditySubscriptionData = {
eventTopics?: Hex[];
origin?: Address;
caller?: Address;
emitter?: Address;
handlerContractAddress: Address;
handlerFunctionSelector?: Hex;
priorityFeePerGas: bigint;
maxFeePerGas: bigint;
gasLimit: bigint;
isGuaranteed: boolean;
isCoalesced: boolean;
}
Copy
await sdk.createSoliditySubscription({
handlerContractAddress: '0x123...',
priorityFeePerGas: 10n,
maxFeePerGas: 20n,
gasLimit: 500_000n,
isGuaranteed: true,
isCoalesced: false,
});
Copy
export type SoliditySubscriptionInfo = {
subscriptionData: SoliditySubscriptionData,
owner: Address
}
Copy
const subscriptionId = 1n;
const subscriptionInfo: SoliditySubscriptionInfo = await sdk.getSubscriptionInfo(
subscriptionId
);
---
# Data Provenance and Verification in Streams | Somnia Docs
When consuming data from any source, especially in a decentralized environment, the most critical question is: **"Can I trust this data?"**
This question is not just about the data's content, but its _origin_. How do you know that data claiming to be from a trusted oracle, a specific device, or another user _actually_ came from them and not from an imposter?
This is the challenge of **Data Provenance**.
In Somnia Data Streams, provenance is not an optional feature or a "best practice". It is a fundamental, cryptographic guarantee built into the core smart contract. This article explains how Streams ensures authenticity via publisher signatures and how you can verify data origin.
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/data-provenance-and-verification-in-streams#the-cryptographic-guarantee-msg.sender-as-provenance)
The Cryptographic Guarantee: `msg.sender` as Provenance
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
The trust layer of Somnia Streams is elegantly simple. It does not rely on complex off-chain signature checking or data fields like `senderName`. Instead, it leverages the most basic and secure primitive of the EVM: `msg.sender`.
All data published to Streams is stored in the core `Streams` smart contract. The data storage mapping has a specific structure:
####
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/data-provenance-and-verification-in-streams#conceptual-contract-storage)
**Conceptual Contract Storage**
Copy
// mapping: schemaId => publisherAddress => dataId => data
mapping(bytes32 => mapping(address => mapping(bytes32 => bytes))) public dsstore;
When a publisher calls `sdk.streams.set(...)` or `sdk.streams.setAndEmitEvents(...)`, their wallet signs a transaction. The `Streams` smart contract receives this transaction and identifies the signer's address via the `msg.sender` variable.
The contract then stores the data _at the_ `_msg.sender_`_'s address_ within the schema's mapping.
**This is the cryptographic guarantee.**
It is **impossible** for `0xPublisher_A` to send a transaction that writes data into the slot for `0xPublisher_B`. They cannot fake their `msg.sender`. The data is automatically and immutably tied to the address of the account that paid the gas to publish it.
* An attacker **cannot** write data as if it came from a trusted oracle.
* A user **cannot** send a chat message pretending to be another user.
* Data integrity is linked directly to wallet security.
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/data-provenance-and-verification-in-streams#verification-is-implicit-in-the-read-operation)
Verification Is Implicit in the Read Operation
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Because the `publisher` address is a fundamental key in the storage mapping, you don't need to perform complex "verification" steps. **Verification is implicit in the read operation.**
When you use the SDK to read data, you must specify which publisher you are interested in:
* `sdk.streams.getByKey(schemaId, publisher, key)`
* `sdk.streams.getAllPublisherDataForSchema(schemaId, publisher)`
When you call `getAllPublisherDataForSchema(schemaId, '0xTRUSTED_ORACLE_ADDRESS')`, you are not _filtering_ data. You are asking the smart contract to retrieve data from the specific storage slot that _only_ `0xTRUSTED_ORACLE_ADDRESS` could have written to.
If an imposter (`0xIMPOSTER_ADDRESS`) publishes data using the same `schemaId`, their data is stored in a completely different location (`dsstore[schemaId]['0xIMPOSTER_ADDRESS']`). It will never be returned when you query for the trusted address.
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/data-provenance-and-verification-in-streams#deliverable-building-a-verification-script)
Deliverable: Building a Verification Script
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Let's build a utility to prove this concept.
**Scenario:** We have a shared `oraclePrice` schema. Two different, trusted oracles (`0xOracle_A` and `0xOracle_B`) publish prices to it. We will build a script that verifies the origin of data and proves that an `imposter` cannot pollute their feeds.
###
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/data-provenance-and-verification-in-streams#project-setup)
**Project Setup**
We will use the same project setup as the "[Multi-Publisher Aggregatorarrow-up-right](https://emre-gitbook.gitbook.io/emre-gitbook-docs/data-streams/working-with-multiple-publishers-in-a-shared-stream#tutorial-building-a-multi-publisher-aggregator-app)
" tutorial. You will need a `.env` file with at least one private key to act as a publisher, and we will simulate the other addresses.
[`**src/lib/clients.ts**`arrow-up-right](https://emre-gitbook.gitbook.io/emre-gitbook-docs/data-streams/working-with-multiple-publishers-in-a-shared-stream#chain-and-client-configuration)
(No changes needed from the previous tutorial. We just need `publicClient`.)
`**src/lib/schema.ts**`
###
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/data-provenance-and-verification-in-streams#the-verification-script)
**The Verification Script**
This script will not publish data. We will assume our two trusted oracles (`PUBLISHER_1_PK` and `PUBLISHER_2_PK` from the previous tutorial) have already published data using the `oraclePriceSchema`.
Our script will:
1. Define a list of `TRUSTED_ORACLES`.
2. Define an `IMPOSTER_ORACLE` (a random address that has _not_ published).
3. Create a `verifyPublisher` function that fetches data _only_ for a specific publisher address.
4. Run verification for all addresses and show that data is only returned for the correct publishers.
`**src/scripts/verifyOrigin.ts**`
###
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/data-provenance-and-verification-in-streams#expected-output)
**Expected Output**
To run this, first publish some data (using the script from the previous tutorial, but adapted for `oraclePriceSchema`) from both `PUBLISHER_1_PK` and `PUBLISHER_2_PK`. Then, run the verification script.
You will see an output similar to this:
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/data-provenance-and-verification-in-streams#conclusion-key-takeaways)
Conclusion: Key Takeaways
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
* **Provenance is Built-In:** Data provenance in Somnia Streams is not an optional feature; it is a core cryptographic guarantee of the `Streams` smart contract, enforced by `msg.sender`.
* **Verification is Implicit:** You verify data origin every time you perform a read operation with `getAllPublisherDataForSchema` or `getByKey`. The `publisher` address acts as the ultimate verification key.
* **Trust Layer:** This architecture creates a robust trust layer. Your application logic can be certain that any data returned for a specific publisher was, without question, signed and submitted by that publisher's wallet.
[PreviousIntersection with Somnia Reactivitychevron-left](https://docs.somnia.network/developer/data-streams/concepts/intersection-with-somnia-reactivity)
[NextSDK Methods Guidechevron-right](https://docs.somnia.network/developer/data-streams/sdk-methods-guide)
Last updated 1 month ago
* [The Cryptographic Guarantee: msg.sender as Provenance](https://docs.somnia.network/developer/data-streams/concepts/data-provenance-and-verification-in-streams#the-cryptographic-guarantee-msg.sender-as-provenance)
* [Verification Is Implicit in the Read Operation](https://docs.somnia.network/developer/data-streams/concepts/data-provenance-and-verification-in-streams#verification-is-implicit-in-the-read-operation)
* [Deliverable: Building a Verification Script](https://docs.somnia.network/developer/data-streams/concepts/data-provenance-and-verification-in-streams#deliverable-building-a-verification-script)
* [Project Setup](https://docs.somnia.network/developer/data-streams/concepts/data-provenance-and-verification-in-streams#project-setup)
* [The Verification Script](https://docs.somnia.network/developer/data-streams/concepts/data-provenance-and-verification-in-streams#the-verification-script)
* [Expected Output](https://docs.somnia.network/developer/data-streams/concepts/data-provenance-and-verification-in-streams#expected-output)
* [Conclusion: Key Takeaways](https://docs.somnia.network/developer/data-streams/concepts/data-provenance-and-verification-in-streams#conclusion-key-takeaways)
Copy
export const oraclePriceSchema = 'uint256 price, uint64 timestamp'
Copy
import 'dotenv/config'
import { SDK, SchemaDecodedItem } from '@somnia-chain/streams'
import { publicClient } from '../lib/clients' // Assuming you have clients.ts from previous tutorial
import { oraclePriceSchema } from '../lib/schema'
import { Address, createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
// --- Setup: Define our trusted and untrusted addresses ---
function getEnv(key: string): string {
const value = process.env[key]
if (!value) throw new Error(`Missing environment variable: ${key}`)
return value
}
// These are the addresses we trust for this schema.
// We get them from our .env file for this example.
const TRUSTED_ORACLES: Address[] = [\
privateKeyToAccount(getEnv('PUBLISHER_1_PK') as `0x${string}`).address,\
privateKeyToAccount(getEnv('PUBLISHER_2_PK') as `0x${string}`).address,\
]
// This is a random, untrusted address.
const IMPOSTER_ORACLE: Address = '0x1234567890123456789012345678901234567890'
// --- Helper Functions ---
// Helper to decode the oracle data
function decodePriceRecord(row: SchemaDecodedItem[]): { price: bigint, timestamp: number } {
const val = (field: any) => field?.value?.value ?? field?.value ?? ''
return {
price: BigInt(val(row[0])),
timestamp: Number(val(r[1])),
}
}
/**
* Verification Utility
* Fetches data for a *single* publisher to verify its origin.
*/
async function verifyPublisher(sdk: SDK, schemaId: `0x${string}`, publisherAddress: Address) {
console.log(`\n--- Verifying Publisher: ${publisherAddress} ---`)
try {
const data = await sdk.streams.getAllPublisherDataForSchema(schemaId, publisherAddress)
if (!data || data.length === 0) {
console.log('[VERIFIED] No data found for this publisher.')
return
}
const records = (data as SchemaDecodedItem[][]).map(decodePriceRecord)
console.log(`[VERIFIED] Found ${records.length} record(s) cryptographically signed by this publisher:`)
records.forEach(record => {
console.log(` - Price: ${record.price}, Time: ${new Date(record.timestamp).toISOString()}`)
})
} catch (error: any) {
console.error(`Error during verification: ${error.message}`)
}
}
// --- Main Execution ---
async function main() {
const sdk = new SDK({ public: publicClient })
const schemaId = await sdk.streams.computeSchemaId(oraclePriceSchema)
if (!schemaId) throw new Error('Could not compute schemaId')
console.log('Starting Data Provenance Verification...')
console.log(`Schema: oraclePriceSchema (${schemaId})`)
// 1. Verify our trusted oracles
for (const oracleAddress of TRUSTED_ORACLES) {
await verifyPublisher(sdk, schemaId, oracleAddress)
}
// 2. Verify the imposter
// This will securely return NO data, even if the imposter
// published data to the same schemaId under their *own* address.
await verifyPublisher(sdk, schemaId, IMPOSTER_ORACLE)
}
main().catch((e) => {
console.error(e)
process.exit(1)
})
Copy
# Add to package.json
"verify": "ts-node src/scripts/verifyOrigin.ts"
# Run it
npm run verify
Copy
Starting Data Provenance Verification...
Schema: oraclePriceSchema (0x...)
--- Verifying Publisher: 0xPublisher1Address... ---
[VERIFIED] Found 2 record(s) cryptographically signed by this publisher:
- Price: 3200, Time: 2025-10-31T12:30:00.000Z
- Price: 3201, Time: 2025-10-31T12:31:00.000Z
--- Verifying Publisher: 0xPublisher2Address... ---
[VERIFIED] Found 1 record(s) cryptographically signed by this publisher:
- Price: 3199, Time: 2025-10-31T12:30:30.000Z
--- Verifying Publisher: 0x1234567890123456789012345678901234567890 ---
[VERIFIED] No data found for this publisher.
---
# Tutorials | Somnia Docs
circle-exclamation
**Somnia Reactivity is currently only available on TESTNET**
[PreviousSubscription managementchevron-left](https://docs.somnia.network/developer/reactivity/tooling/subscription-management)
[NextWildcard Off-Chain Reactivity Tutorialchevron-right](https://docs.somnia.network/developer/reactivity/tutorials/wildcard-off-chain-reactivity-tutorial)
Last updated 1 month ago
---
# Development Frameworks | Somnia Docs
Developing a robust decentralized application (dApp) requires a complex stack of technologies. Software frameworks streamline this process by bundling essential features or offering flexible plugin architectures to customize your toolkit. These frameworks provide immediate, out-of-the-box utility, including:
* Local Environments: Tools to instantly launch a local blockchain instance.
* Development Suite: Utilities for compiling and testing smart contracts.
* Integrated Frontend: Add-ons that allow for client-side development within the same repository.
* Deployment Management: Configurations for connecting to networks (local or public) and deploying contracts.
[PreviousSmart Contractschevron-left](https://docs.somnia.network/developer/smart-contracts)
[NextLocal Testing and Forkingchevron-right](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking)
Last updated 1 month ago
---
# Intersection with Somnia Reactivity | Somnia Docs
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/intersection-with-somnia-reactivity#reactivity-background)
Reactivity background
-----------------------------------------------------------------------------------------------------------------------------------------------------------
For detailed information about reactivity please visit the Reactivity docs:
[](https://docs.somnia.network/developer/reactivity#welcome-to-the-somnia-reactivity-docs)
Reactivity
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/intersection-with-somnia-reactivity#writing-data-events-and-reacting)
Writing data, events and reacting
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
When an ERC20 transfer takes place, balance state is updated and an event is emitted. The ERC20 transfer scenario is very common in smart contracts i.e. publishing state and emitting an event (also known as a log). Somnia Data Streams offers you the tooling to do this without the requirements of having to write your own custom Solidity contract. It also allows you to take advantage of existing schemas for publishing data yielding composibility benefits for applications. Example:
Copy
import { SDK } from "@somnia-chain/streams"
import { zeroAddress, erc721Abi } from "viem"
// Use WebSocket transport in the public client for subscription tasks
// For the SDK instance that executes transactions, stick with htttp
const sdk = new SDK({
public: getPublicClient(),
wallet: getWalletClient(),
})
// Encode view function calls to be executed when an event takes place
const ethCalls = [{\
to: "0x23B66B772AE29708a884cca2f9dec0e0c278bA2c",\
data: encodeFunctionData({\
abi: erc721Abi,\
functionName: "balanceOf",\
args: ["0x3dC360e0389683cA0341a11Fc3bC26252b5AF9bA"]\
})\
}]
// Start a subsciption
const subscription = await sdk.streams.subscribe({
ethCalls,
onData: (data) => {
const decodedLog = decodeEventLog({
abi: fireworkABI,
topics: data.result.topics,
data: data.result.data,
});
const decodedFunctionResult = decodeFunctionResult({
abi: erc721Abi,
functionName: 'balanceOf',
data: data.result.simulationResults[0],
});
console.log("Decoded event", decodedLog);
console.log("Decoded function call result", decodedFunctionResult);
}
})
// Write data and emit events that will trigger the above callback!
const dataStreams = [{\
id,\
schemaId: driverSchemaId,\
data: encodedData\
}]
const eventStreams = [{\
id: somniaStreamsEventId,\
argumentTopics,\
data\
}]
const setAndEmitEventsTxHash = await sdk.streams.setAndEmitEvents(
dataStreams,
eventStreams
)
Writing data and emitting events will trigger a call back to subscribers that care about a specified event emitted from the Somnia Data Streams protocol (or any contract for that matter) without having the need to poll the chain. It follows the observer pattern meaning push rather than pull which is always a more efficient paradigm.
[PreviousSomnia Data vs Event Streamschevron-left](https://docs.somnia.network/developer/data-streams/concepts/somnia-data-vs-event-streams)
[NextData Provenance and Verification in Streamschevron-right](https://docs.somnia.network/developer/data-streams/concepts/data-provenance-and-verification-in-streams)
Last updated 1 month ago
* [Reactivity background](https://docs.somnia.network/developer/data-streams/concepts/intersection-with-somnia-reactivity#reactivity-background)
* [Writing data, events and reacting](https://docs.somnia.network/developer/data-streams/concepts/intersection-with-somnia-reactivity#writing-data-events-and-reacting)
---
# Deploy with Thirdweb | Somnia Docs
[Thirdwebarrow-up-right](https://thirdweb.com/)
is a complete web3 development framework that offers everything you need to connect your apps or games to the Somnia network. Its service allows developers to build, manage, and analyze their Web3 applications.
This tutorial will guide you through deploying a Smart contract to the Somnia Devnet using Thirdweb’s command-line tool (\`thirdweb deploy\`). Thirdweb simplifies deployment and interaction with smart contracts on Somnia.
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-thirdweb#prerequisites)
Prerequisites
-----------------------------------------------------------------------------------------------------------------------------
* This guide is not an introduction to Solidity Programming; you are expected to understand Basic Solidity Programming.
* To complete this guide, you will need MetaMask installed and the Somnia DevNet added to the list of Networks. If you have yet to install MetaMask, please follow this guide to Connect Your Wallet.
* Thirdweb CLI: Install globally with:
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-thirdweb#set-up-the-project)
Set Up the Project
---------------------------------------------------------------------------------------------------------------------------------------
First, create a new folder for your project and initialize it.
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-thirdweb#write-the-smart-contract)
Write the Smart Contract
---------------------------------------------------------------------------------------------------------------------------------------------------
You can write your Smart Contract using the [Remix IDE](https://docs.somnia.network/developer/development-frameworks/deploy-with-remixide)
to ensure it works. Create a file `**OpenGreeter.sol**` and add the following code:
chevron-rightOpenGreeter.sol[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-thirdweb#opengreeter.sol)
This is a simple Greeter Smart Contract that any address can call the `**changeName**` function. The Smart Contract has two functions: `**changeName**` - This function allows anyone to change the name variable. It stores the old name, updates the name variable, and emits the `**NameChanged**` event with the old and new names.
`**greet**` - This function returns a greeting message that includes the current name. It uses abi.encodePacked to concatenate strings efficiently.
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-thirdweb#deploy-the-smart-contract-using-thirdweb)
Deploy the Smart Contract using Thirdweb.
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
First, go to Thirdweb and create a profile. After you have completed the onboarding process, create a Project.

Go to the project **settings.**

**C**opy your secret key, and keep it safe. The secret key will be used to deploy the Smart Contract.

Go to the terminal and paste the following command to deploy the Smart Contract:
Select the `**solc**` option to be `**true**` in the prompts on Terminal.

Click the link to open the User Interface in your Browser to deploy the Smart Contract.

Enter an initialName. Select the Network as Somnia Devnet. Check the option to import it to the list of Contracts in your Thirdweb Dashboard. Click on `**Deploy Now**` and approve the Metamask prompts.


Your Smart Contract is deployed, and you can view it on your Thirdweb Dashboard.

Visit the Explorer section to simulate interactions with your deployed Smart Contract and carry out actual transactions.

Congratulations. 🎉 You have deployed your Smart Contract to the Somnia Network using Thirdweb. 🎉
[PreviousDeploy with RemixIDEchevron-left](https://docs.somnia.network/developer/development-frameworks/deploy-with-remixide)
[NextDeploy with Foundrychevron-right](https://docs.somnia.network/developer/development-frameworks/deploy-with-foundry)
Last updated 1 month ago
* [Prerequisites](https://docs.somnia.network/developer/development-frameworks/deploy-with-thirdweb#prerequisites)
* [Set Up the Project](https://docs.somnia.network/developer/development-frameworks/deploy-with-thirdweb#set-up-the-project)
* [Write the Smart Contract](https://docs.somnia.network/developer/development-frameworks/deploy-with-thirdweb#write-the-smart-contract)
* [Deploy the Smart Contract using Thirdweb.](https://docs.somnia.network/developer/development-frameworks/deploy-with-thirdweb#deploy-the-smart-contract-using-thirdweb)
Copy
npm thirdweb install
Copy
mkdir somnia-thirdweb-example
cd somnia-thirdweb-example
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract OpenGreeter {
string public name;
address public owner;
event NameChanged(string oldName, string newName);
constructor(string memory _initialName) {
name = _initialName;
owner = msg.sender;
}
function changeName(string memory _newName) public {
string memory oldName = name;
name = _newName;
emit NameChanged(oldName, _newName);
}
function greet() external view returns (string memory) {
return string(abi.encodePacked("Hello, ", name, "!"));
}
}
Copy
npx thirdweb deploy -k your_secret_key
---
# Off-Chain Reactivity: Filtered Subscriptions tutorial | Somnia Docs
This tutorial builds on basic off-chain reactivity by adding filters to your WebSocket subscriptions. While wildcard (all events) is great for quick testing and seeing reactivity in action, it's often too verbose for production—flooding logs with irrelevant data. Instead, use filters to target specific emitters or events, making your app more efficient and focused.
We'll subscribe to Transfer events from a specific ERC20 contract, include a view call (e.g., balanceOf), and enable `onlyPushChanges` to notify only on state changes. This is ideal for real-time UIs or monitoring without noise.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/off-chain-reactivity-filtered-subscriptions-tutorial#overview)
Overview
Off-chain subscriptions push filtered events + state via WebSockets to TypeScript apps. Filters reduce volume:
* **eventContractSources**: Limit to specific emitter addresses.
* **topicOverrides**: Filter by event topics (e.g., keccak256 signatures like Transfer's `0xddf...`).
* **onlyPushChanges**: Skip notifications if ethCalls results match the previous one.
* **ethCalls**: Optional view calls for bundled state.
* **onError**: Handle failures gracefully.
No gas costs; runs off-chain.
Prerequisites:
* Same as wildcard tutorial: Node.js, `npm i @somnia-chain/reactivity viem`.
* Know your target contract/event (e.g., ERC20 Transfer).
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/off-chain-reactivity-filtered-subscriptions-tutorial#key-objectives)
Key Objectives
1. **Set Up Chain and SDK**: Configure viem and initialize SDK.
2. **Define Filters and ETH Calls**: Specify emitters, topics, and views.
3. **Create Filtered Subscription**: Use params for targeted listening.
4. **Decode and Handle Data**: Parse with viem; add error handling.
5. **Run and Optimize**: Test with `onlyPushChanges`.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/off-chain-reactivity-filtered-subscriptions-tutorial#step-1-install-dependencies)
Step 1: Install Dependencies
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/off-chain-reactivity-filtered-subscriptions-tutorial#step-2-define-the-somnia-chain)
Step 2: Define the Somnia Chain
(Reuse from wildcard tutorial.)
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/off-chain-reactivity-filtered-subscriptions-tutorial#step-3-initialize-the-sdk)
Step 3: Initialize the SDK
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/off-chain-reactivity-filtered-subscriptions-tutorial#step-4-define-eth-calls-and-filters)
Step 4: Define ETH Calls and Filters
* **ethCalls**: Query balanceOf on an ERC20.
* **eventContractSources**: Array of emitter addresses (e.g., one ERC20 contract).
* **topicOverrides**: Hex for event signatures (e.g., Transfer: `0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef`).
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/off-chain-reactivity-filtered-subscriptions-tutorial#step-5-create-the-filtered-subscription)
Step 5: Create the Filtered Subscription
Include `onlyPushChanges: true` to notify only on balance changes. Add `onError` for robustness.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/off-chain-reactivity-filtered-subscriptions-tutorial#step-6-decode-data-in-the-callback)
Step 6: Decode Data in the Callback
Parse the event (Transfer) and view result (balanceOf).
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/off-chain-reactivity-filtered-subscriptions-tutorial#step-7-put-it-all-together-and-run)
Step 7: Put It All Together and Run
Full script (`main.ts`):
Run: `ts-node main.ts`.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/off-chain-reactivity-filtered-subscriptions-tutorial#testing)
Testing
1. Run the script.
2. Trigger a Transfer on the filtered contract (e.g., send tokens).
3. See notifications only on relevant events/changes.
* If too quiet: Remove `onlyPushChanges` or broaden filters.
* Prod Tip: Start with wildcard for debugging, then add filters.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/off-chain-reactivity-filtered-subscriptions-tutorial#troubleshooting)
Troubleshooting
* **No Notifications?** Verify topics/address (use explorers for keccak). Check WS connection.
* **Errors?** Handle in onError; common: Invalid filters or RPC issues.
* **Too Many?** Tighten topicOverrides or eventContractSources.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/off-chain-reactivity-filtered-subscriptions-tutorial#next-steps)
Next Steps
* Multi-emitters: Add more to eventContractSources.
* Custom Events: Compute topics for your ABI.
* Advanced: Combine with React for live UIs.
* Compare to On-Chain: On-Chain Tutorial.
[PreviousWildcard Off-Chain Reactivity Tutorialchevron-left](https://docs.somnia.network/developer/reactivity/tutorials/wildcard-off-chain-reactivity-tutorial)
[NextSolidity on-chain Reactivity Tutorialchevron-right](https://docs.somnia.network/developer/reactivity/tutorials/solidity-on-chain-reactivity-tutorial)
Last updated 1 month ago
Copy
npm i @somnia-chain/reactivity viem
Copy
import { defineChain } from 'viem';
const somniaTestnet = defineChain({
id: 50312,
name: 'Somnia Testnet',
// ... full config as before
});
Copy
import { SDK } from '@somnia-chain/reactivity';
import { createPublicClient, webSocket } from 'viem';
const publicClient = createPublicClient({
chain: somniaTestnet,
transport: webSocket(),
});
const sdk = new SDK({ public: publicClient });
Copy
import { encodeFunctionData, erc20Abi, keccak256, toHex } from 'viem';
// Example: Transfer topic (keccak256('Transfer(address,address,uint256)'))
const transferTopic = keccak256(toHex('Transfer(address,address,uint256)'));
const ethCall = {
to: '0xExampleERC20Address', // Your target ERC20
data: encodeFunctionData({
abi: erc20Abi,
functionName: 'balanceOf',
args: ['0xYourWalletAddress'], // Monitor this balance
}),
};
const filters = {
eventContractSources: ['0xExampleERC20Address'], // Filter to this emitter
topicOverrides: [transferTopic], // Only Transfer events
};
Copy
const subscription = await sdk.subscribe({
ethCalls: [ethCall], // Bundled state query
...filters, // From Step 4
onlyPushChanges: true, // Efficient: Skip if balance unchanged
onData: (data) => {
console.log('Filtered Notification:', data);
// Decoding here (Step 6)
},
onError: (error) => {
console.error('Subscription Error:', error.message);
// Retry logic or alert
},
});
// Unsubscribe: subscription.unsubscribe();
Copy
import { decodeEventLog, decodeFunctionResult } from 'viem';
// Inside onData:
const decodedLog = decodeEventLog({
abi: erc20Abi,
topics: data.result.topics,
data: data.result.data,
});
const decodedBalance = decodeFunctionResult({
abi: erc20Abi,
functionName: 'balanceOf',
data: data.result.simulationResults[0],
});
console.log('Decoded Transfer:', decodedLog.args); // { from, to, value }
console.log('New Balance:', decodedBalance);
Copy
// Imports...
async function main() {
// Chain, client, SDK from Steps 2-3...
// ethCall and filters from Step 4...
const subscription = await sdk.subscribe({
ethCalls: [ethCall],
...filters,
onlyPushChanges: true,
onData: (data) => {
// Decoding from Step 6...
},
onError: (error) => console.error(error),
});
// Run indefinitely or unsubscribe on signal
}
main().catch(console.error);
---
# Subscription management | Somnia Docs
circle-exclamation
**Reactivity is currently only available on TESTNET**
Manage your reactivity subscriptions efficiently using the SDK, or do the same via Solidity by directly accessing the Somnia Reactivity Precompile. This covers creation, listing, querying, and cancellation for both off-chain (WebSocket) and on-chain (Solidity) types. Off-chain subscriptions are local to your app; on-chain are chain-managed and require funding (min 32 STT).
####
[hashtag](https://docs.somnia.network/developer/reactivity/tooling/subscription-management#off-chain-websocket-subscriptions)
Off-Chain (WebSocket) Subscriptions
Off-chain subs use WebSockets for push notifications. Management is app-side—no chain queries needed.
**Creating a Subscription**
Use `sdk.subscribe()` to start listening. Returns an object with `unsubscribe()`.
Copy
import { SDK, SubscriptionCallback } from '@somnia-chain/reactivity';
const subscription = await sdk.subscribe({
ethCalls: [], // Optional: ETH view calls
onData: (data: SubscriptionCallback) => {
console.log('Event:', data);
},
// Other filters: eventTopics, origin, etc.
});
// Store subscription for later management
**Unsubscribing**
Call the returned method to stop.
**Tips**
* Track subs in your app state (e.g., array of subscription objects).
* No listing/querying via SDK—handle locally as they're not persisted on-chain.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tooling/subscription-management#on-chain-solidity-subscriptions)
On-Chain (Solidity) Subscriptions
On-chain subs invoke handlers via EVM. Managed by the chain; owner must fund.
**Subscription Data Structure**
**Creating a Subscription**
Returns tx hash on success.
**Getting Subscription Info**
Fetch details by ID.
**Cancelling a Subscription**
Returns txn hash on success. Only owner can cancel.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tooling/subscription-management#best-practices)
Best Practices
* **Funding**: Ensure owner has 32+ STT; subs pause if low.
* **Error Handling**: Always check for Error instances.
* **Monitoring**: For on-chain, periodically list and query to monitor status.
* **Security**: Use private keys securely; avoid over-provisioning gas.
For full SDK reference, see API Reference.
###
[hashtag](https://docs.somnia.network/developer/reactivity/tooling/subscription-management#solidity-subscription-management)
Solidity Subscription management
####
[hashtag](https://docs.somnia.network/developer/reactivity/tooling/subscription-management#creating-subscriptions)
Creating Subscriptions
Whoever calls the `subscribe` function becomes the owner of the subscription. The owner can be EOA or a smart contract. In either case, the owner is required to hold a miniumum amount of STT and is responsible for paying the gas fees associated with handling events.
The `SubscriptionData` struct defines the criteria for the event subscription and how it should be handled:
* **eventTopics**: An array of 4 bytes32 values representing the event topics to filter by. Use `bytes32(0)` for wildcards.
* **origin**: Filters by the transaction origin (`tx.origin`). Use `address(0)` for any origin.
* **caller**: Filters by the message sender (`msg.sender`). Use `address(0)` for any caller.
* **emitter**: The address of the contract emitting the event. Use `address(0)` for any emitter.
* **handlerContractAddress**: The address of the contract that will be called when a matching event occurs.
* **handlerFunctionSelector**: The 4-byte function selector of the method to call on the handler contract.
* **priorityFeePerGas**: Additional gas fee paid to validators to prioritize this event handling. This is expressed in nanoSTT (gwei equivalent).
* **maxFeePerGas**: The maximum total gas fee (base + priority) the subscriber is willing to pay. This is expressed in nanoSTT (gwei equivalent).
* **gasLimit**: The maximum gas that will be provisioned per subscription callback
* **isGuaranteed**: If `true`, the event handling is guaranteed to execute, potentially moving to the next block if the current block is full.
* **isCoalesced**: If `true`, multiple matching events in the same block can be coalesced into a single handler call (implementation dependent).
A subscription can be handled by any smart contract (no special op codes). Additionally, the optional function selector can be used, that is prefixed to the event data when calling the handler contract.
**Handling Events**
When an event matching the subscription criteria is emitted, the Somnia Reactivity Precompile will invoke the specified handler contract and function. The handler contract can implement a function that matches the `handlerFunctionSelector` specified in the subscription. This function will be called with the event data when a matching event occurs. The owner of the subscription is charged the gas fees specified in the subscription for each event handled. The two fields indicate that the call the to handler has been initialized by the precompile:
* **msg.sender**: The Somnia Reactivity Precompile address (`0x0100`).
* **tx.origin**: The owner of the subscription.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tooling/subscription-management#examples)
Examples
**1\. Fully On-Chain Subscription**
You can create subscriptions directly from another smart contract. This is useful for creating autonomous agents or protocols that react to network activity.
**Key Snippet:**
[PreviousOn-chain (Solidity)chevron-left](https://docs.somnia.network/developer/reactivity/tooling/on-chain-solidity)
[NextTutorialschevron-right](https://docs.somnia.network/developer/reactivity/tutorials)
Last updated 6 days ago
Copy
subscription.unsubscribe();
Copy
export type SoliditySubscriptionData = {
eventTopics?: Hex[]; // Optional filters
origin?: Address;
caller?: Address;
emitter?: Address;
handlerContractAddress: Address; // Required
handlerFunctionSelector?: Hex; // Optional override
priorityFeePerGas: bigint;
maxFeePerGas: bigint;
gasLimit: bigint;
isGuaranteed: boolean;
isCoalesced: boolean;
};
export type SoliditySubscriptionInfo = {
subscriptionData: SoliditySubscriptionData,
owner: Address
};
Copy
const subData: SoliditySubscriptionData = {
handlerContractAddress: '0x123...',
priorityFeePerGas: parseGwei('2'),
maxFeePerGas: parseGwei('10'),
gasLimit: 500_000n,
isGuaranteed: true,
isCoalesced: false,
// Add filters as needed
};
const txHash = await sdk.createSoliditySubscription(subData);
if (txHash instanceof Error) {
console.error(txHash.message);
} else {
console.log('Created:', txHash);
}
Copy
const info = await sdk.getSubscriptionInfo(123n); // bigint ID
if (info instanceof Error) {
console.error(info.message);
} else {
console.log('Info:', info);
}
Copy
const txHash = await sdk.cancelSoliditySubscription(123n);
if (txHash instanceof Error) {
console.error(txHash.message);
} else {
console.log('Canceled:', txHash);
}
Copy
ISomniaReactivityPrecompile.SubscriptionData memory subscriptionData = ISomniaReactivityPrecompile.SubscriptionData({
eventTopics: [Transfer.selector, bytes32(0), bytes32(0), bytes32(0)],
origin: address(0),
caller: address(0),
emitter: address(tokenAddress),
handlerContractAddress: address(this),
handlerFunctionSelector: ISomniaEventHandler.onEvent.selector,
priorityFeePerGas: parseGwei('2'),
maxFeePerGas: parseGwei('10'),
isGuaranteed: true,
isCoalesced: false
});
uint256 subscriptionId = somniaReactivityPrecompile.subscribe(subscriptionData);
---
# Deploy with RemixIDE | Somnia Docs
The Somnia mission is to enable the building of mass-consumer real-time applications. As a Developer, you must understand the quick steps to deploy your first Smart Contract on the Somnia Network. This guide will teach you how to connect to and deploy your first Smart Contract to the Somia Network using the Remix IDE.
circle-check
Somnia Mainnet is LIVE. To deploy on Somnia Mainnet, you will need SOMI Tokens. Please refer to the [guide](https://docs.somnia.network/get-started/getting-started-for-mainnet)
on Moving from Testnet to Mainnet.
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-remixide#pre-requisites)
Pre-requisites:
--------------------------------------------------------------------------------------------------------------------------------
1. This guide is not an introduction to Solidity Programming; you are expected to understand Basic Solidity Programming.
2. To complete this guide, you will need MetaMask installed and the Somnia Network added to the list of Networks. If you have yet to install MetaMask, please follow this guide to [Connect Your Wallet](https://docs.somnia.network/get-started/connect-your-wallet-to-mainnet)
.
[Remixarrow-up-right](https://remix.ethereum.org/)
is an IDE for Smart Contract development, which includes compilation, deployment, testing, and debugging. It makes it easy for developers to create, debug, and deploy Smart Contracts to the Somnia Network. In this example, we will deploy a Greeter Smart contract, where we can update the state of the Contract to say “Hello” + Name.
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-remixide#connect-to-somnia-testnet)
Connect to Somnia Testnet
-----------------------------------------------------------------------------------------------------------------------------------------------------
Ensure you are logged into your MetaMask, connected to the Somnia Testnet, and have some STT Tokens. [Get STT Tokens](https://docs.somnia.network/get-started/testnet-stt-tokens)
from the faucet.
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-remixide#create-the-smart-contract)
Create the Smart Contract
-----------------------------------------------------------------------------------------------------------------------------------------------------
Go to the Remix IDE and create a new file. Paste the Smart Contract below:
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-remixide#compile-the-smart-contract)
Compile the Smart Contract
-------------------------------------------------------------------------------------------------------------------------------------------------------
On the left tab, click the “Solidity Compiler” menu item and then the “ Compile Greeter.sol” button. This will compile the Solidity file and convert the Solidity code into machine-readable bytecode.

[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-remixide#deploy-the-smart-contract)
Deploy the Smart Contract
-----------------------------------------------------------------------------------------------------------------------------------------------------
The Smart Contract has been created and compiled into ByteCode, and the ABI has also been created. The next step is to deploy the Smart Contract to the Somnia DevNet so that you can perform READ and WRITE operations.
On the left tab, click the “Deploy and run transactions” menu item. To deploy the Smart Contract, we will require a wallet connection. In the Environment dropdown, select the option: “Injected Provider - MetaMask”. Then select the MetaMask account where you have STT Tokens.
In the “DEPLOY” field, enter a value for the “\_INITIALNAME” variable, and click deploy.
When prompted, approve the Contract deployment on your MetaMask.

Look at the terminal for the response and the deployed Smart Contract address. You can interact with the Smart Contract via the Remix IDE. Send a transaction to change the name.

Congratulations. 🎉 You have deployed your first Smart Contract to the Somnia Network. 🎉
[PreviousLocal Testing and Forkingchevron-left](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking)
[NextDeploy with Thirdwebchevron-right](https://docs.somnia.network/developer/development-frameworks/deploy-with-thirdweb)
Last updated 1 month ago
* [Pre-requisites:](https://docs.somnia.network/developer/development-frameworks/deploy-with-remixide#pre-requisites)
* [Connect to Somnia Testnet](https://docs.somnia.network/developer/development-frameworks/deploy-with-remixide#connect-to-somnia-testnet)
* [Create the Smart Contract](https://docs.somnia.network/developer/development-frameworks/deploy-with-remixide#create-the-smart-contract)
* [Compile the Smart Contract](https://docs.somnia.network/developer/development-frameworks/deploy-with-remixide#compile-the-smart-contract)
* [Deploy the Smart Contract](https://docs.somnia.network/developer/development-frameworks/deploy-with-remixide#deploy-the-smart-contract)
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
contract Greeter {
string public name;
address public owner;
event NameChanged(string oldName, string newName);
modifier onlyOwner() {
require(msg.sender == owner, "Only the owner can perform this action");
_;
}
constructor(string memory _initialName) {
name = _initialName;
owner = msg.sender;
}
function changeName(string memory _newName) external onlyOwner {
string memory oldName = name;
name = _newName;
emit NameChanged(oldName, _newName);
}
function greet() external view returns (string memory) {
return string(abi.encodePacked("Hello, ", name, "!"));
}
}
---
# Security | Somnia Docs
[Smart Contract Security 101chevron-right](https://docs.somnia.network/developer/security/smart-contract-security-101)
[Audit Checklistchevron-right](https://docs.somnia.network/developer/security/audit-checklist)
[Node/Infra Securitychevron-right](https://docs.somnia.network/developer/security/node-infra-security)
[Responsible Disclosure Policychevron-right](https://docs.somnia.network/developer/security/responsible-disclosure-policy)
[PreviousBuilding a Simple DEX on Somniachevron-left](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia)
[NextSmart Contract Security 101chevron-right](https://docs.somnia.network/developer/security/smart-contract-security-101)
---
# Deployment and Production | Somnia Docs
[Go-Live Checklistchevron-right](https://docs.somnia.network/developer/deployment-and-production/go-live-checklist)
[Explorer API Health and Monitoringchevron-right](https://docs.somnia.network/developer/deployment-and-production/explorer-api-health-and-monitoring)
[Somnia Gas Differences To Ethereumchevron-right](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum)
[Ecosystemchevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem)
[Support and Communitychevron-right](https://docs.somnia.network/developer/deployment-and-production/support-and-community)
[PreviousResponsible Disclosure Policychevron-left](https://docs.somnia.network/developer/security/responsible-disclosure-policy)
[NextGo-Live Checklistchevron-right](https://docs.somnia.network/developer/deployment-and-production/go-live-checklist)
---
# Ecosystem | Somnia Docs
[Ecosystem Showcasechevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase)
[Ecosystem Toolschevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools)
[PreviousSomnia Gas Differences To Ethereumchevron-left](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum)
[NextEcosystem Showcasechevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase)
---
# Solidity on-chain Reactivity Tutorial | Somnia Docs
This tutorial guides you through building on-chain reactivity on Somnia. You'll create a smart contract that reacts to events from other contracts automatically—invoked by chain validators. Subscriptions trigger handler logic directly in the EVM.
###
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/solidity-on-chain-reactivity-tutorial#overview)
Overview
On-chain reactivity lets Solidity contracts "subscribe" to events emitted by other contracts. When an event fires, your handler contract gets called with the event data, enabling automated business logic like auto-swaps or updates. This is powered by Somnia's native push mechanism, funded by a minimum SOM balance (currently 32 SOM) held by the subscription owner.
Key benefits:
* Atomic: Event + state from the same block (state reads have to be handled by your own contract unlike off-chain subscriptions).
* Decentralized: Runs on-chain without off-chain servers removing liveness assumptions.
* Efficient: Pay only for invocations via gas params.
Prerequisites:
* Solidity basics (e.g., Remix, Hardhat, or Foundry).
* Somnia testnet wallet with 32+ SOM (faucet: [https://docs.somnia.network/developer/network-infoarrow-up-right](https://docs.somnia.network/developer/network-info)
).
* TypeScript SDK for subscription management (install: `npm i @somnia-chain/reactivity`) or manage the subscription directly with the precompile contract on-chain
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/solidity-on-chain-reactivity-tutorial#key-objectives)
Key Objectives
1. **Create a SomniaEventHandler Contract**: Inherit from the abstract contract and override `_onEvent` virtual function with your logic.
2. **Deploy the Handler**: Use your tool of choice (e.g., Remix or Hardhat).
3. **Create a Subscription**: Use the SDK to set up and fund the sub (owner must hold min SOM) or setup the subscription in Solidity.
4. **Handle Callbacks**: The chain invokes your handler on matching filters based on subscription configuration.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/solidity-on-chain-reactivity-tutorial#step-1-install-dependencies)
Step 1: Install Dependencies
Install the reactivity contracts package for the `SomniaEventHandler` abstract contract:
This provides the interface to import. (Public Foundry repo coming soon for easier Forge integration.)
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/solidity-on-chain-reactivity-tutorial#step-2-create-the-handler-contract)
Step 2: Create the Handler Contract
Inherit from `SomniaEventHandler` and implement `_onEvent`. This is where your business logic goes—e.g., update state or call other contracts.
Example: A simple handler that logs or reacts to any event (wildcard).
* **Customization**: Filter in the subscription (Step 4) or add checks in `_onEvent` (e.g., if `emitter == specificAddress`).
* **Warnings**: Keep gas usage low; handlers run in validator context. Test for reentrancy.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/solidity-on-chain-reactivity-tutorial#step-3-deploy-the-handler)
Step 3: Deploy the Handler
* **Using Remix**: Paste code, compile, deploy to Somnia testnet RPC ([https://docs.somnia.network/developer/network-infoarrow-up-right](https://docs.somnia.network/developer/network-info)
).
* **Using Hardhat**: Set up a project, add the contract, and deploy script.
Example Hardhat deploy script (`scripts/deploy.ts`):
Run: `npx hardhat run scripts/deploy.ts --network somniaTestnet` (configure networks in `hardhat.config.ts`).
Note the deployed address (e.g., `0x123...`).
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/solidity-on-chain-reactivity-tutorial#step-4-create-and-manage-the-subscription)
Step 4: Create and Manage the Subscription
Use the TypeScript SDK to create the subscription. The caller becomes the owner and must hold 32+ SOM.
* **Funding**: Chain enforces min SOM; top up if needed.
* **Filters:** See API reference for more filters that can be passed to createSoliditySubscription
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/solidity-on-chain-reactivity-tutorial#step-5-test-the-callback)
Step 5: Test the Callback
1. Deploy an emitter contract that emits events (e.g., simple ERC20 with Transfer).
2. Trigger an event (e.g., transfer tokens).
3. Check your handler: Use explorers or logs to see `_onEvent` executed (e.g., your `ReactedToEvent` emitted).
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/solidity-on-chain-reactivity-tutorial#troubleshooting)
Troubleshooting
* **No Invocation?** Verify sub ID (via `sdk.getSubscriptionInfo`), filters match, and balance funded.
* **Gas Errors?** Increase `gasLimit` or optimize handler.
* **Cancel**: `await sdk.cancelSoliditySubscription(subId);` (get ID from listing).
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/solidity-on-chain-reactivity-tutorial#next-steps)
Next Steps
* Add filters for targeted reactivity.
* Integrate with DeFi/NFT logic.
* Explore hybrid: Off-chain monitoring + on-chain actions.
[PreviousOff-Chain Reactivity: Filtered Subscriptions tutorialchevron-left](https://docs.somnia.network/developer/reactivity/tutorials/off-chain-reactivity-filtered-subscriptions-tutorial)
[NextCron subscriptions via SDKchevron-right](https://docs.somnia.network/developer/reactivity/tutorials/cron-subscriptions-via-sdk)
Last updated 1 day ago
Copy
npm i @somnia-chain/reactivity-contracts
Copy
pragma solidity ^0.8.20;
import { SomniaEventHandler } from "@somnia-chain/reactivity-contracts/contracts/SomniaEventHandler.sol";
contract MyEventHandler is SomniaEventHandler {
event ReactedToEvent(address emitter, bytes32 topic);
function _onEvent(
address emitter,
bytes32[] calldata eventTopics,
bytes calldata data
) internal override {
// Your business logic here
// Example: Emit a new event or update storage
emit ReactedToEvent(emitter, eventTopics[0]);
// Be cautious: Avoid reentrancy or infinite loops (e.g., don't emit events that trigger this handler)
}
}
Copy
import { ethers } from "hardhat";
async function main() {
const Handler = await ethers.getContractFactory("MyEventHandler");
const handler = await Handler.deploy();
await handler.deployed();
console.log("Handler deployed to:", handler.address);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Copy
import { SDK } from '@somnia-chain/reactivity';
import { somniaTestnet } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';
import { createPublicClient, createWalletClient, http } from 'viem';
// Initialize SDK with the required clients
const sdk = new SDK({
public: createPublicClient({
chain: somniaTestnet,
transport: http()
}),
wallet: createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY),
chain: somniaTestnet,
transport: http(),
})
});
const subData = {
handlerContractAddress: '0xYourDeployedHandlerAddress',
priorityFeePerGas: parseGwei('2'),
maxFeePerGas: parseGwei('10'),
gasLimit: 500_000n, // Adjust based on handler complexity
isGuaranteed: true, // Retry on failure
isCoalesced: false, // One call per event
// Optional filters: eventTopics: ['0x...'], emitter: '0xTargetContract'
};
const txHash = await sdk.createSoliditySubscription(subData);
if (txHash instanceof Error) {
console.error('Creation failed:', txHash.message);
} else {
console.log('Subscription created! Tx:', txHash);
}
---
# Tokens and NFTs | Somnia Docs
Tokens and NFTs form the backbone of value and ownership on Somnia. This section walks you through how to **create, deploy, and interact** with ERC-20, ERC-721, and ERC-1155 contracts on the Somnia Network.
You’ll learn how to:
* Deploy fungible tokens (ERC-20) and configure supply and minting logic
* Create and manage NFTs (ERC-721 / ERC-1155) for digital assets and collectibles
* Integrate token interactions into your dApps and subgraphs
* Understand token standards and best practices for gas optimization
> Whether you’re launching a governance token, building an NFT marketplace, or gamifying user engagement, Somnia’s speed and scalability make asset creation seamless.
[PreviousBuilding DAppschevron-left](https://docs.somnia.network/developer/building-dapps)
[NextCreate ERC20 Tokenschevron-right](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc20-tokens)
Last updated 1 month ago
---
# Audit Checklist | Somnia Docs
This Self-Review Audit Checklist is the mandatory, internal quality assurance process that every smart contract must undergo before deployment to any public or private blockchain environment. Its purpose is to catch common, critical, and complex security vulnerabilities early, significantly reducing the risk of exploits, financial loss, and costly post-deployment fixes.
This process consists of two primary phases: a Manual Pre-Deployment Checklist and Automated Static Analysis Tooling.
[hashtag](https://docs.somnia.network/developer/security/audit-checklist#phase-1-manual-pre-deployment-checklist)
Phase 1: Manual Pre-Deployment Checklist
---------------------------------------------------------------------------------------------------------------------------------------------------------------
The development team must manually review and verify that the contract adheres to the following security, logic, and best-practice requirements.
**1.1. Security Vulnerability Checks**
Item
Requirement
Verification Steps
**Reentrancy Protection**
All functions that send Ether or tokens to external addresses must follow the Checks-Effects-Interactions pattern.
Verify that state variables are updated before any external calls are made. Use `transfer`/`send` methods or reentrancy guards where necessary.
**Access Control**
Critical state-changing functions (e.g., `setOwner`, `pause`, `upgrade`, `mint`) must be guarded by proper access modifiers (e.g., `onlyOwner`, `onlyRole`).
Check that function visibility is correctly set (e.g., `internal`, `external`, `private`).
**Integer Overflow/Underflow**
All arithmetic operations, especially those based on user input, must be safe.
For Solidity >= 0.8.0, verify the compiler's default overflow checks are not disabled. For older versions, ensure the use of SafeMath libraries.
**Denial-of-Service (DoS)**
Operations must not iterate over unbounded arrays or map sizes that could be arbitrarily inflated by a malicious user, leading to excessive gas costs.
Check all loops to ensure iteration counts are fixed or restricted.
**External Call Security**
All interactions with unknown or untrusted external contracts are handled safely.
Ensure that results of external calls are checked and fail gracefully if necessary. Use call wrappers to limit reentrancy risk.
**Visibility**
State variables and functions intended for internal use must be declared `private` or `internal`.
Review all function and variable declarations for accidental public exposure.
**1.2. Logic and Functional Checks**
Item
Requirement
Verification Steps
**Functional Specification**
The contract logic precisely matches the intended business logic and all requirements documented in the functional specification.
Verify contract against all use cases and edge cases defined in the project scope.
**State Transitions**
The contract's state (e.g., token balances, operational phase) transitions correctly and predictably.
Trace critical functions (`transfer`, `claim`, `lock`) to ensure state variables update correctly.
**Error Handling**
All potential failure points are handled gracefully with clear, descriptive error messages.
Ensure that `require()` or `revert()` statements are used everywhere necessary and that custom error codes are defined and leveraged.
**Event Emission**
All critical state changes and value transfers must emit an appropriate `Event` to allow for off-chain monitoring, indexing, and UI responsiveness.
Verify that an `Event` is emitted for every action that changes a user-facing state.
[hashtag](https://docs.somnia.network/developer/security/audit-checklist#phase-2-automated-static-analysis-tools)
Phase 2: Automated Static Analysis Tools
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Static analysis is a mandatory, automated review step that must be completed using approved tools before deployment.
**2.1. Mandatory Tooling**
The following static analysis tools must be executed against the final version of the smart contract code:
Tool
Purpose
Output Review Requirement
[**Slither**arrow-up-right](https://github.com/crytic/slither)
Detects various security vulnerabilities (e.g., reentrancy, unprotected calls, misuses of `msg.sender`) and code optimization issues.
Must be executed with all security and efficiency detectors enabled.
[**Mythril**arrow-up-right](https://github.com/ConsenSysDiligence/mythril)
Performs symbolic execution to find potential execution paths that could lead to vulnerabilities (e.g., integer overflows, assertion failures).
Must be run using its security analysis modes (e.g., full scan, execution path analysis).
[**Solhint**arrow-up-right](https://github.com/protofire/solhint)
Enforces code style and security best practices, ensuring clean and maintainable code.
Must pass all configured security and style rulesets without warnings.
**2.2. Warning Triage and Sign-off**
chevron-rightHigh/Critical Severity Warnings[hashtag](https://docs.somnia.network/developer/security/audit-checklist#high-critical-severity-warnings)
Any issue categorized as High or Critical by the static analysis tools must be fixed immediately. The deployment process cannot proceed until these are resolved and the tools run clean.
chevron-rightMedium/Low Severity Warnings[hashtag](https://docs.somnia.network/developer/security/audit-checklist#medium-low-severity-warnings)
These must be reviewed by a lead developer. They must either be:
* Fixed: If the issue is a genuine vulnerability or best practice deviation.
* Justified: If the tool's warning is a false positive or the logic is intentionally implemented in that manner, a clear, documented justification must be added to a dedicated "Audit Waivers" log.
chevron-rightReport Retention[hashtag](https://docs.somnia.network/developer/security/audit-checklist#report-retention)
The final, clean static analysis reports must be saved and archived as part of the pre-deployment documentation.
circle-check
**Congratulations!** You've mastered the audit checklist for both manual and automatic process. Continue to learn prevention strategies and secure coding patterns.
[PreviousSmart Contract Security 101chevron-left](https://docs.somnia.network/developer/security/smart-contract-security-101)
[NextNode/Infra Securitychevron-right](https://docs.somnia.network/developer/security/node-infra-security)
Last updated 1 month ago
* [Phase 1: Manual Pre-Deployment Checklist](https://docs.somnia.network/developer/security/audit-checklist#phase-1-manual-pre-deployment-checklist)
* [Phase 2: Automated Static Analysis Tools](https://docs.somnia.network/developer/security/audit-checklist#phase-2-automated-static-analysis-tools)
---
# OnRamps | Somnia Docs
OnRamps make it easy for users to move from the traditional financial system into the Somnia ecosystem by purchasing tokens directly with their local currency.
This section covers how to integrate **Banxa**, Somnia’s trusted onramping service, to enable seamless and secure fiat-to-crypto transactions within your dApps.
You’ll learn how to:
* Embed Banxa’s checkout flow in your application
* Support multiple payment methods (credit/debit card, bank transfer, etc.)
* Customize the onramp experience to match your app’s branding
* Streamline user onboarding with a direct path from fiat to onchain activity
> Banxa provides developers and users with a regulated, global, and developer-friendly solution for accessing the Somnia network — making your application more inclusive and easier to adopt.
[PreviousAuthenticating with RainbowKitchevron-left](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-rainbowkit)
[NextBuy SOMI Using Banxa Checkoutchevron-right](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout)
Last updated 1 month ago
---
# Deploy with Foundry | Somnia Docs
Somnia empowers developers to build applications for mass adoption. Foundry is a tool for building Smart Contracts for mass adoption, making it easy for developers to create and deploy Smart Contracts to the Somnia Network.
[Foundryarrow-up-right](https://book.getfoundry.sh/)
is a blazing fast, portable and modular toolkit for EVM application development written in Rust.
This guide will teach you how to deploy a “Voting” Smart Contract to the Somia Network using Foundry.
circle-check
Somnia Mainnet is LIVE. To deploy on Somnia Mainnet, you will need SOMI Tokens. Please refer to the [guide](https://docs.somnia.network/get-started/getting-started-for-mainnet)
on Moving from Testnet to Mainnet.
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-foundry#pre-requisites)
Pre-requisites:
-------------------------------------------------------------------------------------------------------------------------------
1. This guide is not an introduction to Solidity Programming; you are expected to understand Basic Solidity Programming.
2. To complete this guide, you will need MetaMask installed and the Somnia Network added to the list of Networks. If you have yet to install MetaMask, please follow this guide to [Connect Your Wallet](https://docs.somnia.network/get-started/connect-your-wallet-to-mainnet)
.
3. Foundry is installed and set up on your local machine. See [Guidearrow-up-right](https://getfoundry.sh/)
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-foundry#initialise-foundry-project)
Initialise Foundry Project
------------------------------------------------------------------------------------------------------------------------------------------------------
To start a new project with Foundry, run the command:
Copy
forge init BallotVoting
This creates a new directory `hello_foundry` from the default template. Open `BallotVoting` directory, and the open the `src` directory where you will find a default `Counter.sol` solidity file. Delete the `Counter.sol` file.
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-foundry#create-the-smart-contract)
Create the Smart Contract
----------------------------------------------------------------------------------------------------------------------------------------------------
Create a new file inside the `src` directory and name it `BallotVoting.sol` and paste the following code:
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-foundry#compile-the-smart-contract)
Compile the Smart Contract
------------------------------------------------------------------------------------------------------------------------------------------------------
Compiling the Smart Contract will convert the Solidity code into machine-readable bytecode.
To compile the Smart Contract, run the command:
It will return the response:
You can learn more about parsing arguments using flags by reading the [Foundry bookarrow-up-right](https://book.getfoundry.sh/reference/forge/forge-build)
.
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-foundry#deploy-contract)
Deploy Contract.
---------------------------------------------------------------------------------------------------------------------------------
Deploying Smart Contracts to the Somnia Network is very straightforward. All you need the RPC URL and the Private Key from an Ethereum address which contains some STT tokens to pay for Gas during deployment. You can get some STT Tokens from the Somnia [Faucetarrow-up-right](https://devnet.somnia.network/)
. Follow this [guidearrow-up-right](https://support.metamask.io/managing-my-wallet/secret-recovery-phrase-and-private-keys/how-to-export-an-accounts-private-key/)
to get your Private Key on MetaMask. To deploy the Smart Contract, run this command in the terminal:
You will see a status response:
Copy the Transaction hash and paste it into the Somnia Network [Explorerarrow-up-right](https://somnia-devnet.socialscan.io/)
. You will find the deployed Smart Contract address. Congratulations. 🎉 You have deployed your “BallotVoting” Smart Contract to the Somnia Network using Foundry. 🎉
> NOTE: Contract Verification on SomniaScan is COMING SOON!
[PreviousDeploy with Thirdwebchevron-left](https://docs.somnia.network/developer/development-frameworks/deploy-with-thirdweb)
[NextDeploy with Hardhatchevron-right](https://docs.somnia.network/developer/development-frameworks/deploy-with-hardhat)
Last updated 1 month ago
* [Pre-requisites:](https://docs.somnia.network/developer/development-frameworks/deploy-with-foundry#pre-requisites)
* [Initialise Foundry Project](https://docs.somnia.network/developer/development-frameworks/deploy-with-foundry#initialise-foundry-project)
* [Create the Smart Contract](https://docs.somnia.network/developer/development-frameworks/deploy-with-foundry#create-the-smart-contract)
* [Compile the Smart Contract](https://docs.somnia.network/developer/development-frameworks/deploy-with-foundry#compile-the-smart-contract)
* [Deploy Contract.](https://docs.somnia.network/developer/development-frameworks/deploy-with-foundry#deploy-contract)
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract BallotVoting {
struct Ballot {
string name;
string[] options;
mapping(uint256 => uint256) votes;
mapping(address => bool) hasVoted;
bool active;
uint256 totalVotes;
}
uint256 public ballotCount;
mapping(uint256 => Ballot) public ballots;
event BallotCreated(uint256 indexed ballotId, string name, string[] options);
event VoteCast(uint256 indexed ballotId, address indexed voter, uint256 optionIndex);
event BallotClosed(uint256 indexed ballotId);
function createBallot(string memory name, string[] memory options) public {
require(options.length > 1, "Ballot must have at least two options");
ballotCount++;
Ballot storage ballot = ballots[ballotCount];
ballot.name = name;
ballot.options = options;
ballot.active = true;
emit BallotCreated(ballotCount, name, options);
}
function vote(uint256 ballotId, uint256 optionIndex) public {
Ballot storage ballot = ballots[ballotId];
require(ballot.active, "This ballot is closed");
require(!ballot.hasVoted[msg.sender], "You have already voted");
require(optionIndex < ballot.options.length, "Invalid option index");
ballot.votes[optionIndex]++;
ballot.hasVoted[msg.sender] = true;
ballot.totalVotes++;
emit VoteCast(ballotId, msg.sender, optionIndex);
}
function closeBallot(uint256 ballotId) public {
Ballot storage ballot = ballots[ballotId];
require(ballot.active, "Ballot is already closed");
ballot.active = false;
emit BallotClosed(ballotId);
}
function getBallotDetails(uint256 ballotId)
public
view
returns (
string memory name,
string[] memory options,
bool active,
uint256 totalVotes
)
{
Ballot storage ballot = ballots[ballotId];
return (ballot.name, ballot.options, ballot.active, ballot.totalVotes);
}
function getBallotResults(uint256 ballotId) public view returns (uint256[] memory results) {
Ballot storage ballot = ballots[ballotId];
uint256[] memory voteCounts = new uint256[](ballot.options.length);
for (uint256 i = 0; i < ballot.options.length; i++) {
voteCounts[i] = ballot.votes[i];
}
return voteCounts;
}
}
Copy
forge build
Copy
[⠊] Compiling...
[⠢] Compiling 27 files with Solc 0.8.28
[⠆] Solc 0.8.28 finished in 2.22s
Compiler run successful!
Copy
forge create --rpc-url
https://dream-rpc.somnia.network
--private-key PRIVATE_KEY src/BallotVoting.sol:BallotVoting
Copy
[⠊] Compiling...
No files changed, compilation skipped
Deployer: 0xb6e4fa6ff2873480590c68D9Aa991e5BB14Dbf03
Deployed to: 0x46639fB6Ce28FceC29993Fc0201Cd5B6fb1b7b16
Transaction hash: 0xb3f8fe0443acae4efdb6d642bbadbb66797ae1dcde2c864d5c00a56302fb9a34
---
# Wildcard Off-Chain Reactivity Tutorial | Somnia Docs
This tutorial shows how to set up an off-chain subscription in TypeScript to listen for _all_ events emitted on the Somnia blockchain (wildcard mode). Notifications will push event data plus results from Solidity view calls (e.g., querying contract state like balances) in a single atomic payload. This reduces RPC roundtrips compared to traditional event listening + separate state fetches.
We'll use the Reactivity SDK for WebSocket subscriptions and viem for chain setup, ABI handling, and decoding. For familiarity, we'll subscribe to all events but decode a common one (ERC20 Transfer) in the callback.
Whilst most developers are unlikely to use reactivity in this way in production, there are scenarios where this will be useful:
* Testing reactivity before applying more filters (see our other tutorials)
* Building an indexer which scrapes required information from the chain into a secondary database that would serve other applications that want large volumes of historical chain data
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/wildcard-off-chain-reactivity-tutorial#overview)
Overview
Off-chain reactivity uses WebSockets to push notifications to your TypeScript app. Key features:
* **Wildcard Listening**: Catch every event without filters.
* **Bundled State**: Include ETH view calls executed at the event's block height.
* **Real-Time**: Low-latency updates for UIs, backends, or scripts.
* **No Gas Costs**: Off-chain, so free per notification (after setup).
This enables reactive apps like live dashboards or automated alerts.
Prerequisites:
* Node.js 20+
* Somnia testnet access (RPC: https://dream-rpc.somnia.network)
* Install dependencies: `npm i @somnia-chain/reactivity viem`
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/wildcard-off-chain-reactivity-tutorial#key-objectives)
Key Objectives
1. **Set Up the Chain and SDK**: Configure viem for Somnia Testnet and initialize the SDK.
2. **Define ETH Calls**: Specify view functions to run on events (e.g., balanceOf).
3. **Create the Subscription**: Start a wildcard WebSocket sub with a data callback.
4. **Decode Notifications**: Use viem to parse event logs and function results.
5. **Run and Test**: Handle incoming data in real-time.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/wildcard-off-chain-reactivity-tutorial#step-1-install-dependencies)
Step 1: Install Dependencies
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/wildcard-off-chain-reactivity-tutorial#step-2-define-the-somnia-chain)
Step 2: Define the Somnia Chain
Use viem's `defineChain` to configure the testnet.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/wildcard-off-chain-reactivity-tutorial#step-3-initialize-the-sdk)
Step 3: Initialize the SDK
Create a public client with WebSocket transport and pass it to the SDK.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/wildcard-off-chain-reactivity-tutorial#step-4-define-eth-calls)
Step 4: Define ETH Calls
Specify view calls to execute when events emit. Here, we query an ERC721 balanceOf (adjust addresses as needed).
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/wildcard-off-chain-reactivity-tutorial#step-5-create-the-wildcard-subscription)
Step 5: Create the Wildcard Subscription
Subscribe with `ethCalls` and an `onData` callback. Omit filters for wildcard (all events).
* **Unsubscribe Later**: `subscription.unsubscribe();`
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/wildcard-off-chain-reactivity-tutorial#step-6-decode-data-in-the-callback)
Step 6: Decode Data in the Callback
Use viem to decode the event log and function results. For example, assuming an ERC20 Transfer event (use `erc20Abi` from viem).
* **Notes**: `data.result` contains `topics`, `data` (event payload), and `simulationResults` (view call outputs). Handle errors if decoding fails (e.g., non-matching ABI).
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/wildcard-off-chain-reactivity-tutorial#step-7-put-it-all-together-and-run)
Step 7: Put It All Together and Run
Full script (`main.ts`):
Run: `ts-node main.ts` (install ts-node if needed).
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/wildcard-off-chain-reactivity-tutorial#testing)
Testing
1. Run the script.
2. Trigger events on Somnia Testnet (e.g., transfer ERC20 tokens via a wallet).
3. Watch console for decoded notifications.
* If no events: Deploy a test contract and emit manually.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/wildcard-off-chain-reactivity-tutorial#troubleshooting)
Troubleshooting
* **No Data?** Ensure WebSocket RPC is connected; check filters (none for wildcard).
* **Decoding Errors?** Verify ABI matches the event/contract.
* **Connection Issues?** Use HTTP fallback if WS fails, but prefer WS for reactivity.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/wildcard-off-chain-reactivity-tutorial#next-steps)
Next Steps
* Add filters (e.g., `eventTopics: ['0xddf...']` for Transfer keccak).
* Integrate with React (future hooks).
* Handle multiple ethCalls/decodes.
* Explore on-chain version: On-Chain Tutorial.
[PreviousTutorialschevron-left](https://docs.somnia.network/developer/reactivity/tutorials)
[NextOff-Chain Reactivity: Filtered Subscriptions tutorialchevron-right](https://docs.somnia.network/developer/reactivity/tutorials/off-chain-reactivity-filtered-subscriptions-tutorial)
Last updated 1 month ago
Copy
npm i @somnia-chain/reactivity viem
Copy
import { defineChain } from 'viem';
const somniaTestnet = defineChain({
id: 50312,
name: 'Somnia Testnet',
network: 'testnet',
nativeCurrency: {
decimals: 18,
name: 'STT',
symbol: 'STT',
},
rpcUrls: {
default: {
http: ['https://dream-rpc.somnia.network'],
webSocket: ['ws://api.infra.testnet.somnia.network/ws'],
},
public: {
http: ['https://dream-rpc.somnia.network'],
webSocket: ['ws://api.infra.testnet.somnia.network/ws'],
},
},
});
Copy
import { SDK } from '@somnia-chain/reactivity';
import { createPublicClient, webSocket } from 'viem';
const publicClient = createPublicClient({
chain: somniaTestnet,
transport: webSocket(),
});
const sdk = new SDK({ public: publicClient });
Copy
import { encodeFunctionData, erc721Abi } from 'viem';
const ethCall = {
to: '0x23B66B772AE29708a884cca2f9dec0e0c278bA2c', // Example Somnia ERC721 contract
data: encodeFunctionData({
abi: erc721Abi,
functionName: 'balanceOf',
args: ['0x3dC360e0389683cA0341a11Fc3bC26252b5AF9bA'], // Example owner address
}),
};
Copy
const subscription = await sdk.subscribe({
ethCalls: [ethCall], // Array of calls; add more if needed
onData: (data) => {
console.log('Raw Notification:', data);
// Decoding happens here (Step 6)
},
});
Copy
import { decodeEventLog, decodeFunctionResult, erc20Abi } from 'viem';
// Inside onData:
const decodedLog = decodeEventLog({
abi: erc20Abi, // Or your custom ABI
topics: data.result.topics,
data: data.result.data,
});
const decodedFunctionResult = decodeFunctionResult({
abi: erc721Abi, // Match the call's ABI
functionName: 'balanceOf',
data: data.result.simulationResults[0], // First call's result
});
console.log('Decoded Event:', decodedLog); // e.g., { eventName: 'Transfer', args: { from, to, value } }
console.log('Decoded Balance:', decodedFunctionResult); // e.g., 42n
Copy
// Imports from above...
async function main() {
// Chain, client, SDK setup from Steps 2-3...
// EthCall from Step 4...
const subscription = await sdk.subscribe({
ethCalls: [ethCall],
onData: (data) => {
// Decoding from Step 6...
},
});
// Keep running (e.g., for a server) or unsubscribe after testing
}
main().catch(console.error);
---
# Understanding Schemas, Schema IDs, Data IDs, and Publisher | Somnia Docs
Somnia Data Streams uses a schema-driven architecture to store and manage blockchain data. Every piece of information stored on the network, whether it’s a chat message, leaderboard score, or todo item, follows a structured schema, is identified by a Schema ID, written with a Data ID, and associated with a Publisher.
In this guide, you’ll learn the difference between Schemas and Schema IDs, how Data IDs uniquely identify records, and how Publishers own and manage their data streams.
* Schemas define the structure of your data.
* Data IDs uniquely identify individual records.
* Publisher determines who owns or controls the data stream.
By the end, you’ll understand how to organize, reference, and manage your application’s data on Somnia.
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#what-are-schemas)
What Are Schemas?
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
A Schema defines the structure and types of the data you want to store onchain. It’s like a blueprint for how your application’s data is encoded, stored, and decoded. A Schema ID, on the other hand, is a unique deterministic hash computed from that schema definition.
When you register or compute a schema, the SDK automatically generates a unique hash (Schema ID) that permanently represents that schema definition.
A schema describes the structure of your data, much like a table in a relational database defines its columns.
####
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#example-defining-a-schema)
Example: Defining a Schema
Copy
const userSchema = `
uint64 timestamp,
string username,
string bio,
address owner
`
This schema tells the Somnia Data Streams system how your data is structured and typed.
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#schema-id-the-unique-identifier)
Schema ID: The Unique Identifier
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
A Schema ID is derived from your schema using a hashing algorithm. It uniquely represents this structure onchain, ensuring consistency and integrity. You can compute its Schema ID before even deploying it onchain.
Example Output:
triangle-exclamation
This hash (schemaId) uniquely identifies the schema onchain. If you change even one character in the schema definition, the Schema ID will change.
The Schema ID is the hash that ensures the same structure is used everywhere, preventing mismatched or corrupted data.
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#registering-a-schema)
Registering a Schema
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
To make the schema usable onchain, it has to be registered by calling the `registerDataSchemas()` method. This ensures other nodes and apps can decode your data correctly:
`id` is a string. human human-readable identifier`ignoreExistingSchemas` is for telling the SDK not to worry about already registered schemas. Once registered, any publisher can use this Schema ID to store or retrieve data encoded according to this structure. The schema defines structure. The Schema ID becomes its permanent onchain reference.
Concept
Database Equivalent
Description
Schema
Table Definition
Defines data fields and types
Schema ID
Table Hash
Uniquely identifies that schema definition
For instance:
`Schema → CREATE TABLE Users (id INT, name TEXT)`
`Schema ID → 0x9f3a...a7c (hash of the above definition)`
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#what-are-data-ids)
What Are Data IDs?
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Every record written to Somnia (e.g., a single message, transaction, or post) must have a Data ID, a unique key representing that entry. It uniquely identifies a specific record (or row). The Data ID ensures that:
* Each entry can be updated or replaced deterministically.
* Developers can reference or fetch a specific record by key.
* Duplicate writes can be prevented.
####
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#example-creating-a-data-id)
Example: Creating a Data ID
A Data ID can be created by hashing a string, typically by combining context and timestamp.
Example Output:
You can now use this ID to publish structured data to the blockchain. A Data ID ensures every record written is unique and can be referenced or updated deterministically.
####
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#example-writing-data-with-a-schema-and-data-id)
Example: Writing Data with a Schema and Data ID
Think of a Data ID like a primary key in a SQL table.
Data ID (Primary Key)
username
bio
0x1234abcd...
Emmanuel
Blockchain Developer
If you write another record with the same Data ID, it updates the existing entry rather than duplicating it, thereby maintaining data integrity. `schemaId` defines how to encode/decode the data, and `dataId` identifies which record this is. The data itself is encoded and written to the blockchain
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#what-are-publishers)
What Are Publishers?
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
A Publisher is any wallet address that sends data to Somnia Streams. Each publisher maintains its own isolated namespace for all schema-based data it writes. This means:
* Data from two different publishers never conflict.
* Apps can filter or query data from a specific publisher.
* Publishers serve as the data owners for all records they create.
####
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#example-getting-a-publisher-address)
Example: Getting a Publisher Address
If you’re using a connected wallet, your publisher is automatically derived using the `createWalletClient` from viem:
Where `publisher = wallet.account.address`
When reading data, you can specify which publisher’s records to fetch:
Example Output:
This retrieves all data published under that schema by that particular address.
Think of Publishers like individual database owners. Each one maintains their own “tables” (schemas) and “records” (data entries) under their unique namespace.
Publisher (Wallet)
Schema ID
Data ID
Description
0x123...abc
Schema A
Data 1
Paul’s todos
0x789...def
Schema A
Data 2
Emmanuel’s todos
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#putting-it-all-together)
Putting It All Together
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
When you publish data on Somnia, three identifiers always work together:
Concept
Role
Example
Schema ID
Identifies schema hash
0x5e4bce54...
Data ID
Identifies record
0x75736572...
Publisher
Identifies sender
0x3dC360e038...
These three make your data verifiable, queryable, and uniquely name-spaced across the blockchain. These form the foundation of the Somnia Data Streams architecture:
* The Schema tells the system what kind of data this is.
* The Schema ID ensures it’s stored consistently across the network.
* The Data ID identifies which record this is.
* The Publisher records who wrote it.
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#example-use-case-chat-messages)
Example Use Case: Chat Messages
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Here’s how they interact in a real-world scenario, a decentralized chat room.
###
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#step-1-define-schema)
Step 1: Define Schema
###
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#step-2-compute-schema-id)
Step 2: Compute Schema ID
###
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#step-3-generate-data-id-for-each-message)
Step 3: Generate Data ID for each message
###
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#step-4-publish-message)
Step 4: Publish Message
Now each message:
* Conforms to a schema
* Is identified by a Schema ID
* Is stored under a unique Data ID
* Is published by a specific Publisher
###
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#common-pitfalls)
Common Pitfalls
Mistake
Description
Fix
Reusing Data IDs incorrectly
Causes overwrites of older records
Use unique IDs like title-timestamp
Forgetting to register schema
Data won’t decode properly
Always call registerDataSchemas() once
Mixing publisher data
Leads to incomplete reads
Query by the correct publisher address and consider aggregating many publishers under a single contract address
###
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#conclusion)
Conclusion
Now that you understand Schemas, Data IDs, and Publishers, you’re ready to build your own data model for decentralized apps and query live data across multiple publishers
[PreviousConceptschevron-left](https://docs.somnia.network/developer/data-streams/concepts)
[NextExtending and composing data schemaschevron-right](https://docs.somnia.network/developer/data-streams/concepts/extending-and-composing-data-schemas)
Last updated 1 month ago
* [What Are Schemas?](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#what-are-schemas)
* [Schema ID: The Unique Identifier](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#schema-id-the-unique-identifier)
* [Registering a Schema](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#registering-a-schema)
* [What Are Data IDs?](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#what-are-data-ids)
* [What Are Publishers?](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#what-are-publishers)
* [Putting It All Together](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#putting-it-all-together)
* [Example Use Case: Chat Messages](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#example-use-case-chat-messages)
* [Step 1: Define Schema](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#step-1-define-schema)
* [Step 2: Compute Schema ID](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#step-2-compute-schema-id)
* [Step 3: Generate Data ID for each message](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#step-3-generate-data-id-for-each-message)
* [Step 4: Publish Message](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#step-4-publish-message)
* [Common Pitfalls](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#common-pitfalls)
* [Conclusion](https://docs.somnia.network/developer/data-streams/concepts/understanding-schemas-schema-ids-data-ids-and-publisher#conclusion)
Copy
import { SDK } from '@somnia-chain/streams'
import { getSdk } from './clients'
const sdk = getSdk()
const schemaId = await sdk.streams.computeSchemaId(userSchema)
console.log("Computed Schema ID:", schemaId)
Copy
Computed Schema ID: 0x5e4bce54a39b42b5b8a235b5d9e27e7031e39b65d7a42a6e0ac5e8b2c79e17b0
Copy
import { zeroBytes32 } from '@somnia-chain/streams'
const ignoreExistingSchemas = true
await sdk.streams.registerDataSchemas([\
{ schemaName: "MySchema", schema: userSchema, parentSchemaId: zeroBytes32 }\
], ignoreExistingSchemas)
Copy
import { toHex } from 'viem'
const dataId = toHex(`username-${Date.now()}`, { size: 32 })
console.log("Data ID:", dataId)
Copy
Data ID: 0x757365726e616d652d31373239303239323435
Copy
import { SchemaEncoder } from '@somnia-chain/streams'
const encoder = new SchemaEncoder(userSchema)
const encodedData = encoder.encodeData([\
{ name: 'timestamp', value: Date.now().toString(), type: 'uint64' },\
{ name: 'username', value: 'Victory', type: 'string' },\
{ name: 'bio', value: 'Blockchain Developer', type: 'string' },\
{ name: 'owner', value: '0xYourWalletAddress', type: 'address' },\
])
await sdk.streams.set([\
{ id: dataId, schemaId, data: encodedData }\
])
Copy
const {
...
createWalletClient,
} = require("viem");
const { privateKeyToAccount } = require("viem/accounts");
// Create wallet client
const walletClient = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY),
chain: dreamChain,
transport: http(dreamChain.rpcUrls.default.http[0]),
});
// Initialize SDK
const sdk = new SDK({
...
wallet: walletClient,
});
const encodedData = schemaEncoder.encodeData([\
...\
{ name: "sender", value: wallet.account.address, type: "address" },\
]);
Copy
const messages = await sdk.streams.getAllPublisherDataForSchema(schemaId, publisherAddress)
Copy
[\
{ timestamp: 1729302920, username: "Victory", bio: "Blockchain Developer" }\
]
Copy
const chatSchema = `
uint64 timestamp,
bytes32 roomId,
string content,
string senderName,
address sender
`
Copy
const schemaId = await sdk.streams.computeSchemaId(chatSchema)
Copy
const dataId = toHex(`${roomName}-${Date.now()}`, { size: 32 })
Copy
const encoded = encoder.encodeData([\
{ name: 'timestamp', value: Date.now().toString(), type: 'uint64' },\
{ name: 'roomId', value: toHex(roomName, { size: 32 }), type: 'bytes32' },\
{ name: 'content', value: 'Hello world!', type: 'string' },\
{ name: 'senderName', value: 'Victory', type: 'string' },\
{ name: 'sender', value: publisherAddress, type: 'address' }\
])
await sdk.streams.set([{ id: dataId, schemaId, data: encoded }])
---
# Cron subscriptions via SDK | Somnia Docs
Starting from `@somnia-chain/[[email protected]](https://docs.somnia.network/cdn-cgi/l/email-protection) `, the typescript SDK introduces two new convenience functions to streamline creating subscriptions for [system-generated events](https://docs.somnia.network/developer/reactivity/system-events)
: `BlockTick` and `Schedule`. These functions reduce boilerplate by handling the underlying `SubscriptionData` structure and precompile interactions for you, while still allowing customization of gas fees, guarantees, and other parameters.
For a deeper understanding of how these system events work under the hood (including event signatures and behaviors), refer to the [System Events documentation](https://docs.somnia.network/developer/reactivity/system-events)
.
###
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/cron-subscriptions-via-sdk#block-tick-subscription)
Block Tick Subscription
The `BlockTick` event triggers at the start of every block if no specific block number is provided, or at a targeted block if specified.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/cron-subscriptions-via-sdk#using-the-sdk)
Using the SDK
Use `createOnchainBlockTickSubscription` to set up the subscription with minimal code:
Copy
import { SDK } from '@somnia-chain/reactivity';
// Assuming you have an instance of SomniaReactivity SDK
const reactivity = new SDK(/* your config */);
async function setupBlockTick() {
try {
const txHash = await reactivity.createOnchainBlockTickSubscription({
// Optional: Specify a future block number; omit for every block
// blockNumber: BigInt(123456789),
handlerContractAddress: '0xYourHandlerContractAddress',
// Optional: Override default handler selector (defaults to onEvent)
// handlerFunctionSelector: '0xYourSelector',
priorityFeePerGas: BigInt(1000000000), // 1 nanoSomi
maxFeePerGas: BigInt(20000000000), // 20 nanoSomi
gasLimit: BigInt(100000),
isGuaranteed: true, // Ensure delivery even if delayed
isCoalesced: false // Handle each event separately
});
console.log('Subscription created with tx hash:', txHash);
} catch (error) {
console.error('Error creating subscription:', error);
}
}
setupBlockTick();
This returns the transaction hash on success or an error if the subscription fails.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/cron-subscriptions-via-sdk#equivalent-in-solidity)
Equivalent in Solidity
For comparison, here's the lower-level Solidity equivalent (as in the core docs):
###
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/cron-subscriptions-via-sdk#schedule-event-one-off-cron-job)
Schedule Event (One-Off Cron Job)
The `Schedule` event is ideal for one-time future actions. Key notes:
* Timestamp must be in the future (at least 12 seconds from n ow).
* It's a one-off subscription—automatically deleted after triggering.
* Use milliseconds (e.g., via [currentmillis.comarrow-up-right](https://currentmillis.com/)
).
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/cron-subscriptions-via-sdk#using-the-sdk-1)
Using the SDK
Use `scheduleOnchainCronJob` for a simple setup:
This returns the transaction hash on success or an error if the subscription fails.
####
[hashtag](https://docs.somnia.network/developer/reactivity/tutorials/cron-subscriptions-via-sdk#equivalent-in-solidity-1)
Equivalent in Solidity
For comparison, here's the lower-level Solidity equivalent (as in the core docs):
circle-info
These SDK functions default the `emitter` to `SOMNIA_REACTIVITY_PRECOMPILE_ADDRESS` and handle event topics automatically, ensuring your handler only responds to genuine system events.
To get started, update your typescript SDK package to `@somnia-chain/[[email protected]](https://docs.somnia.network/cdn-cgi/l/email-protection) ` or later and integrate these functions into your dApp. If you need more advanced customizations (e.g., additional filters like origin or caller), you can still use the full `SoliditySubscriptionData` type directly with the `createSoliditySubscription` SDK function (scheduleOnchainCronJob is calling that internally).
See [Solidity on-chain Reactivity Tutorial](https://docs.somnia.network/developer/reactivity/tutorials/solidity-on-chain-reactivity-tutorial)
for a tutorial on how to create a regular subscription to any event emitted by any smart contract.
[PreviousSolidity on-chain Reactivity Tutorialchevron-left](https://docs.somnia.network/developer/reactivity/tutorials/solidity-on-chain-reactivity-tutorial)
[NextGas Configurationchevron-right](https://docs.somnia.network/developer/reactivity/gas-configuration)
Last updated 1 month ago
* [Block Tick Subscription](https://docs.somnia.network/developer/reactivity/tutorials/cron-subscriptions-via-sdk#block-tick-subscription)
* [Schedule Event (One-Off Cron Job)](https://docs.somnia.network/developer/reactivity/tutorials/cron-subscriptions-via-sdk#schedule-event-one-off-cron-job)
Copy
ISomniaReactivityPrecompile.SubscriptionData
memory subscriptionData = ISomniaReactivityPrecompile
.SubscriptionData({
eventTopics: [BlockTick.selector, bytes32(0), bytes32(0), bytes32(0)], // Or specify blockNumber in topics[1]
emitter: SomniaExtensions.SOMNIA_REACTIVITY_PRECOMPILE_ADDRESS,
handlerContractAddress: address(this),
handlerFunctionSelector: ISomniaEventHandler.onEvent.selector,
/* Add gas params, isGuaranteed, isCoalesced here */
});
// Then call subscribe(subscriptionData) on the precompile
Copy
import { SDK } from '@somnia-chain/reactivity';
// Assuming you have an instance of SomniaReactivity
const reactivity = new SDK(/* your config */);
async function setupSchedule() {
try {
const txHash = await reactivity.scheduleOnchainCronJob({
timestampMs: 1794395471011, // e.g., Nov 11, 2026, 11:11:11.011
handlerContractAddress: '0xYourHandlerContractAddress',
// Optional: Override default handler selector (defaults to onEvent)
// handlerFunctionSelector: '0xYourSelector',
priorityFeePerGas: BigInt(1000000000), // 1 nanoSomi
maxFeePerGas: BigInt(20000000000), // 2 nanoSomi
gasLimit: BigInt(100000),
isGuaranteed: true, // Ensure delivery even if delayed
isCoalesced: false // N/A for one-off, but included for consistency
});
console.log('Cron job scheduled with tx hash:', txHash);
} catch (error) {
console.error('Error scheduling cron job:', error);
}
}
setupSchedule();
Copy
ISomniaReactivityPrecompile.SubscriptionData
memory subscriptionData = ISomniaReactivityPrecompile
.SubscriptionData({
eventTopics: [Schedule.selector, bytes32(uint256(1794395471011)), bytes32(0), bytes32(0)],
emitter: SomniaExtensions.SOMNIA_REACTIVITY_PRECOMPILE_ADDRESS,
handlerContractAddress: address(this),
handlerFunctionSelector: ISomniaEventHandler.onEvent.selector,
/* Add gas params, isGuaranteed, isCoalesced here */
});
// Then call subscribe(subscriptionData) on the precompile
---
# Authenticating with ConnectKit | Somnia Docs
In this guide, we'll integrate [ConnectKitarrow-up-right](https://docs.family.co/connectkit)
with the Somnia Network in a Next.js application. This will enable users to connect their wallets seamlessly, facilitating interactions with the Somnia blockchain.
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-connectkit#prerequisites)
Prerequisites
-----------------------------------------------------------------------------------------------------------------------------------------------------------
Before we begin, ensure you have the following:
1. This guide is not an introduction to JavaScript Programming; you are expected to understand JavaScript.
2. To complete this guide, you will need MetaMask installed and the Somnia DevNet added to the list of Networks. If you have yet to install MetaMask, please follow the [Connect Your Wallet guide](https://docs.somnia.network/get-started/connect-your-wallet-to-mainnet)
.
3. Familiarity with React and Next.js is assumed.
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-connectkit#create-the-next.js-project)
Create the Next.js Project
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Open your terminal and run the following commands to set up a new Next.js project:
Copy
npx create-next-app@latest somnia-connectkit
cd somnia-connectkit
Install the required Dependencies, which are `**wagmi**`, `**viem**`, `**@tanstack/react-query**`, and `**connectkit**`. Run the following command:
Copy
npm install wagmi viem @tanstack/react-query connectkit
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-connectkit#set-up-providers-in-next.js)
Set Up Providers in Next.js
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
We'll set up several providers to manage the application's state and facilitate interactions with the blockchain.
Create a `**components**` directory in the app folder. Inside the components directory, create a file named `**ClientProvider.tsx**` with the following content:
In the app directory, locate the `**layout.tsx**` file and update it as follows:
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-connectkit#build-the-home-page)
Build the Home Page
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
We'll create a simple home page that allows users to connect their wallets and displays their address upon connection.
In the app directory, locate the `**page.tsx**` file and update it as follows:
To run the application, start the Development Server by running the following command:
Open your browser and navigate to `**http://localhost:3000**`. You should see the ConnectKit button, which allows users to connect their wallets to the Somnia network.
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-connectkit#conclusion)
Conclusion
-----------------------------------------------------------------------------------------------------------------------------------------------------
You've successfully integrated ConnectKit with the Somnia Network in a Next.js application. This setup provides a foundation for building decentralized applications on Somnia, enabling seamless wallet connections and interactions with the Somnia Network.
For further exploration, consider adding features such as interacting with smart contracts, displaying user balances, or implementing transaction functionalities.
If you encounter any issues or need assistance, join the [Somnia Developer Discordarrow-up-right](https://discord.gg/somnia)
.
[PreviousAuthenticating with MetaMaskchevron-left](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-metamask)
[NextAuthenticating with Privychevron-right](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy)
Last updated 5 months ago
* [Prerequisites](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-connectkit#prerequisites)
* [Create the Next.js Project](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-connectkit#create-the-next.js-project)
* [Set Up Providers in Next.js](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-connectkit#set-up-providers-in-next.js)
* [Build the Home Page](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-connectkit#build-the-home-page)
* [Conclusion](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-connectkit#conclusion)
Copy
'use client';
import { WagmiConfig, createConfig } from 'wagmi';
import { ConnectKitProvider, getDefaultConfig } from 'connectkit';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { somniaTestnet } from 'viem/chains';
const queryClient = new QueryClient();
const config = createConfig(
getDefaultConfig({
autoConnect: true,
appName: 'Somnia DApp',
chains: [somniaTestnet],
})
);
export default function ClientProvider({ children }) {
return (
{children}
);
}
Copy
import ClientProvider from './components/ClientProvider';
export default function RootLayout({ children }) {
return (
);
}
Copy
npm run dev
---
# Smart Contracts | Somnia Docs
Contract
Address
MultiCallV3
[0x5e44F178E8cF9B2F5409B6f18ce936aB817C5a11arrow-up-right](https://explorer.somnia.network/address/0x5e44F178E8cF9B2F5409B6f18ce936aB817C5a11)
WSOMI
[0x046EDe9564A72571df6F5e44d0405360c0f4dCabarrow-up-right](https://explorer.somnia.network/token/0x046EDe9564A72571df6F5e44d0405360c0f4dCab)
USDC
[0x28bec7e30e6faee657a03e19bf1128aad7632a00arrow-up-right](https://explorer.somnia.network/address/0x28BEc7E30E6faee657a03e19Bf1128AaD7632A00)
WETH
[0x936Ab8C674bcb567CD5dEB85D8A216494704E9D8arrow-up-right](https://explorer.somnia.network/token/0x936Ab8C674bcb567CD5dEB85D8A216494704E9D8)
USDT
[0x67B302E35Aef5EEE8c32D934F5856869EF428330arrow-up-right](https://explorer.somnia.network/token/0x67B302E35Aef5EEE8c32D934F5856869EF428330)
[hashtag](https://docs.somnia.network/developer/smart-contracts#omnichain-somi-deployments)
Omnichain SOMI Deployments
---------------------------------------------------------------------------------------------------------------------------
Mainnet
Address
Type
BNB Chain
[0xa9616e5e23ec1582c2828b025becf3ef610e266farrow-up-right](https://bscscan.com/token/0xa9616e5e23ec1582c2828b025becf3ef610e266f)
OFT
Base
[0x47636b3188774a3E7273D85A537b9bA4Ee7b253arrow-up-right](https://basescan.org/token/0x47636b3188774a3E7273D85A537b9bA4Ee7b2535)
OFT
Ethereum
[0x1B0F6590d21dc02B92ad3A7D00F8884dC4f1aed9arrow-up-right](https://etherscan.io/token/0x1B0F6590d21dc02B92ad3A7D00F8884dC4f1aed9)
OFT
Somnia
[0xC3D4E9Ac47D7f37bB07C2f8355Bb4940DEA3bbC3arrow-up-right](https://explorer.somnia.network/address/0xC3D4E9Ac47D7f37bB07C2f8355Bb4940DEA3bbC3)
NativeOFTAdapter
[hashtag](https://docs.somnia.network/developer/smart-contracts#layerzero-contracts)
LayerZero Contracts
-------------------------------------------------------------------------------------------------------------
* `**chainKey**` **:** `somnia`
* `**stage**` **:** `mainnet`
* `**EID**` **:** `30380`
Contract
Address
endpointV2 (main entrypoint)
[0x6F475642a6e85809B1c36Fa62763669b1b48DD5Barrow-up-right](https://explorer.somnia.network/address/0x6F475642a6e85809B1c36Fa62763669b1b48DD5B)
sendUln302
[0xC39161c743D0307EB9BCc9FEF03eeb9Dc4802de7arrow-up-right](https://explorer.somnia.network/address/0xC39161c743D0307EB9BCc9FEF03eeb9Dc4802de7)
receiveUln302
[0xe1844c5D63a9543023008D332Bd3d2e6f1FE1043arrow-up-right](https://explorer.somnia.network/address/0xC39161c743D0307EB9BCc9FEF03eeb9Dc4802de7)
executor
[0x4208D6E27538189bB48E603D6123A94b8Abe0A0barrow-up-right](https://explorer.somnia.network/address/0x4208D6E27538189bB48E603D6123A94b8Abe0A0b)
deadDVN
[0x6788f52439ACA6BFF597d3eeC2DC9a44B8FEE842arrow-up-right](https://explorer.somnia.network/address/0x6788f52439ACA6BFF597d3eeC2DC9a44B8FEE842)
[hashtag](https://docs.somnia.network/developer/smart-contracts#oracles)
Oracles
-------------------------------------------------------------------------------------
###
[hashtag](https://docs.somnia.network/developer/smart-contracts#dia-mainnet-price-feeds)
DIA - Mainnet [Price Feeds](https://docs.somnia.network/developer/building-dapps/oracles/dia-price-feeds)
Contract
Address
Oracle
[0xbA0E0750A56e995506CA458b2BdD752754CF39C4arrow-up-right](https://explorer.somnia.network/address/0xbA0E0750A56e995506CA458b2BdD752754CF39C4)
Gas Wallet
[0x3073d2E61ecb6E4BF4273Af83d53eDAE099ea04aarrow-up-right](https://mainnet.somnia.w3us.site/address/0x3073d2E61ecb6E4BF4273Af83d53eDAE099ea04a)
USDT
[0x936C4F07fD4d01485849ee0EE2Cdcea2373ba267arrow-up-right](https://mainnet.somnia.w3us.site/address/0x936C4F07fD4d01485849ee0EE2Cdcea2373ba267)
USDC
[0x5D4266f4DD721c1cD8367FEb23E4940d17C83C93arrow-up-right](https://explorer.somnia.network/address/0x5D4266f4DD721c1cD8367FEb23E4940d17C83C93)
BTC
[0xb12e1d47b0022fA577c455E7df2Ca9943D0152bEarrow-up-right](https://explorer.somnia.network/address/0xb12e1d47b0022fA577c455E7df2Ca9943D0152bE)
ARB
[0x6a96a0232402c2BC027a12C73f763b604c9F77a6arrow-up-right](https://explorer.somnia.network/address/0x6a96a0232402c2BC027a12C73f763b604c9F77a6)
SOL
[0xa4a3a8B729939E2a79dCd9079cee7d84b0d96234arrow-up-right](https://mainnet.somnia.w3us.site/address/0xa4a3a8B729939E2a79dCd9079cee7d84b0d96234)
WETH
[0x4E5A9Ebc4D48d7dB65bCde4Ab9CBBE89Da2Add52arrow-up-right](https://explorer.somnia.network/address/0x4E5A9Ebc4D48d7dB65bCde4Ab9CBBE89Da2Add52)
SOMI
[0x1f5f46B0DABEf8806a1f33772522ED683Ba64E27arrow-up-right](https://explorer.somnia.network/address/0x1f5f46B0DABEf8806a1f33772522ED683Ba64E27)
###
[hashtag](https://docs.somnia.network/developer/smart-contracts#protofire-mainnet-vrf-smart-contracts)
Protofire - Mainnet [VRF Smart Contracts](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf)
Contract
Address
VRFV2PlusWrapper
[0x606b2B36516AB7479D1445Ec14B6B39B44901bf8arrow-up-right](https://explorer.somnia.network/address/0x606b2B36516AB7479D1445Ec14B6B39B44901bf8)
LINK Token
[0x0a4Db7035284566F6f676991ED418140dC01A2aaarrow-up-right](https://explorer.somnia.network/address/0x0a4Db7035284566F6f676991ED418140dC01A2aa)
LINK/NATIVE oracle
[0xEBD41881413dD76F42DF2902ee865099af9099B4arrow-up-right](https://explorer.somnia.network/address/0xEBD41881413dD76F42DF2902ee865099af9099B4)
[PreviousBuild a Realtime On-Chain Gamechevron-left](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game)
[NextDevelopment Frameworkschevron-right](https://docs.somnia.network/developer/development-frameworks)
Last updated 1 month ago
* [Omnichain SOMI Deployments](https://docs.somnia.network/developer/smart-contracts#omnichain-somi-deployments)
* [LayerZero Contracts](https://docs.somnia.network/developer/smart-contracts#layerzero-contracts)
* [Oracles](https://docs.somnia.network/developer/smart-contracts#oracles)
* [DIA - Mainnet Price Feeds](https://docs.somnia.network/developer/smart-contracts#dia-mainnet-price-feeds)
* [Protofire - Mainnet VRF Smart Contracts](https://docs.somnia.network/developer/smart-contracts#protofire-mainnet-vrf-smart-contracts)
---
# Verifying via Explorer | Somnia Docs
This documentation provides complete guidance for verifying your smart contracts using our web interface, API endpoints, Hardhat, and Foundry.
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#how-it-works)
How It Works
Somnia Explorer operates a comprehensive API server that includes **all stable Solidity compiler versions** (excluding nightly builds). Our verification system supports multiple methods to accommodate different development workflows:
* Single File Verification: For simple contracts contained in one source file
* Multi-Part File Verification: For complex contracts with multiple dependencies
* Standard JSON Input: The recommended method with complete compilation metadata
* API Verification: Submit verification requests programmatically through REST endpoints
* Hardhat Verification: Verify contracts directly from your Hardhat environment using the Somnia Explorer API
* Foundry Verification: Verify contracts from the command line with Foundry’s `forge verify-contract` command
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#verification-process)
Verification Process
1. Input Processing: We receive your contract information through web interface or API
2. Compilation: Our system compiles your source code using the specified compiler version
3. Response Generation: Verification results are returned with contract details
circle-info
Recommendation: We strongly recommend using Standard JSON Input as it provides the most comprehensive information about your code, including optimizer settings and complete compilation metadata.
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#id-1.-explorer-ui)
1\. Explorer UI
The Somnia Explorer web interface provides an intuitive way to verify your smart contracts. The verification process is designed to be user-friendly while maintaining technical accuracy.
####
[hashtag](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#getting-started)
Getting Started

* Navigate to the Verify Contract page on Somnia Explorer
* Enter the contract address you want to verify
* Select the appropriate compiler type from the available options
####
[hashtag](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#solidity-single-file)
Solidity (Single File)
Perfect for simple contracts that don't have external dependencies or complex import structures.

1
####
[hashtag](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#single-file-verification-steps)
Single-file verification — Steps
* Contract Code: Copy and paste your complete smart contract source code into the text area
* Compiler version: Choose the Solidity compiler version used during development
* Optimization Settings:
* Enable optimization if it was used during compilation (default: disabled)
* Set the optimization runs value (default: 200)
* EVM Version: Select the appropriate EVM version (default: Prague)
* License Type: Specify the open source license type for your contract
* Constructor Arguments: Enter constructor parameters if your contract was deployed with initialization arguments
* Submit: Click "Verify and Publish" to complete verification
####
[hashtag](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#solidity-multi-part-files)
Solidity (Multi-Part Files)
Ideal for complex contracts with multiple source files, libraries, and dependencies.

1
####
[hashtag](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#multi-file-verification-steps)
Multi-file verification — Steps
* File Upload: Upload all source files required for compilation
* Supported formats: `.sol`, `.json`
* Include all imported contracts and dependencies
* Compiler version: Choose the Solidity compiler version used during development
* Optimization Settings:
* Enable optimization if it was used during compilation (default: disabled)
* Set the optimization runs value (default: 200)
* EVM Version: Select the appropriate EVM version (default: Prague)
* License Type: Specify the open source license type for your contract
* Constructor Arguments: Enter constructor parameters if your contract was deployed with initialization arguments
* Submit: Click "Verify and Publish" to complete verification
####
[hashtag](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#solidity-standard-json-input)
Solidity (Standard-JSON-Input)
The most comprehensive verification method, recommended for production contracts.

1
####
[hashtag](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#standard-json-input-steps)
Standard JSON input — Steps
* JSON File Upload: Upload your complete Standard JSON input file
* Supported formats: `.json`
* Should include all compilation settings, optimizer configuration, and source mappings
* Compiler version: Choose the Solidity compiler version used during development
* License Type: Specify the open source license type for your contract
* Constructor Arguments: Enter constructor parameters if your contract was deployed with initialization arguments
* Submit: Click "Verify and Publish" to complete verification
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#id-2.-api-verification)
2\. API Verification
####
[hashtag](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#base-configuration)
Base Configuration
API Base URL [https://explorer-somnia.api.xangle.ioarrow-up-right](https://explorer-somnia.api.xangle.io/)
Required Header `X-Chain: SOMNIA`
####
[hashtag](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#endpoints)
Endpoints
**Single File Contract Verification**
POST `/api/contract/verify/single-file`
Content Type: `application/json`
Description: Verify a smart contract using a single Solidity source file.
Request Body
Response
**Multi-File Contract Verification**
POST `/api/contract/verify/multi-file`
Content Type: `multipart/form-data`
Description: Verify a smart contract using multiple source files.
Request Body
Response
**JSON Input Contract Verification**
POST `/api/contract/verify/json`
Content Type: `multipart/form-data`
Description: Verify a smart contract using multiple source files (Standard JSON input).
Request Body
Response
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#id-3.-hardhat-verification)
3\. Hardhat Verification
Hardhat provides a convenient way to verify smart contracts directly from your development environment. By integrating with the Somnia Explorer API, you can submit your contract source code and metadata without leaving your workflow.
Base Configuration
In your `hardhat.config.js` (or `hardhat.config.ts`), configure the Somnia mainnet settings:
* `networks`: Use public RPC or a private RPC you manage for Somnia mainnet
* `etherscan.apiKey`: Normally used for Etherscan, but for Somnia an empty value is sufficient
* `customChains`: Points Hardhat to the Somnia Explorer API and browser endpoints
Verification Command
* ``: The address of your deployed contract
* `[constructorArgs...]`: Any constructor parameters passed during deployment
1
####
[hashtag](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#hardhat-example-workflow)
Hardhat example workflow
1. Deploy your contract on Somnia mainnet using Hardhat
2. Copy the deployed address from the deployment output
3. Run the verify command with the address and constructor arguments
4. The verified source code will appear on Somnia Explorer
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#id-4.-foundry-verification)
4\. Foundry Verification
Foundry offers a streamlined way to verify your smart contracts directly from the command line. By using the built-in `forge verify-contract` command, you can connect to Somnia Explorer and publish your contract’s source code automatically.
Verification Command
* `-rpc-url`: Use public RPC or a private RPC you manage for Somnia mainnet
* `-verifier`: Use Etherscan
* `-verifier-url`: API endpoint of Somnia Explorer contract verification
* ``: The deployed contract address
* `[contractFile]:[contractName]`: The Solidity file and the specific contract name to verify
1
####
[hashtag](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#foundry-example-workflow)
Foundry example workflow
1. Deploy your contract to Somnia mainnet using Foundry or another deployment method
2. Copy the deployed address from the deployment output
3. Run the verify command with the contract address, source file, and contract name
4. Once complete, your verified source code will be visible on Somnia Explorer
[PreviousUsing the Viem Librarychevron-left](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library)
[NextDebug Playbookchevron-right](https://docs.somnia.network/developer/development-frameworks/debug-playbook)
Last updated 1 month ago
* [How It Works](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#how-it-works)
* [Verification Process](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#verification-process)
* [1\. Explorer UI](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#id-1.-explorer-ui)
* [2\. API Verification](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#id-2.-api-verification)
* [3\. Hardhat Verification](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#id-3.-hardhat-verification)
* [4\. Foundry Verification](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer#id-4.-foundry-verification)
Copy
{
"mainNetType": "SOMNIA",
"evmVersion": "string",
"contractAddress": "string",
"solcVersion": "string",
"optimizationEnabled": boolean,
"runs": number,
"viaIR": boolean,
"constructorArgs": "string",
"sourceCode": "string"
}
Copy
{
"STS": "Y",
"CTRTADDR": "string",
"MSG": "string",
"VA": number,
"NM": "string",
"BTCD": "string",
"ABI": "string"
}
Copy
{
"mainNetType": "SOMNIA",
"evmVersion": "string",
"contractAddress": "string",
"solcVersion": "string",
"optimizationEnabled": boolean,
"runs": number,
"viaIR": boolean,
"constructorArgs": "string",
"sourceFileList": [\
"string($binary)"\
]
}
Copy
{
"STS": "Y",
"CTRTADDR": "string",
"MSG": "string",
"VA": number,
"NM": "string",
"BTCD": "string",
"ABI": "string"
}
Copy
{
"mainNetType": "SOMNIA",
"evmVersion": "string",
"contractAddress": "string",
"solcVersion": "string",
"optimizationEnabled": boolean,
"runs": number,
"viaIR": boolean,
"constructorArgs": "string",
"sourceFileList": [\
"string($binary)"\
]
}
Copy
{
"STS": "Y",
"CTRTADDR": "string",
"MSG": "string",
"VA": number,
"NM": "string",
"BTCD": "string",
"ABI": "string"
}
Copy
const config: HardhatUserConfig = {
solidity: "v0.8.30", // replace if necessary
networks: {
'somnia-mainnet': {
url: {public rpc or your own rpc url}
},
},
etherscan: {
apiKey: {
'somnia-mainnet': 'empty'
},
customChains: [\
{\
network: "somnia-mainnet",\
chainId: 5031,\
urls: {\
apiURL: "https://verify-contract.xangle.io/somnia/api",\
browserURL: "https://somnia-explorer.xangle.io"\
}\
}\
]
}
};
Copy
npx hardhat verify \
--network somnia-mainnet \
\
[...constructorArgs]
Copy
forge verify-contract \
--rpc-url {public rpc or your own rpc url} \
--verifier etherscan \
--verifier-url ‘https://verify-contract.xangle.io/somnia/api’ \
\
[contractFile]:[contractName]
---
# Wallet Integration and Auth | Somnia Docs
Seamless onboarding and intuitive wallet experiences are at the heart of successful dApps. Somnia supports a range of modern wallet integration tools to help you deliver smooth, secure, and gas-efficient user journeys — from first interaction to advanced transactions.
In this section, you’ll learn how to:
* [Integrate **Privy** for embedded, Web2-friendly wallet flows](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy)
* [Use **RainbowKit** to offer a beautiful, customizable wallet connection UI](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-rainbowkit)
* [Leverage **ConnectKit** for flexible and extensible wallet integrations](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-connectkit)
> Whether you're onboarding crypto-first users or Web2 newcomers, these wallet SDKs give you the tools to build dApps that are simple, secure, and delightful to use.
[PreviousUsing Native SOMI/STTchevron-left](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/using-native-somi-stt)
[NextAuthenticating with MetaMaskchevron-right](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-metamask)
Last updated 1 month ago
---
# Oracles | Somnia Docs
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](https://docs.somnia.network/developer/building-dapps/oracles/dia-price-feeds)
* [How to implement **Protofire Price Feeds**](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds)
* [How to implement **Verifiable Randomness (VRF)** via **Protofire + Chainlink**](https://docs.somnia.network/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.
[PreviousListening to Blockchain Events (WebSocket)chevron-left](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/listening-to-blockchain-events-websocket)
[NextDIA Price Feedschevron-right](https://docs.somnia.network/developer/building-dapps/oracles/dia-price-feeds)
Last updated 1 month ago
---
# Buy SOMI Using Banxa Checkout | Somnia Docs
You can easily buy SOMI tokens using Banxa, a trusted fiat to crypto gateway partner. This guide walks you through purchasing SOMI directly from the [Banxa Checkout Portalarrow-up-right](https://checkout.banxa.com/)
.
* * *
Banxa lets you buy cryptocurrency with local payment methods, including credit/debit cards, bank transfers, and mobile wallets. In this guide, you’ll learn how to:
* Access the Banxa checkout page
* Select SOMI as the token to purchase
* Complete payment securely
* Receive SOMI in your wallet
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#prerequisites)
Prerequisites
--------------------------------------------------------------------------------------------------------------------------------------
Before you start, ensure you have:
* Your wallet address handy, this is where SOMI will be sent.
* A valid payment method supported in your region (Visa, Mastercard, bank transfer, etc.).
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#step-1-visit-the-banxa-checkout-page)
Step 1 — Visit the Banxa Checkout Page
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Go to [https://checkout.banxa.com/arrow-up-right](https://checkout.banxa.com/)
. You’ll see a simple interface to select your fiat currency and cryptocurrency.
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#undefined)

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
[hashtag](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#step-2-choose-your-purchase-options)
Step 2 — Choose Your Purchase Options
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
* In the “You Pay” field, enter how much fiat (e.g. USD, EUR, NGN) you want to spend.
* In the “You Get” field, select SOMI as the token.
* Select the network as Somnia Network.
Banxa will automatically show the conversion rate, fees, and delivery estimate.
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#step-3-enter-your-wallet-address)
Step 3 — Enter Your Wallet Address
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Copy your wallet address from your Somnia-compatible wallet (e.g. MetaMask) and paste it into the “Wallet Address” field.
triangle-exclamation
_**Triple check the address before proceeding. Funds sent to the wrong address cannot be recovered.**_
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#step-4-complete-kyc-verification)
Step 4 — Complete KYC Verification
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Banxa complies with international regulations and may ask you to verify your identity. Typical verification steps include:
* Uploading a government-issued ID
* Taking a selfie
* Confirming your billing address
This process only needs to be done once per account.

* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#step-5-select-a-payment-method)
Step 5 — Select a Payment Method
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Choose one of the available payment options for your country:
* Credit/Debit Card
* Bank Transfer
* Apple Pay / Google Pay (where supported)
Follow the on-screen instructions to complete the payment.
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#step-6-receive-somi-in-your-wallet)
Step 6 — Receive SOMI in Your Wallet
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Once payment is confirmed:
* Banxa will process the transaction.
* SOMI tokens will be sent directly to your wallet on the Somnia Network.
* You’ll receive a confirmation email once the transfer is complete.
This may take a few minutes, depending on network congestion and payment method.
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#need-help)
Need Help?
-------------------------------------------------------------------------------------------------------------------------------
If your transaction is delayed or you need assistance, you can contact:
* Banxa Support: [support.banxa.comarrow-up-right](https://support.banxa.com/)
* Somnia Community: [Discord → #supportarrow-up-right](https://discord.gg/somnia)
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#disclaimers)
Disclaimers
----------------------------------------------------------------------------------------------------------------------------------
* Banxa is a third party provider. Always ensure you are using the official [checkout.banxa.comarrow-up-right](https://checkout.banxa.com/)
link.
* Transaction fees, limits, and verification steps vary by country and payment method.
* SOMI purchases are non reversible once processed.
[PreviousOnRampschevron-left](https://docs.somnia.network/developer/building-dapps/onramps)
[NextAccount Abstractionchevron-right](https://docs.somnia.network/developer/building-dapps/account-abstraction)
Last updated 4 months ago
* [Prerequisites](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#prerequisites)
* [Step 1 — Visit the Banxa Checkout Page](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#step-1-visit-the-banxa-checkout-page)
* [](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#undefined)
* [Step 2 — Choose Your Purchase Options](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#step-2-choose-your-purchase-options)
* [Step 3 — Enter Your Wallet Address](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#step-3-enter-your-wallet-address)
* [Step 4 — Complete KYC Verification](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#step-4-complete-kyc-verification)
* [Step 5 — Select a Payment Method](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#step-5-select-a-payment-method)
* [Step 6 — Receive SOMI in Your Wallet](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#step-6-receive-somi-in-your-wallet)
* [Need Help?](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#need-help)
* [Disclaimers](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout#disclaimers)
---
# “Hello World” App | Somnia Docs
If you’ve ever wanted to see your data travel onchain in real time, this is the simplest way to begin. In this guide, we’ll build and run a Hello World Publisher and Subscriber using the Somnia Data Streams SDK. It demonstrates how to define a schema, publish onchain data, and read it in real time.
Somnia Data Streams enables developers to store, retrieve, and react to real-time blockchain data without needing to build indexers or manually poll the chain.
Each app works around three key ideas:
1. Schemas – define the data format.
2. Data IDs – uniquely identify each record.
3. Publishers – wallet addresses that own and post data.
Your app can write (“publish”) data using one account, and another app (or user) can “subscribe” to read or monitor that data stream. This “Hello World” project demonstrates exactly how that works.
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/hello-world-app#prerequisites)
Prerequisites
------------------------------------------------------------------------------------------------------------------------
Before you begin:
* Node.js 20+
* A Somnia Testnet wallet with STT test tokens
* `.env` file containing your wallet credentials
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/hello-world-app#project-setup)
Project Setup
------------------------------------------------------------------------------------------------------------------------
Create a Project Directory and install dependencies [@somnia-chain/streamsarrow-up-right](https://www.npmjs.com/package/@somnia-chain/streams)
and [viemarrow-up-right](https://viem.sh/)
:
Copy
npm i @somnia-chain/streams viem dotenv
Now create a .env file to hold your test wallet’s private key:
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/hello-world-app#project-overview)
Project Overview
------------------------------------------------------------------------------------------------------------------------------
The project contains four files:
File
Description
publisher.js
Sends “Hello World” messages to Somnia Data Streams
subscriber.js
Reads and displays those messages
dream-chain.js
Configures the Somnia Dream testnet connection
package.json
Handles dependencies and npm scripts
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/hello-world-app#network-configuration)
Network Configuration
----------------------------------------------------------------------------------------------------------------------------------------
The file `dream-chain.js` defines the blockchain network connection.
This allows both publisher and subscriber scripts to easily reference the same testnet environment.
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/hello-world-app#hello-world-publisher)
Hello World Publisher
----------------------------------------------------------------------------------------------------------------------------------------
The publisher connects to the blockchain, registers a schema if necessary, and sends a “Hello World” message every few seconds.
This function connects to Somnia Dream Testnet using your wallet and computes the schema ID for the message structure. It then registers the schema if not already registered. The \`encodeData\` method encodes each message as a structured data packet, and it then publishes data to the chain using sdk.streams.set().
Each transaction is a verifiable, timestamped on-chain record.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/hello-world-app#hello-world-subscriber)
Hello World Subscriber
------------------------------------------------------------------------------------------------------------------------------------------
The subscriber listens for any messages published under the same schema and publisher address. It uses a simple polling mechanism, executed every 3 seconds, to fetch and decode updates.
This function computes the same Schema ID used by the publisher. It polls the blockchain for all messages from that publisher and decodes data according to the schema fields. Then, it displays any new messages with timestamps and sender addresses.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/hello-world-app#run-the-app)
Run the App
--------------------------------------------------------------------------------------------------------------------
Run both scripts in separate terminals:
and then
You’ll see Publisher Output:
Subscriber Output
Congratulations 🎉 You’ve just published and read blockchain data using Somnia Data Streams!
This is the foundation for real-time decentralized apps, chat apps, dashboards, IoT feeds, leaderboards, and more.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/hello-world-app#conclusion)
Conclusion
------------------------------------------------------------------------------------------------------------------
You’ve just learned how to:
* Define and compute a schema and schema ID
* Register it on the Somnia Testnet
* Publish and subscribe to on-chain structured data
* Decode and render blockchain messages in real time
This simple 'Hello World' app is your first step toward building real-time, decentralized applications on Somnia.
[PreviousTutorialschevron-left](https://docs.somnia.network/developer/data-streams/tutorials)
[NextBuild Your First Schemachevron-right](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema)
Last updated 1 month ago
* [Prerequisites](https://docs.somnia.network/developer/data-streams/tutorials/hello-world-app#prerequisites)
* [Project Setup](https://docs.somnia.network/developer/data-streams/tutorials/hello-world-app#project-setup)
* [Project Overview](https://docs.somnia.network/developer/data-streams/tutorials/hello-world-app#project-overview)
* [Network Configuration](https://docs.somnia.network/developer/data-streams/tutorials/hello-world-app#network-configuration)
* [Hello World Publisher](https://docs.somnia.network/developer/data-streams/tutorials/hello-world-app#hello-world-publisher)
* [Hello World Subscriber](https://docs.somnia.network/developer/data-streams/tutorials/hello-world-app#hello-world-subscriber)
* [Run the App](https://docs.somnia.network/developer/data-streams/tutorials/hello-world-app#run-the-app)
* [Conclusion](https://docs.somnia.network/developer/data-streams/tutorials/hello-world-app#conclusion)
Copy
PRIVATE_KEY=0xYOUR_PRIVATE_KEY
PUBLIC_KEY=0xYOUR_PUBLIC_ADDRESS
Copy
const { defineChain } = require("viem");
const dreamChain = defineChain({
id: 50312,
name: "Somnia Dream",
network: "somnia-dream",
nativeCurrency: { name: "STT", symbol: "STT", decimals: 18 },
rpcUrls: {
default: { http: ["https://dream-rpc.somnia.network"] },
},
});
module.exports = { dreamChain };
Copy
const { SDK, SchemaEncoder, zeroBytes32 } = require("@somnia-chain/streams")
const { createPublicClient, http, createWalletClient, toHex } = require("viem")
const { privateKeyToAccount } = require("viem/accounts")
const { waitForTransactionReceipt } = require("viem/actions")
const { dreamChain } = require("./dream-chain")
require("dotenv").config()
async function main() {
const publicClient = createPublicClient({ chain: dreamChain, transport: http() })
const walletClient = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY),
chain: dreamChain,
transport: http(),
})
const sdk = new SDK({ public: publicClient, wallet: walletClient })
// 1️⃣ Define schema
const helloSchema = `string message, uint256 timestamp, address sender`
const schemaId = await sdk.streams.computeSchemaId(helloSchema)
console.log("Schema ID:", schemaId)
// 2️⃣ Safer schema registration
const ignoreAlreadyRegistered = true
try {
const txHash = await sdk.streams.registerDataSchemas(
[\
{\
schemaName: 'hello_world',\
schema: helloSchema,\
parentSchemaId: zeroBytes32\
},\
],
ignoreAlreadyRegistered
)
if (txHash) {
await waitForTransactionReceipt(publicClient, { hash: txHash })
console.log(`✅ Schema registered or confirmed, Tx: ${txHash}`)
} else {
console.log('ℹ️ Schema already registered — no action required.')
}
} catch (err) {
// fallback: if the SDK doesn’t support the flag yet
if (String(err).includes('SchemaAlreadyRegistered')) {
console.log('⚠️ Schema already registered. Continuing...')
} else {
throw err
}
}
// 3️⃣ Publish messages
const encoder = new SchemaEncoder(helloSchema)
let count = 0
setInterval(async () => {
count++
const data = encoder.encodeData([\
{ name: 'message', value: `Hello World #${count}`, type: 'string' },\
{ name: 'timestamp', value: BigInt(Math.floor(Date.now() / 1000)), type: 'uint256' },\
{ name: 'sender', value: walletClient.account.address, type: 'address' },\
])
const dataStreams = [{ id: toHex(`hello-${count}`, { size: 32 }), schemaId, data }]
const tx = await sdk.streams.set(dataStreams)
console.log(`✅ Published: Hello World #${count} (Tx: ${tx})`)
}, 3000)
}
main()
Copy
const { SDK, SchemaEncoder } = require("@somnia-chain/streams");
const { createPublicClient, http } = require("viem");
const { dreamChain } = require("./dream-chain");
require('dotenv').config();
async function main() {
const publisherWallet = process.env.PUBLISHER_WALLET;
const publicClient = createPublicClient({ chain: dreamChain, transport: http() });
const sdk = new SDK({ public: publicClient });
const helloSchema = `string message, uint256 timestamp, address sender`;
const schemaId = await sdk.streams.computeSchemaId(helloSchema);
const schemaEncoder = new SchemaEncoder(helloSchema);
const result = new Set();
setInterval(async () => {
const allData = await sdk.streams.getAllPublisherDataForSchema(schemaId, publisherWallet);
for (const dataItem of allData) {
const fields = dataItem.data ?? dataItem;
let message = "", timestamp = "", sender = "";
for (const field of fields) {
const val = field.value?.value ?? field.value;
if (field.name === "message") message = val;
if (field.name === "timestamp") timestamp = val.toString();
if (field.name === "sender") sender = val;
}
const id = `${timestamp}-${message}`;
if (!result.has(id)) {
result.add(id);
console.log(`🆕 ${message} from ${sender} at ${new Date(Number(timestamp) * 1000).toLocaleTimeString()}`);
}
}
}, 3000);
}
main();
Copy
npm run publisher
Copy
npm run subscriber
Copy
Schema ID: 0x27c30fa6547c34518f2de6a268b29ac3b54e51c98f8d0ef6018bbec9153e9742
⚠️ Schema already registered. Continuing...
✅ Published: Hello World #1 (Tx: 0xf21ad71a6c7aa54c171ad38b79ef417e8488fd750ce00c1357918b7c7fa5c951)
✅ Published: Hello World #2 (Tx: 0xe999b0381ba9d937d85eb558fefe214fa4e572767c4e698c6e31588ff0e68f0a)
Copy
🆕 Hello World #2 from 0xb6e4fa6ff2873480590c68D9Aa991e5BB14Dbf03 at 2:24:04 PM
🆕 Hello World #3 from 0xb6e4fa6ff2873480590c68D9Aa991e5BB14Dbf03 at 2:24:07 PM
---
# Build Your First Schema | Somnia Docs
Before you can publish or read structured data on the Somnia Network using Somnia Data Streams, you must first define a Schema. A schema acts as the blueprint or data contract between your publisher and all subscribers who wish to interpret your data correctly.
In the Somnia Data Streams system, every schema is expressed as a canonical string. A strict, ordered list of fields with Solidity compatible types.
For example, a chat application schema:
Copy
uint64 timestamp, bytes32 roomId, string content, string senderName, address sender
This simple definition:
* Establishes how data should be encoded and decoded on-chain.
* Produces a unique schemaId derived from its exact string representation.
* Enables multiple publishers and readers to exchange data consistently, without needing to redeploy contracts or agree on custom ABIs.
Each schema you define becomes a typed, reusable data model, similar to a table definition in a database or an ABI for events, but far simpler. Once created, schemas can be:
* Reused across many applications.
* Extended to create hierarchical data definitions (e.g., “GPS coordinates” → “Vehicle telemetry”).
* Versioned by creating new schemas when structure changes occur.
This tutorial will walk you through building, registering, and validating your first schema step by step.
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#prerequisites)
Prerequisites
--------------------------------------------------------------------------------------------------------------------------------
Before continuing, ensure you have the following:
1. Node.js 20+
2. TypeScript configured in your project
3. `.env.local` file for environment variables
Add your credentials to .env.local:
Copy
RPC_URL=https://dream-rpc.somnia.network
PRIVATE_KEY=0xYOUR_FUNDED_PRIVATE_KEY
4. A Funded Testnet Account. You’ll need an address with test tokens on the Somnia Testnet to register schemas or publish data.
triangle-exclamation
NOTE: The Private Key is only required if connecting a Private Key via a Viem wallet account. Important: Never expose your private key to a client-side environment. Keep it in server scripts or backend environments only.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#what-youll-build)
What You’ll Build
---------------------------------------------------------------------------------------------------------------------------------------
In this tutorial, you will:
* Create a canonical schema string (your “data ABI”)
* Compute the schema ID
* Register your schema on-chain (idempotently)
* Validate your schema with a simple encode/decode test
We’ll use a chat message schema as a running example:
This schema represents a single chat message, which can be used later to build a full on-chain chat application.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#project-setup)
Project Setup
--------------------------------------------------------------------------------------------------------------------------------
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#install-dependencies)
Install dependencies
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#define-chain-configuration)
Define Chain configuration
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#set-up-your-clients)
Set up your clients
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#define-the-schema-string)
Define the Schema String
------------------------------------------------------------------------------------------------------------------------------------------------------
Field order matters, and ensure to always use Solidity-compatible types. It is important to keep the `string` fields short to minimize gas. Note that changing type or order creates a new schema ID.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#compute-the-schemaid)
Compute the schemaId
----------------------------------------------------------------------------------------------------------------------------------------------
The SDK computes a unique hash of the schema string. This `schemaId` is your permanent identifier. Anyone using the same schema string will derive the same ID _\[confirm with Vincent for correctness\]_.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#register-the-schema)
Register the Schema
--------------------------------------------------------------------------------------------------------------------------------------------
Registration makes your schema discoverable and reusable by others. _\[confirm with Vincent for correctness\]_.
`isSchemaRegistered()` checks chain state. `registerSchema()` publishes the schema definition to Streams. Thus, the transaction is idempotent, meaning that it is safe to re-run.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#encode-and-decode-a-sample-payload)
Encode and Decode a Sample Payload
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Test your schema locally before publishing any data.
`encodeData()` serializes the payload according to the schema definition. `decodeData()` restores readable field values from the encoded hex. This step ensures your schema fields align correctly.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#conclusion)
Conclusion
--------------------------------------------------------------------------------------------------------------------------
You’ve just built and registered your first schema on Somnia Data Streams.
Your schema now acts as a public data contract between any publisher and subscriber that wants to communicate using this structure.
[Previous“Hello World” Appchevron-left](https://docs.somnia.network/developer/data-streams/tutorials/hello-world-app)
[NextStreams Case Study: Formula 1chevron-right](https://docs.somnia.network/developer/data-streams/tutorials/streams-case-study-formula-1)
Last updated 1 month ago
* [Prerequisites](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#prerequisites)
* [What You’ll Build](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#what-youll-build)
* [Project Setup](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#project-setup)
* [Install dependencies](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#install-dependencies)
* [Define Chain configuration](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#define-chain-configuration)
* [Set up your clients](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#set-up-your-clients)
* [Define the Schema String](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#define-the-schema-string)
* [Compute the schemaId](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#compute-the-schemaid)
* [Register the Schema](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#register-the-schema)
* [Encode and Decode a Sample Payload](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#encode-and-decode-a-sample-payload)
* [Conclusion](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema#conclusion)
Copy
uint64 timestamp, bytes32 roomId, string content, string senderName, address sender
Copy
npm i @somnia-chain/streams viem
npm i -D @types/node
Copy
// src/lib/chain.ts
import { defineChain } from 'viem'
export const somniaTestnet = defineChain({
id: 50312,
name: 'Somnia Testnet',
network: 'somnia-testnet',
nativeCurrency: { name: 'STT', symbol: 'STT', decimals: 18 },
rpcUrls: {
default: { http: ['https://dream-rpc.somnia.network'] },
public: { http: ['https://dream-rpc.somnia.network'] },
},
})
Copy
// src/lib/clients.ts
import { createPublicClient, createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { somniaTestnet } from './chain'
function need(key: 'RPC_URL' | 'PRIVATE_KEY') {
const v = process.env[key]
if (!v) throw new Error(`Missing ${key} in .env.local`)
return v
}
export const publicClient = createPublicClient({
chain: somniaTestnet,
transport: http(need('RPC_URL')),
})
export const walletClient = createWalletClient({
account: privateKeyToAccount(need('PRIVATE_KEY') as `0x${string}`),
chain: somniaTestnet,
transport: http(need('RPC_URL')),
})
Copy
// src/lib/chatSchema.ts
export const chatSchema =
'uint64 timestamp, bytes32 roomId, string content, string senderName, address sender'
Copy
// scripts/compute-schema-id.ts
import 'dotenv/config'
import { SDK } from '@somnia-chain/streams'
import { publicClient } from '../src/lib/clients'
import { chatSchema } from '../src/lib/chatSchema'
async function main() {
const sdk = new SDK({ public: publicClient })
const id = await sdk.streams.computeSchemaId(chatSchema)
console.log('Schema ID:', id)
}
main().catch((e) => {
console.error(e)
process.exit(1)
})
Copy
// scripts/register-schema.ts
import 'dotenv/config'
import { SDK, zeroBytes32 } from '@somnia-chain/streams'
import { publicClient, walletClient } from '../src/lib/clients'
import { chatSchema } from '../src/lib/chatSchema'
import { waitForTransactionReceipt } from 'viem/actions'
async function main() {
const sdk = new SDK({ public: publicClient, wallet: walletClient })
const id = await sdk.streams.computeSchemaId(chatSchema)
const isRegistered = await sdk.streams.isSchemaRegistered(id)
if (isRegistered) {
console.log('Schema already registered.')
return
}
const txHash = await sdk.streams.registerDataSchemas({ schemaName: "chat", schema: chatSchema })
console.log('Register tx:', txHash)
const receipt = await waitForTransactionReceipt(publicClient, { hash: txHash })
console.log('Registered in block:', receipt.blockNumber)
}
main().catch((e) => {
console.error(e)
process.exit(1)
})
Copy
// scripts/encode-decode.ts
import 'dotenv/config'
import { SchemaEncoder } from '@somnia-chain/streams'
import { toHex, type Hex } from 'viem'
import { chatSchema } from '../src/lib/chatSchema'
const encoder = new SchemaEncoder(chatSchema)
const encodedData: Hex = encoder.encodeData([\
{ name: 'timestamp', value: Date.now().toString(), type: 'uint64' },\
{ name: 'roomId', value: toHex('general', { size: 32 }), type: 'bytes32' },\
{ name: 'content', value: 'Hello Somnia!', type: 'string' },\
{ name: 'senderName', value: 'Victory', type: 'string' },\
{ name: 'sender', value: '0x0000000000000000000000000000000000000001', type: 'address' },\
])
console.log('Encoded:', encodedData)
console.log('Decoded:', encoder.decodeData(encodedData))
---
# The DApp Publisher Proxy Pattern | Somnia Docs
In the "[Working with Multiple Publishersarrow-up-right](https://www.google.com/search?q=httpsa://emre-gitbook.gitbook.io/emre-gitbook-docs/data-streams/working-with-multiple-publishers-in-a-shared-stream)
" tutorial, you learned the standard pattern for building an aggregator:
1. Maintain a list of all known publisher addresses.
2. Loop through this list.
3. Call `sdk.streams.getAllPublisherDataForSchema()` for each address.
4. Merge and sort the results on the client side.
This pattern is simple and effective for a known, manageable number of publishers (e.g., 50 IoT sensors from a single company).
**But what happens at a massive scale?**
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern#the-problem-the-10-000-publisher-scenario)
The Problem: The 10,000-Publisher Scenario
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Imagine you are building a popular on-chain game. You have a `leaderboardSchema` and 10,000 players actively publishing their scores.
If you use the standard aggregator pattern, your "global leaderboard" DApp would need to:
1. Somehow find all 10,000 player addresses.
2. Perform **10,000 separate read calls** (`getAllPublisherDataForSchema`) to the Somnia RPC node.
This is not scalable, fast, or efficient. It creates an enormous (and slow) data-fetching burden on your application.
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern#the-solution-the-dapp-publisher-proxy)
The Solution: The DApp Publisher Proxy
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
This is an advanced architecture that inverts the model to solve the read-scalability problem.
Instead of having 10,000 publishers write to Streams _directly_, they all write to **your DApp's smart contract**, which then publishes to Streams on their behalf.
**The Flow:**
1. **User (Publisher):** Calls a function on your DApp's contract (e.g., `myGame.submitScore(100)`). The `msg.sender` is the user's address.
2. **DApp Contract (The Proxy):** Internally, your `submitScore` function:
* Adds the user's address (`msg.sender`) _into the data payload_ to preserve provenance.
* Calls `somniaStreams.esstores(...)` using its _own_ contract address.
3. **Somnia Data Streams:** Records the data. To the Streams contract, the **only publisher** is your DApp Contract's address.
The Result:
Your global leaderboard aggregator now only needs to make one single read call to fetch all 10,000 players' data:
`sdk.streams.getAllPublisherDataForSchema(schemaId, YOUR_DAPP_CONTRACT_ADDRESS)`
This is massively scalable and efficient for read-heavy applications.
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern#tutorial-building-a-gameleaderboard-proxy)
Tutorial: Building a `GameLeaderboard` Proxy
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Let's build a conceptual example of this pattern.
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern#what-youll-build)
What You'll Build
------------------------------------------------------------------------------------------------------------------------------------------------
1. **A new Schema** that _includes_ the original publisher's address.
2. **A** `**GameLeaderboard.sol**` smart contract that acts as the proxy.
3. **A Client Script** that writes to the _proxy contract_ instead of Streams.
4. **A new Aggregator** that reads from the _proxy contract's_ address.
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern#the-schema-solving-for-provenance)
The Schema (Solving for Provenance)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Since the `msg.sender` to the Streams contract will always be our _proxy contract_, we lose the built-in provenance. We must re-create it by adding the original player's address to the schema itself.
`**src/lib/schema.ts**`
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern#the-proxy-smart-contract-solidity)
The Proxy Smart Contract (Solidity)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
This is a new smart contract you would write and deploy for your DApp. It acts as the gatekeeper.
circle-info
**SDK set() vs. Contract esstores()** This example uses the low-level contract function esstores(). When you use sdk.streams.set() in your client-side code, the SDK is calling the esstores() function on the Somnia Streams contract "under the hood." This proxy contract is simply calling that same function directly.
`**src/contracts/GameLeaderboard.sol**`
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern#the-client-script-publishing-to-the-proxy)
The Client Script (Publishing to the Proxy)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
The client-side logic changes. The user no longer needs the Streams SDK to publish, but rather a way to call your DApp's `submitScore` function.
`**src/scripts/publishScore.ts**`
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern#the-aggregator-script-simple-scalable-reads)
The Aggregator Script (Simple, Scalable Reads)
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
This is the pay-off. The aggregator script is now _dramatically_ simpler and more scalable. It only needs to know the single DApp contract address.
`**src/scripts/readLeaderboard.ts**`
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern#trade-offs-and-considerations)
Trade-Offs & Considerations
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
This pattern is powerful, but it's important to understand the trade-offs.
**Feature**
**Standard Pattern (Multi-Publisher)**
**Proxy Pattern (Single Publisher)**
**Read Scalability**
**Low.** Requires N read calls (N = # of publishers).
**High.** Requires 1 read call, regardless of publisher count.
**Publisher Gas Cost**
**Low.** 1 transaction (`streams.set`).
**High.** 1 transaction + 1 internal transaction. User pays more gas.
**Provenance**
**Automatic & Implicit.** `msg.sender` is the user.
**Manual.** Must be built into the schema (`address player`).
**Complexity**
**Simple.** Requires only the SDK.
**Complex.** Requires writing, deploying, and maintaining a custom smart contract.
####
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern#conclusion)
Conclusion
The **DApp Publisher Proxy** is an advanced but essential pattern for any Somnia Data Streams application that needs to scale to thousands or millions of publishers (e.t., games, social media, large IoT networks).
It simplifies the data aggregation logic from `**N+1**` read calls down to `**1**`, at the cost of higher gas fees for publishers and increased development complexity.
For most DApps, we recommend starting with the simpler "Multi-Publisher Aggregator" pattern. When your application's read performance becomes a bottleneck due to a high number of publishers, you can evolve to this proxy pattern to achieve massive read scalability.
[PreviousWorking with Multiple Publishers in a Shared Streamchevron-left](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream)
[NextBuild a Minimal On-Chain Chat Appchevron-right](https://docs.somnia.network/developer/data-streams/tutorials/build-a-minimal-on-chain-chat-app)
Last updated 1 month ago
* [The Problem: The 10,000-Publisher Scenario](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern#the-problem-the-10-000-publisher-scenario)
* [The Solution: The DApp Publisher Proxy](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern#the-solution-the-dapp-publisher-proxy)
* [Tutorial: Building a GameLeaderboard Proxy](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern#tutorial-building-a-gameleaderboard-proxy)
* [What You'll Build](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern#what-youll-build)
* [The Schema (Solving for Provenance)](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern#the-schema-solving-for-provenance)
* [The Proxy Smart Contract (Solidity)](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern#the-proxy-smart-contract-solidity)
* [The Client Script (Publishing to the Proxy)](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern#the-client-script-publishing-to-the-proxy)
* [The Aggregator Script (Simple, Scalable Reads)](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern#the-aggregator-script-simple-scalable-reads)
* [Trade-Offs & Considerations](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern#trade-offs-and-considerations)
Copy
// Schema: 'uint64 timestamp, address player, uint256 score'
export const leaderboardSchema =
'uint64 timestamp, address player, uint256 score'
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// A simplified interface for the Somnia Streams contract
interface IStreams {
struct DataStream {
bytes32 id;
bytes32 schemaId;
bytes data;
}
// This is the correct low-level function name
function esstores(DataStream[] calldata streams) external;
}
/**
* @title GameLeaderboard
* This contract is a DApp Publisher Proxy.
* Users call submitScore() here.
* This contract then calls somniaStreams.esstores() as a single publisher.
*/
contract GameLeaderboard {
IStreams public immutable somniaStreams;
bytes32 public immutable leaderboardSchemaId;
event ScoreSubmitted(address indexed player, uint256 score);
/**
* @param _streamsAddress The deployed address of the Somnia Streams contract
* (e.g., 0x6AB397FF662e42312c003175DCD76EfF69D048Fc on Somnia Testnet).
* @param _schemaId The pre-computed schemaId for 'uint64 timestamp, address player, uint256 score'.
*/
constructor(address _streamsAddress, bytes32 _schemaId) {
somniaStreams = IStreams(_streamsAddress);
leaderboardSchemaId = _schemaId;
}
/**
* @notice Players call this function to submit their score.
* @param score The player's score.
*/
function submitScore(uint256 score) external {
// 1. Get the original publisher's address
address player = msg.sender;
uint64 timestamp = uint64(block.timestamp);
// 2. Encode the data payload to match the schema
// Schema: 'uint64 timestamp, address player, uint256 score'
bytes memory data = abi.encode(timestamp, player, score);
// 3. Create a unique dataId (e.g., hash of player and time)
bytes32 dataId = keccak256(abi.encodePacked(player, timestamp));
// 4. Prepare the DataStream struct
IStreams.DataStream[] memory d = new IStreams.DataStream[](1);
d[0] = IStreams.DataStream({
id: dataId,
schemaId: leaderboardSchemaId,
data: data
});
// 5. Call Somnia Streams. The `msg.sender` for this call
// is THIS contract (GameLeaderboard).
somniaStreams.esstores(d);
// 6. Emit a DApp-specific event for good measure
emit ScoreSubmitted(player, score);
}
}
Copy
import 'dotenv/config'
import { createWalletClient, http, createPublicClient, parseAbi } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { somniaTestnet } from '../lib/chain' // From previous tutorials
import { waitForTransactionReceipt } from 'viem/actions'
// --- DApp Contract Setup ---
// This is the address you get after deploying GameLeaderboard.sol
const DAPP_CONTRACT_ADDRESS = '0x...' // Your deployed GameLeaderboard contract address
// A minimal ABI for our GameLeaderboard contract
const DAPP_ABI = parseAbi([\
'function submitScore(uint256 score) external',\
])
// --- --- ---
function getEnv(key: string): string {
const value = process.env[key]
if (!value) throw new Error(`Missing environment variable: ${key}`)
return value
}
// We can use any publisher wallet
const walletClient = createWalletClient({
account: privateKeyToAccount(getEnv('PUBLISHER_1_PK') as `0x${string}`),
chain: somniaTestnet,
transport: http(getEnv('RPC_URL')),
})
const publicClient = createPublicClient({
chain: somniaTestnet,
transport: http(getEnv('RPC_URL')),
})
async function main() {
const newScore = Math.floor(Math.random() * 10000)
console.log(`Player ${walletClient.account.address} submitting score: ${newScore}...`)
try {
const { request } = await publicClient.simulateContract({
account: walletClient.account,
address: DAPP_CONTRACT_ADDRESS,
abi: DAPP_ABI,
functionName: 'submitScore',
args: [BigInt(newScore)],
})
const txHash = await walletClient.writeContract(request)
console.log(`Transaction sent, hash: ${txHash}`)
await waitForTransactionReceipt(publicClient, { hash: txHash })
console.log('Score submitted successfully!')
} catch (e: any) {
console.error(`Failed to submit score: ${e.message}`)
}
}
main().catch(console.error)
Copy
import 'dotenv/config'
import { SDK, SchemaDecodedItem } from '@somnia-chain/streams'
import { createPublicClient, http } from 'viem'
import { somniaTestnet } from '../lib/chain'
import { leaderboardSchema } from '../libL/schema' // Our new schema
// --- DApp Contract Setup ---
const DAPP_CONTRACT_ADDRESS = '0x...' // Your deployed GameLeaderboard contract address
// --- --- ---
function getEnv(key: string): string {
const value = process.env[key]
if (!value) throw new Error(`Missing environment variable: ${key}`)
return value
}
const publicClient = createPublicClient({
chain: somniaTestnet,
transport: http(getEnv('RPC_URL')),
})
// Helper to decode the leaderboard data
interface ScoreRecord {
timestamp: number
player: `0x${string}`
score: bigint
}
function decodeScoreRecord(row: SchemaDecodedItem[]): ScoreRecord {
const val = (field: any) => field?.value?.value ?? field?.value ?? ''
return {
timestamp: Number(val(row[0])),
player: val(row[1]) as `0x${string}`,
score: BigInt(val(row[2])),
}
}
async function main() {
// The aggregator only needs a public client
const sdk = new SDK({ public: publicClient })
const schemaId = await sdk.streams.computeSchemaId(leaderboardSchema)
if (!schemaId) throw new Error('Could not compute schemaId')
console.log('--- Global Leaderboard Aggregator ---')
console.log(`Reading all data from proxy: ${DAPP_CONTRACT_ADDRESS}\n`)
// 1. Make ONE call to get all data for the DApp
const data = await sdk.streams.getAllPublisherDataForSchema(
schemaId,
DAPP_CONTRACT_ADDRESS
)
if (!data || data.length === 0) {
console.log('No scores found.')
return
}
// 2. Decode and sort the records
const allScores = (data as SchemaDecodedItem[][]).map(decodeScoreRecord)
allScores.sort((a, b) => (b.score > a.score ? 1 : -1)) // Sort descending by score
// 3. Display the leaderboard
console.log(`Total scores found: ${allScores.length}\n`)
allScores.forEach((record, index) => {
console.log(
`#${index + 1}: Player ${record.player} - Score: ${record.score} (at ${new Date(record.timestamp).toISOString()})`
)
})
}
main().catch(console.error)
---
# Data Indexing and Querying | Somnia Docs
Subgraphs allow your dApp to efficiently index and query onchain data from the Somnia Network. Whether you're building dashboards, tracking events, or querying user actions, Subgraphs provide a scalable way to make your smart contract data accessible and searchable.
In this section, you’ll learn:
* [How to create and deploy subgraphs on Somnia using **ORMI**](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/ormi-subgraph)
* [How to build with **Protofire’s subgraph infrastructure**](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/protofire-subgraph)
* [Frontend integrations for building responsive UIs from your subgraph data](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-nextjs-fetch)
> Subgraphs are essential for any developer building real-time dApps, analytics dashboards, or event-driven logic on Somnia.
[PreviousSmart Wallet App with Thirdwebchevron-left](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb)
[NextOrmi Subgraphchevron-right](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/ormi-subgraph)
Last updated 4 months ago
---
# Account Abstraction | Somnia Docs
Account Abstraction (AA) revolutionizes how users interact with blockchain applications by making wallets **smarter, simpler, and more programmable**.
In this section, you’ll explore how to implement **Smart Contract Accounts (SCAs)** on Somnia using modern tooling like **Thirdweb** and **Privy**, and learn how to enable **gasless transactions** and **session keys** for better UX.
You’ll learn how to:
* Create and manage smart contract wallets
* Implement user operations via ERC-4337-style flows
* Enable sponsored and gasless transactions
* Simplify onboarding through smart wallets and relayers
> Account Abstraction bridges the gap between Web2 simplicity and Web3 ownership — empowering developers to build dApps users actually love to use.
[PreviousBuy SOMI Using Banxa Checkoutchevron-left](https://docs.somnia.network/developer/building-dapps/onramps/buy-somi-using-banxa-checkout)
[NextGasless Transactions with Thirdwchevron-right](https://docs.somnia.network/developer/building-dapps/account-abstraction/gasless-transactions-with-thirdw)
Last updated 4 months ago
---
# Ecosystem Showcase | Somnia Docs
[Elix.fichevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/elix.fi)
[Meme Coinschevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins)
[NFTs2Mechevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/nfts2me)
[Somnia Domains (.somi)chevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-domains-.somi)
[Somnia Exchangechevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange)
[Tokoschevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/tokos)
[PreviousEcosystemchevron-left](https://docs.somnia.network/developer/deployment-and-production/ecosystem)
[NextElix.fichevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/elix.fi)
---
# Somnia Data vs Event Streams | Somnia Docs
###
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/somnia-data-vs-event-streams#tl-dr)
tl;dr
* Data Streams: Raw bytes calldata written to chain with contextual information on how to parse the data using a public or private `data schema`
* Event Streams: [EVM logsarrow-up-right](https://docs.chainstack.com/docs/ethereum-logs-tutorial-series-logs-and-filters)
emitted by the Somnia Streams protocol. Protocol users register and `event schema` that can be referenced they want to emit an event that others can `subscribe` to with Somnia streams reactivity
Both data and event streams can be done without knowing Solidity and without deploying any smart contracts
###
[hashtag](https://docs.somnia.network/developer/data-streams/concepts/somnia-data-vs-event-streams#typescript-sdk-interface)
TypeScript SDK interface
Copy
/**
* @param somniaStreamsEventId The identifier of a registered event schema within Somnia streams protocol or null if using a custom event source
* @param ethCalls Fixed set of ETH calls that must be executed before onData callback is triggered. Multicall3 is recommended. Can be an empty array
* @param context Event sourced selectors to be added to the data field of ETH calls, possible values: topic0, topic1, topic2, topic3, topic4, data and address
* @param onData Callback for a successful reactivity notification
* @param onError Callback for a failed attempt
* @param eventContractSource Alternative contract event source (any on somnia) that will be emitting the logs specified by topicOverrides
* @param topicOverrides Optional when using Somnia streams as an event source but mandatory when using a different event source
* @param onlyPushChanges Whether the data should be pushed to the subscriber only if eth_call results are different from the previous
*/
export type SubscriptionInitParams = {
somniaStreamsEventId?: string
ethCalls: EthCall[]
context?: string
onData: (data: any) => void
onError?: (error: Error) => void
eventContractSource?: Address
topicOverrides?: Hex[]
onlyPushChanges: boolean
}
export interface StreamsInterface {
// Write
set(d: DataStream[]): Promise;
emitEvents(e: EventStream[]): Promise;
setAndEmitEvents(d: DataStream[], e: EventStream[]): Promise;
// Manage
registerDataSchemas(registrations: DataSchemaRegistration[]): Promise;
registerEventSchemas(ids: string[], schemas: EventSchema[]): Promise;
manageEventEmittersForRegisteredStreamsEvent(
streamsEventId: string,
emitter: Address,
isEmitter: boolean
): Promise;
// Read
getByKey(schemaId: SchemaID, publisher: Address, key: Hex): Promise;
getAtIndex(schemaId: SchemaID, publisher: Address, idx: bigint): Promise;
getBetweenRange(
schemaId: SchemaID,
publisher: Address,
startIndex: bigint,
endIndex: bigint
): Promise;
getAllPublisherDataForSchema(
schemaReference: SchemaReference,
publisher: Address
): Promise;
getLastPublishedDataForSchema(
schemaId: SchemaID,
publisher: Address
): Promise;
totalPublisherDataForSchema(schemaId: SchemaID, publisher: Address): Promise;
isDataSchemaRegistered(schemaId: SchemaID): Promise;
computeSchemaId(schema: string): Promise;
parentSchemaId(schemaId: SchemaID): Promise;
schemaIdToId(schemaId: SchemaID): Promise;
idToSchemaId(id: string): Promise;
getAllSchemas(): Promise;
getEventSchemasById(ids: string[]): Promise;
// Helper
deserialiseRawData(
rawData: Hex[],
parentSchemaId: Hex,
schemaLookup: {
schema: string;
schemaId: Hex;
} | null
): Promise;
// Subscribe
subscribe(initParams: SubscriptionInitParams): Promise<{ subscriptionId: string, unsubscribe: () => void } | undefined>;
// Protocol
getSomniaDataStreamsProtocolInfo(): Promise;
}
[PreviousExtending and composing data schemaschevron-left](https://docs.somnia.network/developer/data-streams/concepts/extending-and-composing-data-schemas)
[NextIntersection with Somnia Reactivitychevron-right](https://docs.somnia.network/developer/data-streams/concepts/intersection-with-somnia-reactivity)
Last updated 1 month ago
* [tl;dr](https://docs.somnia.network/developer/data-streams/concepts/somnia-data-vs-event-streams#tl-dr)
* [TypeScript SDK interface](https://docs.somnia.network/developer/data-streams/concepts/somnia-data-vs-event-streams#typescript-sdk-interface)
---
# Using the Viem Library | Somnia Docs
Somnia empowers developers to build applications for mass adoption. Smart Contracts deployed on Somnia will require front-end user interfaces to interact with them. These front-end user interfaces will require middleware libraries to establish a connection to the Somnia Network and enable interaction with Smart Contracts. In this Guide, you will learn how to use the Viem Library to establish a connection between your deployed Smart Contracts on Somnia Network and your Front-end User application. You will also learn how to perform READ and WRITE operations using Viem. [Viemarrow-up-right](https://viem.sh/)
is a TypeScript interface for Ethereum that provides low-level stateless primitives for interacting with Ethereum.
circle-check
Somnia Mainnet is LIVE. To deploy on Somnia Mainnet, you will need SOMI Tokens. Please refer to the [guide](https://docs.somnia.network/get-started/getting-started-for-mainnet)
on Moving from Testnet to Mainnet.
[hashtag](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library#how-does-viem-enable-ui-interaction)
How does Viem enable UI interaction?
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
When a Smart Contract is programmed using any development tool such as RemixIDE, Hardhat or Foundry, the Smart Contract undergoes a “compilation” stage. Compiling a Smart Contract, among other things will convert the Solidity code into machine-readable bytecode. An ABI file is also produced when a Smart Contrac is compiled. ABI stand for Application Binary Interface. You can think of an ABI like the Interface that make it possible for a User Interface to connect with the Smart Contract functions in a way similar to how an API makes it possible to to connect a UI and Backend server in web2.

[hashtag](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library#example-smart-contract)
Example Smart Contract
-------------------------------------------------------------------------------------------------------------------------------------------------
Here is an example \`Greeter.sol\` Smart Contract:
[hashtag](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library#example-abi)
Example ABI
---------------------------------------------------------------------------------------------------------------------------
When the Greeter Smart Contract is compiled, below is its ABI:
chevron-rightABI[hashtag](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library#abi)
[https://gist.github.com/emmaodia/bdb9b84998b1e4f3f19d0ae27c541e63arrow-up-right](https://gist.github.com/emmaodia/bdb9b84998b1e4f3f19d0ae27c541e63)
The ABI is an array of JSON objects containing the constructor, event, and four functions in the Smart Contract. Using a Library such as Viem, you can perform READ and WRITE operations, for each of the ABI objects. You can READ the events, and other “view” only methods. You can perform WRITE operations on the “changeName” function. A cursory look at each ABI method will help you understand the function and what can be accomplished by interacting with the method. For example:
[hashtag](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library#how-to-use-viem)
How to use Viem.
------------------------------------------------------------------------------------------------------------------------------------
To use Viem, it has to be installed in the project directory where you want to perform READ and WRITE operations. First, create a directory and initialize a new project using npm.
Initialize a project in the directory by running the command:
Install Viem by running the following command.
[hashtag](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library#set-up-viem)
Set Up Viem
---------------------------------------------------------------------------------------------------------------------------
To connect to the deployed Example Greeter Smart Contract using Viem, it is necessary to have access to the Smart Contract’s ABI and its Contract Address. Viem sets up a “`transport`” infrastructure to connect with a node in the EVM Network and the deployed Smart Contracts. We will use some Viem methods to connect to your Smart Contract deployed on the Somnia Network. Viem has a \`createPublicClient\` and a \`createWalletClient\` method. The PublicClient is used to perform READ operations, while the WalletClient is used to perform WRITE operations. Create a new file `index.js` Import the method classes from the Library:
The `http` is the transport protocol for interacting with the Node of the Somnia Blockchain via RPC. It uses the default Somnia RPC URL: [`https://dream-rpc.somnia.network`arrow-up-right](https://dream-rpc.somnia.network/)
. In the future developers can use RPC providers to avoid rate limiting.
[hashtag](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library#set-up-publicclient)
Set up PublicClient
-------------------------------------------------------------------------------------------------------------------------------------------
We will start with setting up the `publicClient` to read `view` only methods. Set up a `**publicClient**` where the default Transport is `http` and `chain` is `SOMNIA` network created using the `defineChain` method.
[hashtag](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library#consume-actions)
Consume Actions
-----------------------------------------------------------------------------------------------------------------------------------
Now that you have a Client set up, you can interact with Somnia Blockchain and consume Actions! An example will be to call the `greet` method on the deployed Smart Contract. To do this, we have to create a file name `abi.js` and add the exported ABI in the file.
In the `index.js` we can import the ABI file and start calling methods on the deployed Smart Contract. Import the ABI:
Set Contract Address
> This is an example Greeter Smart Contract deployed on Somnia Testnet
Write a Function \`interactWithContract\`:
Open your terminal and run the following:
You will see the response from the Smart Contract logged into the Console!

Congratulations, you have successfully performed a READ operation on your Smart Contract deployed on Somnia.
[hashtag](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library#set-up-wallet-client)
Set up Wallet Client
---------------------------------------------------------------------------------------------------------------------------------------------
To perform a write operation, we will parse the \`createWalletClient\` method to a \`walletClient\` variable. It is important to understand that carrying out WRITE operations changes the state of the Blockchain, unlike READ operations, where you read the state of the Blockchain. So, to perform WRITE operations, a user will have to spend Gas, and to be able to spend Gas, a user will have to parse his Private Key from an EOA to give the Library masked permission to carry out transactions on behalf of the user. To read the Private Key from an EOA, we will use a Viem method:
Then, create a variable `walletClient`
> The variable \`$YOUR\_PRIVATE\_KEY\` variable can be parsed using a dotenv file.
After sending a WRITE operation, we also have to be able to read the transaction to see the state changes. We will rely on a READ method to read a transaction, \`waitForTransactionReceipt\`. Update the \`interactWithContract\` function with the code below:
Save the file and run the node command to see your responses logged into the console.
Congratulations, you have successfully performed a WRITE operation on your Smart Contract deployed on Somnia. 🎉
[PreviousDeploy with Hardhatchevron-left](https://docs.somnia.network/developer/development-frameworks/deploy-with-hardhat)
[NextVerifying via Explorerchevron-right](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer)
Last updated 1 month ago
* [How does Viem enable UI interaction?](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library#how-does-viem-enable-ui-interaction)
* [Example Smart Contract](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library#example-smart-contract)
* [Example ABI](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library#example-abi)
* [How to use Viem.](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library#how-to-use-viem)
* [Set Up Viem](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library#set-up-viem)
* [Set up PublicClient](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library#set-up-publicclient)
* [Consume Actions](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library#consume-actions)
* [Set up Wallet Client](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library#set-up-wallet-client)
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
contract Greeter {
string public name;
address public owner;
event NameChanged(string oldName, string newName);
modifier onlyOwner() {
require(msg.sender == owner, "Only the owner can perform this action");
_;
}
constructor(string memory _initialName) {
name = _initialName;
owner = msg.sender;
}
function changeName(string memory _newName) external onlyOwner {
string memory oldName = name;
name = _newName;
emit NameChanged(oldName, _newName);
}
function greet() external view returns (string memory) {
return string(abi.encodePacked("Hello, ", name, "!"));
}
}
Copy
{
"inputs": [ --->specifies it is an input, i.e. a WRITE function\
{\
"internalType": "string", ---> the data type\
"name": "_newName", ---> params name\
"type": "string" ---> data type\
}\
],
"name": "changeName", ---> function name
"outputs": [], ---> it does not have a return property
"stateMutability": "nonpayable", ---> It changes the Blockchain State without Token exchange, it simply stores information.
"type": "function" ---> It is a function.
},
Copy
mkdir viem-example && cd viem-example
Copy
npm init -y
Copy
npm i viem
Copy
import { createPublicClient, createWalletClient, http } from "viem";
Copy
import { somniaTestnet } from "viem/chains"
Copy
const publicClient = createPublicClient({
chain: somniaTestnet,
transport: http(),
})
Copy
export const ABI = [//...ABI here]
Copy
import { ABI } from "./abi.js";
Copy
const CONTRACT_ADDRESS = "0x2e7f682863a9dcb32dd298ccf8724603728d0edd";
Copy
const interactWithContract = async () => {
try {
console.log("Reading message from the contract...");
// Read the "greet" function
const greeting = await publicClient.readContract({
address: CONTRACT_ADDRESS,
abi: ABI,
functionName: "greet",
});
console.log("Current greeting:", greeting);
} catch (error) {
console.error("Error interacting with the contract:", error);
}
};
interactWithContract();
Copy
node index.js
Copy
import { privateKeyToAccount } from "viem/accounts";
Copy
const walletClient = createWalletClient({
account: privateKeyToAccount($YOUR_PRIVATE_KEY),
chain: somniaTestnet,
transport: http(),
});
Copy
// Write to the "changeName" function
const txHash = await walletClient.writeContract({
address: CONTRACT_ADDRESS,
abi: ABI,
functionName: "changeName",
args: ["Emmanuel!"],
});
console.log("Transaction sent. Hash:", txHash);
console.log("Waiting for transaction confirmation...");
// Wait for the transaction to be confirmed
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
console.log("Transaction confirmed. Receipt:", receipt);
// Read the updated "greet" function
const updatedGreeting = await publicClient.readContract({
address: CONTRACT_ADDRESS,
abi: ABI,
functionName: "greet",
});
console.log("Updated greeting:", updatedGreeting);
Copy
node index.js
---
# Go-Live Checklist | Somnia Docs
This page provides a **checklist for deploying Somnia applications to production (Mainnet)**. It ensures that all necessary environment variables, contract addresses, allowlists, and rollback strategies are correctly configured before launch.
circle-exclamation
Never commit secrets to git. Use encrypted secret storage or CI secret managers for production deployments.
* * *
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/go-live-checklist#environment-variables)
Environment Variables
* Store RPC URLs, private keys, and API keys in `.env` files (never commit to git).
* Define `SOMNIA_RPC_MAINNET` and `SOMNIA_RPC_TESTNET` for switching between environments.
* Configure block explorer API keys (for contract verification).
* Separate `.env.production` vs `.env.development`.
* Double-check secrets with `printenv | grep SOMNIA`.
Example `.env.production`:
.env.production
Copy
SOMNIA_RPC_MAINNET=https://api.infra.mainnet.somnia.network/
PRIVATE_KEY=0xabc123...
EXPLORER_API_KEY=...
ALLOWED_ORIGIN=https://yourapp.com
* * *
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/go-live-checklist#contract-addresses)
Contract Addresses
* Verify all **core Somnia system contracts** (e.g., wrapped SOMI, multicall, registry).
* Update deployed contract addresses in `.env` or config files.
* Confirm that addresses match **Mainnet deployments** (not testnet).
* Cross-check with block explorer for correct bytecode & verification.
Example `config/addresses.json`:
* * *
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/go-live-checklist#allowlists)
Allowlists
* Maintain allowlists for admin roles, multisigs, and privileged addresses.
* Use multisig wallets ([Safearrow-up-right](https://safe.somnia.network/welcome)
) for critical roles (owner, pauser, upgrader).
* Double-check allowlist in contracts (no dev/test keys).
* Store allowlist in version control (JSON/YAML).
Example `allowlist.json`:
* * *
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/go-live-checklist#rollback-plan)
Rollback Plan
Having a clear rollback strategy is essential if something goes wrong during or after deployment. This should include both **technical measures** and **operational procedures**.
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/go-live-checklist#technical-rollback-steps)
Technical Rollback Steps
* **Pause contracts** – ensure critical contracts implement a `pause()` function for emergencies.
* **Feature flags** – enable/disable risky features without redeployment.
* **Upgradable contracts** – if using proxies, keep previous implementation verified and ready.
* **Role revocation** – remove compromised keys or revoke admin privileges quickly.
* **Migration scripts** – maintain scripts to redeploy or roll forward to a safe version.
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/go-live-checklist#operational-rollback-steps)
Operational Rollback Steps
* **Communication plan** – inform users immediately via Discord, Twitter, or status page.
* **Validator coordination** – notify Somnia infra providers/validators in case of critical incidents.
* **Emergency access** – keep a secure list of multisig signers available for urgent transactions.
* **Data backups** – keep copies of config files, [subgrapharrow-up-right](https://subgraph.somnia.network/)
schemas, and off-chain DBs.
* **Post-mortem process** – document the issue, resolution, and future prevention steps.
* * *
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/go-live-checklist#best-practices)
Best Practices
* Always test on **Shannon Testnet** before mainnet deployment.
* Pin dependencies & compiler versions for reproducibility.
* Run **audit checklist** (see Smart Contract Security 101).
* Monitor deployed contracts with logging & health checks.
* Communicate launch windows to your community in advance.
[PreviousDeployment and Productionchevron-left](https://docs.somnia.network/developer/deployment-and-production)
[NextExplorer API Health and Monitoringchevron-right](https://docs.somnia.network/developer/deployment-and-production/explorer-api-health-and-monitoring)
Last updated 1 month ago
* [Environment Variables](https://docs.somnia.network/developer/deployment-and-production/go-live-checklist#environment-variables)
* [Contract Addresses](https://docs.somnia.network/developer/deployment-and-production/go-live-checklist#contract-addresses)
* [Allowlists](https://docs.somnia.network/developer/deployment-and-production/go-live-checklist#allowlists)
* [Rollback Plan](https://docs.somnia.network/developer/deployment-and-production/go-live-checklist#rollback-plan)
* [Best Practices](https://docs.somnia.network/developer/deployment-and-production/go-live-checklist#best-practices)
config/addresses.json
Copy
{
"network": "mainnet",
"dao": "0x1234...",
"token": "0xabcd...",
"subgraph": "https://subgraph.somnia.network/dashboard/subgraph/..."
}
allowlist.json
Copy
{
"admins": ["0xAdmin1...", "0xAdmin2..."],
"oracles": ["0xOracle1..."],
"relayers": ["0xRelayer1..."]
}
---
# Deploy with Hardhat | Somnia Docs
Various developer tools can be used to build on Somnia to enable the Somnia mission of empowering developers to build Mass applications. One such development tool is Hardhat. [Hardhatarrow-up-right](https://hardhat.org/)
is a development environment for the EVM i.e. Somnia. It consists of different components for editing, compiling, debugging, and deploying your smart contracts and dApps, all working together to create a complete development environment. This guide will teach you how to deploy a “Buy Me Coffee” Smart Contract to the Somia Network using Hardhat Development tools.
circle-check
Somnia Mainnet is LIVE. To deploy on Somnia Mainnet, you will need SOMI Tokens. Please refer to the [guide](https://docs.somnia.network/get-started/getting-started-for-mainnet)
on Moving from Testnet to Mainnet.
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-hardhat#pre-requisites)
Pre-requisites
------------------------------------------------------------------------------------------------------------------------------
1. This guide is not an introduction to Solidity Programming; you are expected to understand Basic Solidity Programming.
2. To complete this guide, you will need MetaMask installed and the Somnia Network added to the list of Networks. If you have yet to install MetaMask, please follow this guide to [Connect Your Wallet](https://docs.somnia.network/get-started/connect-your-wallet-to-mainnet)
.
3. Hardhat is installed and set up on your local machine. See [Guidearrow-up-right](https://hardhat.org/hardhat-runner/docs/getting-started#installation)
.
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-hardhat#initialise-hardhat-project)
Initialise Hardhat Project
------------------------------------------------------------------------------------------------------------------------------------------------------
Start a new Hardhat project by running the following command in your Terminal:
This will give you a series of prompts. Select the option to “Create a TypeScript Project (with Viem)”

This will install the required dependencies for your project. Once the installation is complete, open the project directory and check the directories where you will find the \`contracts\` directory. This is where the Smart Contract will be added.
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-hardhat#create-the-smart-contract)
Create the Smart Contract
----------------------------------------------------------------------------------------------------------------------------------------------------
Open the Smart Contracts folder and delete the default `Lock.sol` file. Create a new file, `BuyMeCoffee.sol` and paste the following code:
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-hardhat#compile-the-smart-contract)
Compile the Smart Contract
------------------------------------------------------------------------------------------------------------------------------------------------------
To compile your contracts, you need to customize the Solidity compiler options, open the `hardhat.config.js` file and ensure the Solidity version is `0.8.28` and then run the command:
It will return the response:
This will compile the Solidity file and convert the Solidity code into machine-readable bytecode. By default, the compiled _artifacts_ will be saved in the newly created `artifacts` directory. The next step is to deploy the contracts to the Somnia Network. In Hardhat, deployments are defined through **Ignition Modules**. These modules are abstractions that describe a deployment, specifically, JavaScript functions that process the file you want to deploy. Open the `ignition` directory inside the project root's directory, then enter the directory named `modules`. Delete the `Lock.ts` file. Create a `deploy.ts` file and paste the following code:
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-hardhat#deploy-contract)
Deploy Contract
--------------------------------------------------------------------------------------------------------------------------------
Open the `hardhat.config.js` file and update the network information by adding Somnia Network to the list of networks. Copy your Wallet Address Private Key from MetaMask, and add it to the **accounts** section. Ensure there are enough STT Token in the Wallet Address to pay for Gas. You can get some from the Somnia [Faucetarrow-up-right](https://devnet.somnia.network/)
.
> The "**0xPRIVATE\_KEY**" is used to sign the Transaction from your EOA without permission. When deploying the smart contract, you must ensure the EOA that owns the Private Key is funded with enough STT Tokens to pay for gas. Follow this [guidearrow-up-right](https://support.metamask.io/managing-my-wallet/secret-recovery-phrase-and-private-keys/how-to-export-an-accounts-private-key/)
> to get your Private Key on MetaMask.
Open a new terminal and deploy the smart contract to the Somnia Network. Run the command:
You will see a confirmation message asking if you want to deploy to the Somnia Network. Answer by hitting “**y**” on your keyboard. This will confirm the deployment of the Smart Contract to the Somnia Network.

Congratulations. 🎉 You have deployed your “BuyMeCoffee” Smart Contract to the Somnia Network using Hardhat. 🎉
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-hardhat#verify-your-smart-contract)
**Verify Your Smart Contract**
----------------------------------------------------------------------------------------------------------------------------------------------------------
After deploying your contract, you can verify it using the [Hardhat Verify pluginarrow-up-right](https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-verify)
. This allows your source code to be visible and validated on the [Somnia Explorerarrow-up-right](https://shannon-explorer.somnia.network/)
.
####
[hashtag](https://docs.somnia.network/developer/development-frameworks/deploy-with-hardhat#update-hardhat.config.tsadd-the-following-to-your-config-file)
Update `hardhat.config.ts`Add the following to your config file:
> Store your private key in a `.env` file and import it securely to avoid hardcoding.
After deploying your contract, run the Verify command. Copy the deployed address and run:
Example for a contract with one string constructor arg:
Visit the [Somnia Explorerarrow-up-right](https://shannon-explorer.somnia.network/)
and search for your contract address. If successful, the source code will appear under the **“Contract”** tab and show as **verified**.


The verified Smart Contracts contain the Source Code, which anyone can review for bugs and malicious code. Users can also connect with and interact with the Verified Smart Contract.
[PreviousDeploy with Foundrychevron-left](https://docs.somnia.network/developer/development-frameworks/deploy-with-foundry)
[NextUsing the Viem Librarychevron-right](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library)
Last updated 1 month ago
* [Pre-requisites](https://docs.somnia.network/developer/development-frameworks/deploy-with-hardhat#pre-requisites)
* [Initialise Hardhat Project](https://docs.somnia.network/developer/development-frameworks/deploy-with-hardhat#initialise-hardhat-project)
* [Create the Smart Contract](https://docs.somnia.network/developer/development-frameworks/deploy-with-hardhat#create-the-smart-contract)
* [Compile the Smart Contract](https://docs.somnia.network/developer/development-frameworks/deploy-with-hardhat#compile-the-smart-contract)
* [Deploy Contract](https://docs.somnia.network/developer/development-frameworks/deploy-with-hardhat#deploy-contract)
* [Verify Your Smart Contract](https://docs.somnia.network/developer/development-frameworks/deploy-with-hardhat#verify-your-smart-contract)
Copy
npx hardhat init
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract BuyMeCoffee {
event CoffeeBought(
address indexed supporter,
uint256 amount,
string message,
uint256 timestamp
);
address public owner;
struct Contribution {
address supporter;
uint256 amount;
string message;
uint256 timestamp;
}
Contribution[] public contributions;
constructor() {
owner = msg.sender;
}
function buyCoffee(string memory message) external payable {
require(msg.value > 0, "Amount must be greater than zero.");
contributions.push(
Contribution(msg.sender, msg.value, message, block.timestamp)
);
emit CoffeeBought(msg.sender, msg.value, message, block.timestamp);
}
function withdraw() external {
require(msg.sender == owner, "Only the owner can withdraw funds.");
payable(owner).transfer(address(this).balance);
}
function getContributions() external view returns (Contribution[] memory) {
return contributions;
}
function setOwner(address newOwner) external {
require(msg.sender == owner, "Only the owner can set a new owner.");
owner = newOwner;
}
}
Copy
npx hardhat compile
Copy
Compiling...
Compiled 1 contract successfully
Copy
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
const BuyMeCoffee = buildModule("BuyMeCoffee", (m) => {
const contract = m.contract("BuyMeCoffee");
return { contract };
});
module.exports = BuyMeCoffee;
Copy
module.exports = {
// ...
networks: {
somnia: {
url: "https://dream-rpc.somnia.network",
accounts: ["0xPRIVATE_KEY"], // put dev menomonic or PK here,
},
},
// ...
};
Copy
npx hardhat ignition deploy ./ignition/modules/deploy.ts --network somnia
Copy
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
const config: HardhatUserConfig = {
solidity: "0.8.28",
networks: {
somnia: {
url: "https://dream-rpc.somnia.network",
accounts: ["YOUR_PRIVATE_KEY"],
},
},
sourcify: {
enabled: false,
},
etherscan: {
apiKey: {
somnia: "empty",
},
customChains: [\
{\
network: "somnia",\
chainId: 50312,\
urls: {\
apiURL: "https://shannon-explorer.somnia.network/api",\
browserURL: "https://shannon-explorer.somnia.network",\
},\
},\
],
},
};
Copy
npx hardhat verify --network somnia DEPLOYED_CONTRACT_ADDRESS "ConstructorArgument1" ...
Copy
npx hardhat verify --network somnia 0xYourContractAddress "YourDeployerWalletAddress"
---
# Create ERC20 Tokens | Somnia Docs
The Somnia mission is to enable the development of mass-consumer real-time applications. To achieve this as a developer, you will need to build applications that are Token enabled, as this is a requirement for many Blockchain applications. This guide will teach you how to connect to and deploy your ERC20 Smart Contract to the Somia Network using the [Remix IDEarrow-up-right](https://remix.ethereum.org/)
.
circle-check
Somnia Mainnet is LIVE. To deploy on Somnia Mainnet, you will need SOMI Tokens. Please refer to the [guide](https://docs.somnia.network/get-started/getting-started-for-mainnet)
on Moving from Testnet to Mainnet.
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc20-tokens#pre-requisite)
Pre-requisite
------------------------------------------------------------------------------------------------------------------------------------
1. This guide is not an introduction to Solidity Programming; you are expected to understand Basic Solidity Programming.
2. To complete this guide, you will need MetaMask installed and the Somnia Network added to the list of Networks. If you have yet to install MetaMask, please follow this guide to [Connect Your Wallet](https://docs.somnia.network/get-started/connect-your-wallet-to-mainnet)
.
Somnia Network is an EVM-compatible Layer 1 Blockchain. This means that critical implementation on Ethereum is available on Somnia, with higher throughput and faster finality. Smart Contract that follows the [ERC-20 standardarrow-up-right](https://eips.ethereum.org/EIPS/eip-20)
is an ERC-20 token; these Smart Contracts are often referred to as Token Contracts.
ERC-20 tokens provide functionality to
* Transfer tokens
* Allow others to transfer tokens on behalf of the token holder
It is important to note that ERC20 Smart Contracts are different from the Native Somnia Contracts, which are used to pay gas fees when transacting on Somnia. In the following steps, we will create an ERC20 Token by following the EIP Standard and also demonstrate the option to use a Library to create the ERC20 Token.
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc20-tokens#ierc-20)
IERC-20
------------------------------------------------------------------------------------------------------------------------
According to the EIP Standard, certain Smart Contract methods must be implemented adhering to the standard so that other Smart Contracts can interact with the deployed Smart Contracts and call the method. To achieve this, we will use the Solidity Interface Type to create the Smart Contract standard.
In Solidity, an interface is a special contract that defines a set of function signatures without implementation. It acts as a "blueprint" for other contracts, ensuring they adhere to a specific structure. Interfaces are crucial for creating standards, such as the ERC-20 token standards, allowing different contracts to interact seamlessly within the EVM ecosystem.
Create an Interface for the ERC20 Token. Copy and paste the code below into a file named `IERC20.sol`
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc20-tokens#erc-20-token-contract)
ERC-20 Token Contract
----------------------------------------------------------------------------------------------------------------------------------------------------
With the Interface created and all the Token standard methods implemented, the next step is to import the Interface into the ERC20 Smart Contract implementation.
Create a file called `ERC20.sol` and paste the code below into it.
We have implemented the various requirements for the ERC20 Token Standard:
`**constructor**`: Initializes the token's basic properties.
Accepts the token's `name`, `symbol`, and the number of `decimals` as parameters and sets these values as the token's immutable metadata.
Example: `ERC20("MyToken", "MTK", 18)` initializes a token named `MyToken` with the symbol
`MTK` and 18 decimal places (the standard for ERC-20).
###
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc20-tokens#functions)
Functions
`**transfer**`: Moves a specified `amount` of tokens from the _sender_ to a _recipient_.
It checks if the sender has enough balance and deducts the amount from the sender's balance, and adds it to the recipient's. It also **emits a Transfer event** to log the transaction and returns \`**true**\` if the transfer is successful.
`**approve**`: It allows a _spender_ to spend up to a certain amount of tokens on behalf of the _owner_. It sets the `allowance` for the _spender_ to the specified `amount`. **The spender is usually another Smart Contract**. It then **emits an Approval event** to record the spender's `allowance`. It returns **true** if the `approval` is successful. A common use case is to enable spending through third-party contracts like decentralized exchanges (DEXs).
`**transferFrom**`: It allows a _spender_ to transfer tokens on behalf of another account.
It checks if the _sender_ has an approved `allowance` for the spender and ensures it is sufficient for the _amount_ and deducts the amount from the `allowance` and the sender's _balance_ and adds the _amount_ to the recipient's balance. It also **emits a Transfer event** to log the transaction. It returns \`**true**\` if the transfer is successful. A common use case is DEXs or smart contracts to handle token transactions on behalf of users.
`**_mint**`: Creates new tokens and adds them to a specified account's _balance_.
Calling the method increases the recipient's `**balanceOf**` by the specified amount. It also increases the `**totalSupply**` of the ERC20 Tokens by the same amount. A transfer event with the from address as address(0) (indicating tokens are created).
`**_burn**`: Destroys a specified number of _tokens_ from an account's _balance_.
It reduces the account's `**balanceOf**` and decreases the `**totalSupply**` by the amount. It emits a Transfer event with the to address as address(0) (indicating tokens are burned).
It is typically implemented in token-burning mechanisms to reduce supply, increasing scarcity.
`**mint**`: Public wrapper for `**_mint**`. It calls `**_mint**` to create new tokens for a specified to address. Allows the contract owner or authorized accounts to mint new tokens.
`**burn**`: Public wrapper for `**_burn**`. Calls \_burn to destroy tokens from a specified from address.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc20-tokens#events)
Events
`**Transfer**`:
Logs token transfers, including minting and burning events. Parameters: from (sender), to (recipient), value (amount).
`**Approval**`:
Logs approvals of allowances for spenders. Parameters: owner, spender, value (allowance amount).
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc20-tokens#compile-smart-contract)
Compile Smart Contract
------------------------------------------------------------------------------------------------------------------------------------------------------
The ERC20 Token is now ready to be deployed to the Somnia Blockchain.
On the left tab, click the “Solidity Compiler” menu item and then the “ Compile ERC20.sol” button. This will compile the Solidity file and convert the Solidity code into machine-readable bytecode.

[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc20-tokens#deploy-smart-contract)
Deploy Smart Contract
----------------------------------------------------------------------------------------------------------------------------------------------------
The Smart Contract has been created and compiled into ByteCode, and the ABI has also been created. The next step is to deploy the Smart Contract to the Somnia DevNet so that you can perform READ and WRITE operations.
On the left tab, click the “Deploy and run transactions” menu item. To deploy the Smart Contract, we will require a wallet connection. In the Environment dropdown, select the option: “Injected Provider - MetaMask”. Then select the MetaMask account where you have STT Tokens.
In the “DEPLOY” field, enter the property values for the ERC20 Token:
* “\_NAME” - type string
* “\_SYMBOL” - type string
* “\_DECIMALS” - type uint8
Click Deploy.
When prompted, approve the Contract deployment on your MetaMask.

Look at the terminal for the response and the deployed Smart Contract address. You can interact with the Smart Contract via the Remix IDE. Send a transaction to change the name.

Congratulations. 🎉 You have deployed an ERC20 Smart Contract to the Somnia Network. 🎉
* * *
###
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc20-tokens#openzeppelin)
OpenZeppelin
As was mentioned at the beginning of this Tutorial, there is the option to use a Library to create the ERC20 Token. The OpenZeppelin Smart Contract Library can be used to create an ERC20 Token, and developers can rely on the Smart Contracts wizard to specify particular properties for the created Token. See an example below:
The process for deploying this Smart Contract implementation built with OpenZeppelin is the same as Steps 3 and 4 above.
[PreviousTokens and NFTschevron-left](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts)
[NextCreate ERC721 NFT Collectionschevron-right](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections)
Last updated 1 month ago
* [Pre-requisite](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc20-tokens#pre-requisite)
* [IERC-20](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc20-tokens#ierc-20)
* [ERC-20 Token Contract](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc20-tokens#erc-20-token-contract)
* [Functions](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc20-tokens#functions)
* [Events](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc20-tokens#events)
* [Compile Smart Contract](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc20-tokens#compile-smart-contract)
* [Deploy Smart Contract](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc20-tokens#deploy-smart-contract)
* [OpenZeppelin](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc20-tokens#openzeppelin)
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount)
external
returns (bool);
function allowance(address owner, address spender)
external
view
returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount)
external
returns (bool);
}
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import "./IERC20.sol";
contract ERC20 is IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(
address indexed owner, address indexed spender, uint256 value
);
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
string public name;
string public symbol;
uint8 public decimals;
constructor(string memory _name, string memory _symbol, uint8 _decimals) {
name = _name;
symbol = _symbol;
decimals = _decimals;
}
function transfer(address recipient, uint256 amount)
external
returns (bool)
{
balanceOf[msg.sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(msg.sender, recipient, amount);
return true;
}
function approve(address spender, uint256 amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transferFrom(address sender, address recipient, uint256 amount)
external
returns (bool)
{
allowance[sender][msg.sender] -= amount;
balanceOf[sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(sender, recipient, amount);
return true;
}
function _mint(address to, uint256 amount) internal {
balanceOf[to] += amount;
totalSupply += amount;
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal {
balanceOf[from] -= amount;
totalSupply -= amount;
emit Transfer(from, address(0), amount);
}
function mint(address to, uint256 amount) external {
_mint(to, amount);
}
function burn(address from, uint256 amount) external {
_burn(from, amount);
}
}
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import "@openzeppelin/[email protected]/token/ERC20/ERC20.sol";
import "@openzeppelin/[email protected]/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/[email protected]/access/Ownable.sol";
contract MyToken is ERC20, ERC20Burnable, Ownable {
constructor(address initialOwner)
ERC20("MyToken", "MTK")
Ownable()
{}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
}
---
# Support and Community | Somnia Docs
[General FAQschevron-right](https://docs.somnia.network/developer/deployment-and-production/support-and-community/general-faqs)
[Developer FAQschevron-right](https://docs.somnia.network/developer/deployment-and-production/support-and-community/developer-faqs)
[PreviousAPIschevron-left](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/apis)
[NextGeneral FAQschevron-right](https://docs.somnia.network/developer/deployment-and-production/support-and-community/general-faqs)
---
# Build a Realtime On-Chain Game | Somnia Docs
This tutorial shows how to build a Tap-to-Play Onchain Game using Somnia Data Streams, where every player’s tap is written directly to the blockchain and the leaderboard updates in realtime.
Each tap is stored onchain as a structured data record following a schema. The game uses MetaMask for wallet identity and Somnia Streams SDK to:
* Store tap events onchain using `sdk.streams.set()`
* Retrieve and rank all players from onchain data
By the end of this guide, you’ll have: - A working Next.js app - Onchain data storage using Somnia Data Streams - A live leaderboard that reads blockchain state - MetaMask integration for identity and transaction signing
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#prerequisites)
Prerequisites
---------------------------------------------------------------------------------------------------------------------------------------
* Node.js 20+
* A funded Somnia Testnet wallet. Kindly get some from the [Faucetarrow-up-right](https://testnet.somnia.network/)
* Basic familiarity with TypeScript and Next.js
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#project-setup)
Project Setup
---------------------------------------------------------------------------------------------------------------------------------------
Initialize a new Next.js app and install dependencies. Create the app by creating a directory where the app will live
Copy
npx create-next-app@latest somnia-chat --ts --app --no-tailwind
cd somnia-chat
Install the [Somnia Streamsarrow-up-right](https://www.npmjs.com/package/@somnia-chain/streams)
and ViemJS dependencies
Create a .env.local file for storing secrets and environmental variables
triangle-exclamation
Never expose PRIVATE\_KEY to the browser. Keep all publishing code in API routes or server code only. NOTE: You can connect a Privy Wallet (or equivalent) to the SDK, avoiding the need entirely for private keys.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#chain-configuration)
Define Tap Schema
-------------------------------------------------------------------------------------------------------------------------------------------------
Create a file `lib/schema.ts`:
This schema defines the structure of each tap event:
* `timestamp`: when the tap occurred
* `player`: who tapped (wallet address)
* A `nonce` will be added when the schema is deployed to ensures each record is unique
The Schema ID will be automatically computed by the SDK from this schema.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#setup-clients)
Setup Clients
---------------------------------------------------------------------------------------------------------------------------------------
Create `lib/serverClient.ts` for server-side reads:
Create `lib/clients.ts` for client-side access:
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#writing-tap-data-onchain)
Writing Tap Data Onchain
-------------------------------------------------------------------------------------------------------------------------------------------------------------
Each tap is recorded onchain with the `sdk.streams.set()` method. In this section, we’ll walk through how each part of the `sendTap()` logic works from wallet connection to writing structured schema data onchain.
* * *
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#set-up-state-variables)
**Set up state variables**
We’ll start by tracking a number of states, such as:
* the connected wallet address
* the wallet client (MetaMask connection)
* and a few helper states for loading, cooldowns, and errors.
These ensure that you can access the connected wallet address (`address`) and track transaction state (`pending`). It also prevents spam taps with a 1-second cooldown (`cooldownMs`)
* * *
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#connect-metamask)
**Connect MetaMask**
We use the browser’s `window.ethereum` API to connect to MetaMask. Once connected, we create a **wallet client** that Somnia’s SDK can use for signing transactions.
`createWalletClient` from Viem wraps MetaMask into a signer object that the Somnia SDK can use. This is how the UI and the blockchain are bridged securely.
* * *
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#initialize-the-sdk)
**Initialize the SDK**
The **Somnia Data Streams SDK** provides methods to compute schema IDs, encode structured data, and publish to the blockchain. We initialize it with both the **public client** (for chain access) and the **wallet client** (for signing transactions).
This gives you full read/write access to the Somnia Streams contract on Somnia Testnet.
* * *
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#compute-the-schema-id)
**Compute the Schema ID**
Schemas define how the onchain data is structured. In this case, the tap schema looks like this:
Before writing data, we must compute its **unique Schema ID**:
This produces a deterministic ID derived from the schema text, ensuring that any app using the same schema can read or decode your data.
* * *
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#register-the-schema)
**Register the Schema**
If this schema wasn’t registered yet, we register it once. It’s safe to call this before sending the first message.
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#encode-the-data)
**Encode the Data**
Somnia Streams stores structured data using its `SchemaEncoder` class. We create an encoder and provide each field according to the schema definition.
This converts your JavaScript values into the precise binary format that can be stored onchain and later decoded.
* * *
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#generate-a-unique-data-id)
**Generate a Unique Data ID**
Each record needs a **unique identifier** within the schema. We use the `keccak256` hash of the player’s address and timestamp to ensure that it is packed into 32 bits of data.
This ensures no two taps collide, even if the same player taps rapidly.
* * *
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#store-the-tap-onchain)
**Store the Tap Onchain**
Finally, we push the structured data to the blockchain using:
The `set()` method writes one or more records (called _Data Streams_) to the chain. Each record is cryptographically signed by the player’s wallet, and gets stored on Somnia’s decentralized data infrastructure. It can also be retrieved instantly using the same schema
* * *
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#manage-cooldowns-and-feedback)
**Manage Cooldowns and Feedback**
After the tap is sent, we apply a 1-second cooldown to avoid flooding transactions and reset the pending state.
This gives players a smooth UX while maintaining blockchain transaction integrity.
* * *
####
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#putting-it-all-together)
Putting It All Together
Here’s the complete `sendTap()` method with all steps combined:
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#complete-page.tsx-code)
Complete \`page.tsx\` Code
chevron-rightpage.tsx[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#page.tsx)
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#reading-leaderboard-data-onchain)
Reading Leaderboard Data Onchain
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
The leaderboard is calculated server-side by reading all tap data stored onchain. Create a `lib/store.ts` file and add the following code:
The leaderboard logic begins inside the `getLeaderboard()` function, where we use the **SDK** to read structured tap data directly from the blockchain. First, the function initializes the SDK with a **server-compatible public client**, which allows read-only access to the chain without a connected wallet. The next step computes the `schemaId` by passing our `tapSchema` to `sdk.streams.computeSchemaId()`. This produces a deterministic identifier that ensures we’re always referencing the correct data structure.
Once the `schemaId` is known, the core operation happens through `sdk.streams.getAllPublisherDataForSchema(schemaId, publisher)`. This method queries the blockchain for all records written by the specified publisher under that schema. Each returned record is an array of fields that align with the schema’s definition, in this case `[timestamp, player]`. The helper function `val()` is then used to unwrap nested field values (`f?.value?.value`) from the SDK’s response format, giving us clean, readable values.
`getAllPublisherDataForSchema` acts like a decentralized “SELECT \* FROM” query, fetching all onchain data tied to a schema and publisher, while the rest of the function transforms that raw blockchain data into a structured leaderboard the app can display.
* * *
Creat a api route to retrieve Leaderboard score. Create the file `app/api/leaderboard/route.ts`
This endpoint imports the `getLeaderboard()` function from `lib/store.ts`, which handles the heavy lifting of querying Somnia Data Streams, and then exposes that onchain data as a clean, JSON-formatted response for your application. The client simply fetches the leaderboard via `/api/leaderboard`.
The page.tsx fetches /api/leaderboard every few seconds to stay updated.
* * *
Every tap executes a real blockchain transaction:
Field
Description
timestamp
Time of the tap
player
Wallet address of the player
When the `set()` call succeeds, Somnia Data Streams stores the record and indexes it under your publisher’s address. Any application (including yours) can then read this data and build dashboards, analytics, or game leaderboards.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#run-the-app)
Run the App
-----------------------------------------------------------------------------------------------------------------------------------
Open [http://localhost:3000arrow-up-right](http://localhost:3000/)
and connect your MetaMask wallet. Click 🖱️ Tap to send onchain transactions, and watch your leaderboard update live.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#conclusion)
Conclusion
---------------------------------------------------------------------------------------------------------------------------------
You’ve built a fully onchain game where player interactions are stored via Somnia Data Streams and leaderboard rankings are derived from immutable blockchain data. MetaMask provides secure, user-friendly authentication. This same pattern powers realtime Web3 experiences, from social apps to competitive games, using Somnia’s high-performance onchain data infrastructure.
[PreviousBuild a Minimal On-Chain Chat Appchevron-left](https://docs.somnia.network/developer/data-streams/tutorials/build-a-minimal-on-chain-chat-app)
[NextSmart Contractschevron-right](https://docs.somnia.network/developer/smart-contracts)
Last updated 1 month ago
* [Prerequisites](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#prerequisites)
* [Project Setup](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#project-setup)
* [Define Tap Schema](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#chain-configuration)
* [Setup Clients](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#setup-clients)
* [Writing Tap Data Onchain](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#writing-tap-data-onchain)
* [Set up state variables](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#set-up-state-variables)
* [Connect MetaMask](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#connect-metamask)
* [Initialize the SDK](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#initialize-the-sdk)
* [Compute the Schema ID](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#compute-the-schema-id)
* [Register the Schema](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#register-the-schema)
* [Encode the Data](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#encode-the-data)
* [Generate a Unique Data ID](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#generate-a-unique-data-id)
* [Store the Tap Onchain](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#store-the-tap-onchain)
* [Manage Cooldowns and Feedback](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#manage-cooldowns-and-feedback)
* [Complete \`page.tsx\` Code](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#complete-page.tsx-code)
* [Reading Leaderboard Data Onchain](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#reading-leaderboard-data-onchain)
* [Run the App](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#run-the-app)
* [Conclusion](https://docs.somnia.network/developer/data-streams/tutorials/build-a-realtime-on-chain-game#conclusion)
Copy
npm i @somnia-chain/streams viem
Copy
NEXT_PUBLIC_PUBLISHER_ADDRESS=0xb6e4fa6ff2873480590c68D9Aa991e5BB14Dbf03
NEXT_PUBLIC_RPC_URL=https://dream-rpc.somnia.network
Copy
// lib/schema.ts
export const tapSchema = 'uint64 timestamp, address player'
Copy
// lib/serverClient.ts
import { createPublicClient, http } from 'viem'
import { somniaTestnet } from 'viem/chains'
export function getServerPublicClient() {
return createPublicClient({
chain: somniaTestnet,
transport: http(process.env.RPC_URL || 'https://dream-rpc.somnia.network'),
})
}
Copy
// lib/clients.ts
'use client'
import { createPublicClient, http } from 'viem'
import { somniaTestnet } from 'viem/chains'
export function getPublicHttpClient() {
return createPublicClient({
chain: somniaTestnet,
transport: http(process.env.NEXT_PUBLIC_RPC_URL || 'https://dream-rpc.somnia.network'),
})
}
Copy
const [address, setAddress] = useState('')
const [walletClient, setWalletClient] = useState(null)
const [cooldownMs, setCooldownMs] = useState(0)
const [pending, setPending] = useState(false)
const [error, setError] = useState('')
Copy
async function connectWallet() {
if (typeof window !== "undefined" && window.ethereum !== undefined)
try {
await window.ethereum.request({ method: "eth_requestAccounts" });
const walletClient = createWalletClient({
chain: somniaDream,
transport: custom(window.ethereum),
});
const [account] = await walletClient.getAddresses();
setWalletClient(walletClient)
setAddress(account)
} catch (e: any) {
setError(e?.message || String(e))
} setWalletClient(wallet)
}
Copy
const sdk = new SDK({
public: getPublicHttpClient(),
wallet: walletClient,
})
Copy
tapSchema = 'uint64 timestamp, address player'
Copy
const schemaId = await sdk.streams.computeSchemaId(tapSchema)
Copy
// Register schema
const schemaId = await sdk.streams.computeSchemaId(chatSchema)
const isRegistered = await sdk.streams.isDataSchemaRegistered(schemaId)
if (!isRegistered) {
const ignoreAlreadyRegistered = true
const txHash = await sdk.streams.registerDataSchemas(
[{ schemaName: 'tap', schema: tapSchema, parentSchemaId: zeroBytes32 }],
ignoreAlreadyRegistered
)
if (!txHash) throw new Error('Failed to register schema')
await waitForTransactionReceipt(getPublicHttpClient(), { hash: txHash })
}
Copy
const encoder = new SchemaEncoder(tapSchema)
const now = BigInt(Date.now())
const data = encoder.encodeData([\
{ name: 'timestamp', value: now, type: 'uint64' },\
{ name: 'player', value: address, type: 'address' },\
])
Copy
const id = keccak256(toHex(`${address}-${Number(nonce)}`))
Copy
await sdk.streams.set([{ id, schemaId, data }])
Copy
setCooldownMs(1000)
setPending(false)
Copy
async function sendTap() {
if (!walletClient || !address) return
setPending(true)
const sdk = new SDK({ public: getPublicHttpClient(), wallet: walletClient })
const schemaId = await sdk.streams.computeSchemaId(tapSchema)
const encoder = new SchemaEncoder(tapSchema)
const now = BigInt(Date.now())
const data = encoder.encodeData([\
{ name: 'timestamp', value: now, type: 'uint64' },\
{ name: 'player', value: address, type: 'address' },\
])
const id = keccak256(toHex(`${address}-${Number(now)}`))
await sdk.streams.set([{ id, schemaId, data }])
setCooldownMs(1000)
setPending(false)
}
Copy
'use client'
import { useState, useEffect, useRef } from 'react'
import { SDK, SchemaEncoder } from '@somnia-chain/streams'
import { getPublicHttpClient } from '@/lib/clients'
import { tapSchema } from '@/lib/schema'
import { keccak256, toHex, createWalletClient, custom } from 'viem'
import { somniaTestnet } from 'viem/chains'
export default function Page() {
const [address, setAddress] = useState('')
const [walletClient, setWalletClient] = useState(null)
const [leaderboard, setLeaderboard] = useState<{ address: string; count: number }[]>([])
const [cooldownMs, setCooldownMs] = useState(0)
const [pending, setPending] = useState(false)
const [error, setError] = useState('')
const lastNonce = useRef(0)
async function connectWallet() {
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' })
const wallet = createWalletClient({
chain: somniaTestnet,
transport: custom(window.ethereum),
})
setAddress(accounts[0])
setWalletClient(wallet)
}
async function sendTap() {
if (!walletClient || !address) return
setPending(true)
const sdk = new SDK({ public: getPublicHttpClient(), wallet: walletClient })
const schemaId = await sdk.streams.computeSchemaId(tapSchema)
const encoder = new SchemaEncoder(tapSchema)
const now = BigInt(Date.now())
const data = encoder.encodeData([\
{ name: 'timestamp', value: now, type: 'uint64' },\
{ name: 'player', value: address, type: 'address' },\
{ name: 'nonce', value: BigInt(lastNonce.current++), type: 'uint256' },\
])
const id = keccak256(toHex(`${address}-${Number(now)}`))
await sdk.streams.set([{ id, schemaId, data }])
setCooldownMs(1000)
setPending(false)
}
return (
}
)
}
function Leaderboard({ leaderboard }: { leaderboard: { address: string; count: number }[] }) {
if (!leaderboard.length) return
No taps yet
return (
{leaderboard.map((p, i) => (
#{i + 1} {p.address} — {p.count} taps
))}
)
}
Copy
lib/store.ts
import { SDK } from '@somnia-chain/streams'
import { getServerPublicClient } from './serverClient'
import { tapSchema } from './schema'
const publisher =
process.env.NEXT_PUBLIC_PUBLISHER_ADDRESS ||
'0x0000000000000000000000000000000000000000'
const val = (f: any) => f?.value?.value ?? f?.value
export async function getLeaderboard() {
const sdk = new SDK({ public: getServerPublicClient() })
const schemaId = await sdk.streams.computeSchemaId(tapSchema)
const rows = await sdk.streams.getAllPublisherDataForSchema(schemaId, publisher)
if (!Array.isArray(rows)) return []
const counts = new Map()
for (const row of rows) {
const player = String(val(row[1]) ?? '').toLowerCase()
if (!player.startsWith('0x')) continue
counts.set(player, (counts.get(player) || 0) + 1)
}
return Array.from(counts.entries())
.map(([address, count]) => ({ address, count }))
.sort((a, b) => b.count - a.count)
}
Copy
import { NextResponse } from 'next/server'
import { getLeaderboard } from '@/lib/store'
export async function GET() {
const leaderboard = await getLeaderboard()
return NextResponse.json({ leaderboard })
}
Copy
npm run dev
---
# Example Applications | Somnia Docs
This section showcases **hands-on examples** of real applications built on Somnia, from small experimental dApps to full scale and production ready projects.
Each example walks you through architecture, deployment, and integration patterns, helping you understand **how all the pieces fit together:** contracts, subgraphs, oracles, APIs, and SDKs.
You’ll find examples like:
* Token swap dApps (DEX)
* DAO Smart Contract
* DAO User Interface
> These examples are meant to inspire and guide you. Use them as blueprints, remix them, or extend them — and build the next great dApp on Somnia.
[PreviousUsing Verifiable Randomness (VRF)chevron-left](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf)
[NextDAO Smart Contractchevron-right](https://docs.somnia.network/developer/building-dapps/example-applications/dao-smart-contract)
Last updated 1 month ago
---
# Meme Coins | Somnia Docs
Launch and trade meme coins on Somnia Network. Earn passive income with Revenue Key NFTs. 1
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#buying-meme-coins)
Buying Meme Coins
-----------------------------------------------------------------------------------------------------------------------------------------------------------
1
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#connect-wallet)
Connect Wallet
Visit [somnia.memearrow-up-right](https://somnia.meme/)
and connect your Web3 wallet.
2
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#browse-tokens)
Browse Tokens
Explore available meme coins on the platform.
3
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#select-amount)
Select Amount
Choose how much you want to buy.
4
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#execute-trade)
Execute Trade
Confirm transaction in your wallet.
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#selling-meme-coins)
Selling Meme Coins
-------------------------------------------------------------------------------------------------------------------------------------------------------------
1
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#access-portfolio)
Access Portfolio
Go to your token holdings.
2
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#choose-token)
Choose Token
Select the meme coin you want to sell.
3
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#set-amount)
Set Amount
Enter how much to sell.
4
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#confirm-sale)
Confirm Sale
Complete the transaction.
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#how-to-launch-your-own-meme-coin)
How to Launch Your Own Meme Coin
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#step-by-step-launch-process)
Step-by-Step Launch Process
1
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#access-launch-page)
Access Launch Page
Click "Launch Token" on [somnia.memearrow-up-right](https://somnia.meme/)
2
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#set-token-details)
Set Token Details
* Token name (e.g., "DogeCoin2024")
* Token symbol (e.g., "DOGE24")
* Total supply
* Token description
3
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#configure-parameters)
Configure Parameters
* Initial price
* Liquidity settings
* Trading parameters
4
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#deploy)
Deploy
Pay deployment fees and confirm transaction.
5
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#your-token-goes-live)
Your Token Goes Live
Start trading immediately on the platform.
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#bonus-revenue-keys-for-passive-income)
Bonus: Revenue Keys for Passive Income
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#earn-from-platform-activity)
Earn from Platform Activity
Get Revenue Key NFTs to earn passive income from all platform trading fees: 1
* **Purple Key**: 28% revenue share, 1,000 supply
* **Blue Key**: 32% revenue share, 1,333 supply
* **Yellow Key**: 15% revenue share, 9,000 supply - **FREE** (complete 10 trades + launch a token) 1
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#how-to-get-keys)
How to Get Keys
1
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#buy-blue-key)
Buy Blue Key
Visit [somnia.meme/mintarrow-up-right](https://somnia.meme/mint)
(max 10 per transaction) 1
2
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#earn-yellow-key)
Earn Yellow Key
Trade 10 times + launch 1 token = FREE key 1
3
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#claim-rewards)
Claim Rewards
Visit [portal.somnia.memearrow-up-right](https://portal.somnia.meme/)
anytime 2
_Image placeholder: Revenue Keys overview_
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#faq)
FAQ
-------------------------------------------------------------------------------------------------------------------------------
chevron-rightHow much does it cost to launch a meme coin?[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#how-much-does-it-cost-to-launch-a-meme-coin)
3.5 SOMI + gas
chevron-rightCan I trade immediately after launching?[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#can-i-trade-immediately-after-launching)
Yes! Your token goes live instantly and can be traded right away.
chevron-rightWhat wallets work with Somnia Meme?[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#what-wallets-work-with-somnia-meme)
MetaMask, WalletConnect, and other Web3 wallets compatible with Somnia Network.
chevron-rightDo I need Revenue Keys to trade or launch?[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#do-i-need-revenue-keys-to-trade-or-launch)
No! Keys are optional for earning passive income from platform fees.
* * *
[PreviousElix.fichevron-left](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/elix.fi)
[NextNFTs2Mechevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/nfts2me)
Last updated 4 months ago
* [Buying Meme Coins](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#buying-meme-coins)
* [Selling Meme Coins](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#selling-meme-coins)
* [How to Launch Your Own Meme Coin](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#how-to-launch-your-own-meme-coin)
* [Bonus: Revenue Keys for Passive Income](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#bonus-revenue-keys-for-passive-income)
* [FAQ](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins#faq)
---
# NFTs2Me | Somnia Docs
NFTs2Me is a comprehensive no-code toolkit for creating, deploying, and managing NFT collections on any EVM blockchain. The platform offers everything from simple editions to complex generative art collections, all with advanced features like enforced royalties and token gating.
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/nfts2me#create-section-features)
Create Section Features
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/nfts2me#editions-artwork-multiple-nfts)
Editions (Artwork - Multiple NFTs)
The easiest way to start your NFT journey. Create collections where all NFTs share the same artwork. 3
1
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/nfts2me#create-an-edition)
Create an Edition
* Connect your wallet to NFTs2Me
* Click "Editions" from the create menu
* Set project name, token symbol, and description
* Upload artwork or generate using AI
* Set minting fee and edition size
* Deploy to your chosen blockchain
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/nfts2me#drop-multiple-artworks-multiple-nfts)
Drop (Multiple Artworks - Multiple NFTs)
Perfect when you have specific images for each NFT. Assign different artwork to each token in your collection.
**Key Features:**
* Upload multiple unique artworks
* Individual metadata for each NFT
* Customizable rarity traits
* Batch upload support
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/nfts2me#generative-art-combination-of-artworks)
Generative Art (Combination of Artworks)
Advanced mode for creating thousands of unique NFTs by combining image layers. 2
1
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/nfts2me#prepare-files)
Prepare files
* Create or upload PSD files with layers
2
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/nfts2me#configure-rarities)
Configure rarities
* Set rarity percentages for each trait
3
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/nfts2me#configure-combinations)
Configure combinations
* Configure layer combinations
4
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/nfts2me#auto-background-and-preview)
Auto-background and preview
* Generate auto-backgrounds
* Preview combinations in real-time
5
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/nfts2me#generate-collection)
Generate collection
* Generate complete collection
**Features:**
* Drag-and-drop layer management
* Rarity configuration per trait
* Unlimited preview generations
* Auto-background generation
* PSD file support
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/nfts2me#dashboard)
Dashboard
----------------------------------------------------------------------------------------------------------------------------------------
Powerful control center for managing your NFT collections with advanced features. 1
**Dashboard Capabilities:**
* Multi-phase sales management
* Airdrops and whitelists
* Snapshot tools
* Revenue tracking
* Collection analytics
* Minting page customization
* * *
Visit [nfts2me.comarrow-up-right](https://nfts2me.com/)
to start creating your NFT collection.
[PreviousMeme Coinschevron-left](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins)
[NextSomnia Domains (.somi)chevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-domains-.somi)
Last updated 4 months ago
* [Create Section Features](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/nfts2me#create-section-features)
* [Dashboard](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/nfts2me#dashboard)
---
# DAO UI Tutorial p1 | Somnia Docs
Somnia empowers developers to build applications for mass adoption. Smart Contracts deployed on the Somnia Blockchain will sometimes require building a User Interface. This guide will teach you how to build a user interface for a [DAO Smart Contract](https://docs.somnia.network/developer/building-dapps/example-applications/dao-smart-contract)
using Next.js and React Context. It is divided into three parts. At the end of this guide, you’ll learn how to:
1. Initialize a Next.js project.
2. Set up a global state using the Context API ([`**useContext**`arrow-up-right](https://react.dev/reference/react/useContext)
hook).
3. Add a global NavBar in `**_app.js**` so it appears on every page.
You will have a basic skeleton of a DApp, ready for **READ/WRITE** operations and UI components —topics we’ll cover in the subsequent articles.
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p1#pre-requisites)
Pre-requisites:
-------------------------------------------------------------------------------------------------------------------------------------------
1. This guide is not an introduction to JavaScript Programming; you are expected to understand JavaScript.
2. To complete this guide, you will need MetaMask installed and the Somnia Network added to the list of Networks. If you have yet to install MetaMask, please follow this guide to [Connect Your Walletarrow-up-right](https://codex.somnia.network/get-started/connect-your-wallet)
.
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p1#create-your-next.js-project)
Create Your Next.js Project
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
To create a NextJS project, run the command:
Copy
npx create-next-app my-dapp-ui
Accept the prompts and change directory into the folder after the build is completed.
This gives you a minimal Next.js setup with a pages folder (holding your routes), a public folder (for static assets), and config files.
####
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p1#optional-add-tailwind-css)
(Optional) Add Tailwind CSS
If you plan to style your app with Tailwind, install and configure it now:
Then edit your tailwind.config.js:
Finally, include Tailwind in styles/globals.css:
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p1#setting-up-a-react-context-for-global-state)
Setting Up a React Context for Global State
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
In many DApps, including this one, developers will manage **Wallet connection** and **State** globally so each page and component in the project can access it without repetitive code. This can be achieved by a [Contextarrow-up-right](https://react.dev/learn/passing-data-deeply-with-context)
following React patterns.
* Create a folder and name it `**contexts**` at the project root or inside `**pages**` directory.
* Inside it, create a file called `**walletcontext.js**` add the following code to the file:
chevron-rightwalletcontext.js[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p1#walletcontext.js)
We parse three class methods from React: `**createContext**`, `**useContext**`, and`**useState**`
`createContext` is used to create a [contextarrow-up-right](https://react.dev/learn/passing-data-deeply-with-context)
that components can provide or read. In this example, we assign `createContext` to the `**WalletContext**` variable and call the `**Provider**` method on each State and Function to make them available throughout the application.
`**useWallet()**` is a custom hook, so any page or component can do:
to access the global wallet state i.e. any of the `**Wallet.Provider**` methods
`**connectToMetaMask()**` triggers the MetaMask connection flow.
`**WalletProvider**` manages **State** and **Methods** in the application.
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p1#creating-a-global-navbar-in-_app.js)
Creating a Global NavBar in \_app.js
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Next.js automatically uses `**pages/_app.js**` to initialize every page. We will wrap the entire app in the `**WalletProvider**` inside `_app.js` and inject a `**NavBar**` menu that appears site-wide in the application
####
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p1#create-the-_app.js-and-add-the-code)
Create the `_app.js` and add the code:
`****` wraps the entire `****` tree so that every page can share the same wallet state.
`****` is placed above `****`, so it’s visible on all pages. We give `****` a pt-16 to avoid content hiding behind a fixed navbar.
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p1#navbar)
NavBar
--------------------------------------------------------------------------------------------------------------------------
Create a sub directory **components** and add a file called `**navbar.js**` Add the code:
It uses `**useWallet()**` to read the global `connected` and `address`states, and the `**disconnectWallet**` function. The truncated `address` is displayed if a user is logged into the App or “Not connected” otherwise. A **Logout** button calls `**disconnectWallet()**` to reset the global state.
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p1#test-your-setup)
Test Your Setup
--------------------------------------------------------------------------------------------------------------------------------------------
Start the dev server:
Open **http://localhost:3000** in a Web Browser. You should see your NavBar at the top.

Not Logged In

Logged In
Because we haven’t built any advanced pages yet, you will see a blank home page. The important part is that your **WalletContext** and global **NavBar** are in place and ready for the next steps.
* * *
###
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p1#id-5.-next-steps)
5\. Next Steps
* Article 2 shows you how to implement READ/WRITE operations (e.g., deposit, create proposals, vote, etc.) across different Next.js pages—using the same WalletContext to handle contract calls.
* Article 3 will focus on UI components, like forms, buttons, and event handling, tying it all together into a polished user interface.
Congratulations! You have a clean foundation, a Next.js project configured with Tailwind, a global context to manage wallet states, and a NavBar appearing across all routes. This sets the stage for adding contract interactions and advanced UI flows in the subsequent articles. Happy building!
[PreviousDAO Smart Contractchevron-left](https://docs.somnia.network/developer/building-dapps/example-applications/dao-smart-contract)
[NextDAO UI Tutorial p2chevron-right](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2)
Last updated 10 months ago
* [Pre-requisites:](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p1#pre-requisites)
* [Create Your Next.js Project](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p1#create-your-next.js-project)
* [Setting Up a React Context for Global State](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p1#setting-up-a-react-context-for-global-state)
* [Creating a Global NavBar in \_app.js](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p1#creating-a-global-navbar-in-_app.js)
* [NavBar](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p1#navbar)
* [Test Your Setup](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p1#test-your-setup)
* [5\. Next Steps](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p1#id-5.-next-steps)
Copy
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Copy
module.exports = {
content: [\
"./pages/**/*.{js,ts,jsx,tsx}",\
"./components/**/*.{js,ts,jsx,tsx}",\
],
theme: {
extend: {},
},
plugins: [],
}
Copy
@tailwind base;
@tailwind components;
@tailwind utilities;
Copy
import { createContext, useContext, useState } from "react";
const WalletContext = createContext();
export function WalletProvider({ children }) {
const [connected, setConnected] = useState(false);
const [address, setAddress] = useState("");
async function connectToMetaMask() {
if (typeof window !== "undefined" && window.ethereum) {
try {
await window.ethereum.request({ method: "eth_requestAccounts" });
// For simplicity, get the first address
const [userAddress] = window.ethereum.selectedAddress
? [window.ethereum.selectedAddress]
: [];
setAddress(userAddress);
setConnected(true);
} catch (err) {
console.error("User denied account access:", err);
}
} else {
console.log("MetaMask is not installed!");
}
}
function disconnectWallet() {
setConnected(false);
setAddress("");
}
// Return the context provider
return (
{children}
);
}
export function useWallet() {
return useContext(WalletContext);
}
Copy
const { connected, ... } = useWallet()
Copy
import "../styles/globals.css";
import { WalletProvider } from "../contexts/walletcontext";
import NavBar from "../components/navbar";
function MyApp({ Component, pageProps }) {
return (
);
}
export default MyApp;
Copy
import { useWallet } from "../contexts/walletcontext";
import Link from "next/link";
export default function NavBar() {
const { connected, address, disconnectWallet } = useWallet();
return (
);
}
Copy
npm run dev
---
# Authenticating with RainbowKit | Somnia Docs
In this guide, we'll integrate [RainbowKitarrow-up-right](https://www.rainbowkit.com/docs/introduction)
with the Somnia Network in a Next.js application. This will enable users to connect their wallets seamlessly, facilitating interactions with the Somnia blockchain.
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-rainbowkit#prerequisites)
Prerequisites
-----------------------------------------------------------------------------------------------------------------------------------------------------------
Before we begin, ensure you have the following:
1. This guide is not an introduction to JavaScript Programming; you are expected to understand JavaScript.
2. To complete this guide, you will need MetaMask installed and the Somnia DevNet added to the list of Networks. If you have yet to install MetaMask, please follow this guide to [Connect Your Wallet guide](https://docs.somnia.network/get-started/connect-your-wallet-to-mainnet)
.
3. Familiarity with React and Next.js is assumed.
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-rainbowkit#create-the-next.js-project)
Create the Next.js Project
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Open your terminal and run the following commands to set up a new Next.js project:
Copy
npx create-next-app@latest somnia-rainbowkit
cd somnia-rainbowkit
Install the required Dependencies, which are `**wagmi**`, `**viem**`, @tanstack/react-query, and rainbowkit. Run the following command:
Copy
npm install wagmi viem @tanstack/react-query rainbowkit
###
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-rainbowkit#set-up-providers-in-next.js)
Set Up Providers in Next.js
We'll set up several providers to manage the application's state and facilitate interactions with the blockchain.
Create a `**components**` directory in the app folder. Inside the components directory, create a file named `**ClientProvider.tsx**` with the following content:
> Every Rainbowkit dApp relies on **WalletConnect** and needs to obtain a `**projectId**` from WalletConnect Cloud. Get one [herearrow-up-right](https://cloud.walletconnect.com/)
> .
In the app directory, locate the `**layout.tsx**` file and update it as follows:
###
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-rainbowkit#build-the-home-page)
Build the Home Page
We'll create a simple home page that allows users to connect their wallets and displays their address upon connection.
In the app directory, locate the `**page.tsx**` file and update it as follows:
###
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-rainbowkit#run-the-application)
Run the Application
Start the Development Server by running the following command:
Open your browser and navigate to http://localhost:3000. You should see the RainbowKit button, allowing users to connect their wallets to the Somnia network.
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-rainbowkit#conclusion)
Conclusion
-----------------------------------------------------------------------------------------------------------------------------------------------------
You've successfully integrated RainbowKit with the Somnia Network in a Next.js application. This setup provides a foundation for building decentralized applications on Somnia, enabling seamless wallet connections and interactions with the Somnia Network.
For further exploration, consider adding features such as interacting with smart contracts, displaying user balances, or implementing transaction functionalities.
If you encounter any issues or need assistance, join the [Somnia Developer Discordarrow-up-right](https://discord.gg/somnia)
.
[PreviousAuthenticating with Privychevron-left](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy)
[NextOnRampschevron-right](https://docs.somnia.network/developer/building-dapps/onramps)
Last updated 5 months ago
* [Prerequisites](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-rainbowkit#prerequisites)
* [Create the Next.js Project](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-rainbowkit#create-the-next.js-project)
* [Set Up Providers in Next.js](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-rainbowkit#set-up-providers-in-next.js)
* [Build the Home Page](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-rainbowkit#build-the-home-page)
* [Run the Application](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-rainbowkit#run-the-application)
* [Conclusion](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-rainbowkit#conclusion)
Copy
'use client';
import { WagmiProvider } from "wagmi";
import {
RainbowKitProvider,
getDefaultConfig,
} from "@rainbow-me/rainbowkit";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { somniaTestnet } from "viem/chains";
const queryClient = new QueryClient();
const config = getDefaultConfig({
appName: "Somnia Example App",
projectId: "Get_WalletConnect_ID",
chains: [somniaTestnet],
ssr: true,
});
export default function ClientProvider({ children }) {
return (
{children}
);
}
Copy
import ClientProvider from './components/ClientProvider';
export default function RootLayout({ children }) {
return (
Somnia DApp{children}
);
}
Copy
'use client';
import { useAccount } from 'wagmi';
import { ConnectButton } from "@rainbow-me/rainbowkit";
export default function Home() {
const { address, isConnected } = useAccount();
return (
{isConnected && (
Connected as: {address}
)}
);
}
Copy
npm run dev
---
# APIs | Somnia Docs
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/apis#ormi)
[Ormiarrow-up-right](https://ormilabs.com/)
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Ormi is the only unified Web3 data layer to supercharge live, historical, and AI-powered blockchain data applications
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/apis#resources)
Resources
* [API Listarrow-up-right](https://subgraph.somnia.network/)
* [Documentation](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi)
[PreviousAccount Abstractionchevron-left](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/account-abstraction)
[NextSupport and Communitychevron-right](https://docs.somnia.network/developer/deployment-and-production/support-and-community)
Last updated 6 months ago
* [Ormi](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/apis#ormi)
* [Resources](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/apis#resources)
---
# Somnia Domains (.somi) | Somnia Docs
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-domains-.somi#somnia-domains-.somi)
Somnia Domains (.somi)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-domains-.somi#id-1.-for-users)
1\. For Users
---------------------------------------------------------------------------------------------------------------------------------------------------------------
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-domains-.somi#how-to-claim-a-domain)
How to Claim a Domain?

1. Go to [somnia.domainsarrow-up-right](https://somnia.domains/)
and connect your wallet.
2. Enter the domain you wish to claim in the “Domain Name” field.
3. If the domain address has not been reserved or claimed, you can claim it.
4. Claim periods of 1, 2, or 3 years are available. If you wish to extend it further, you can extend the period by clicking the “Renew” button in the “Domain Management” menu.
5. Domains that are not renewed before their expiration date will automatically expire and become available for others to claim.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-domains-.somi#how-to-set-primary-domain)
How to Set Primary Domain?

1. You can view the domains you own from the “Domain Management” menu.
2. Here, you can set your desired domain as the Primary Domain.
3. Your primary domain is the domain that dApps can access via API. You can remove it or change it to another domain at any time.
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-domains-.somi#id-2.-for-developers)
2\. For Developers
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
**.somi API**
You can retrieve the primary domain data of wallets using somi's API. This allows users to see their Primary Domains instead of their wallet addresses in the frontend.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-domains-.somi#what-does-the-api-returns)
What does the API returns?
You can display the `wallet` or `primaryDomain` strings in your frontend using the `hasPrimary` boolean value.
[PreviousNFTs2Mechevron-left](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/nfts2me)
[NextSomnia Exchangechevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange)
Last updated 4 months ago
* [Somnia Domains (.somi)](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-domains-.somi#somnia-domains-.somi)
* [1\. For Users](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-domains-.somi#id-1.-for-users)
* [How to Claim a Domain?](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-domains-.somi#how-to-claim-a-domain)
* [How to Set Primary Domain?](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-domains-.somi#how-to-set-primary-domain)
* [2\. For Developers](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-domains-.somi#id-2.-for-developers)
* [What does the API returns?](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-domains-.somi#what-does-the-api-returns)
Copy
https://api.somnia.domains/api/primary-domain/{walletAddress}
Copy
{
"wallet":"0x4F40da1a67b891Ec7cCD54F809AFDD72CCc07BEC",
"primaryDomain":"primary.somi",
"hasPrimary":true
}
---
# RPC | Somnia Docs
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/rpc#ankr)
[Ankrarrow-up-right](https://ankr.com/)
-----------------------------------------------------------------------------------------------------------------------------------------------------------
Ankr provides scalable RPC and API services to interact with the Somnia blockchain efficiently. Developers can use Ankr's enterprise-grade infrastructure to enhance dApp performance.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/rpc#resources)
Resources
* [Ankr RPC for Somniaarrow-up-right](https://www.ankr.com/rpc/somnia/)
* * *
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/rpc#public-node)
Public Node
-------------------------------------------------------------------------------------------------------------------------------------
Public Node by All Nodes provides fast, free, and privacy-first RPC endpoints for Somnia Network.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/rpc#resources-1)
Resources
* [Public Node RPC for Somniaarrow-up-right](https://somnia.publicnode.com/)
* * *
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/rpc#stakely)
Stakely
-----------------------------------------------------------------------------------------------------------------------------
Stakely is an enterprise-grade infrastructure providerr, trusted by developers and institutions.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/rpc#resources-2)
Resources
* [Stakely RPC for Somniaarrow-up-right](https://somnia-json-rpc.stakely.io/)
* * *
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/rpc#validation-cloud)
Validation Cloud
-----------------------------------------------------------------------------------------------------------------------------------------------
Validation Cloud is the world’s fastest node provider according to Compare Nodes. With 50 million compute units available for use without a credit card and a scale tier that never imposes rate limits, Validation Cloud is built to support the most rigorous, low-latency workloads. Somnia Testnet & Somnia Mainnet are supported.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/rpc#resources-3)
Resources
* [Linkarrow-up-right](https://www.validationcloud.io/somnia)
[PreviousEcosystem Toolschevron-left](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools)
[NextOracleschevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/oracles)
Last updated 5 months ago
* [Ankr](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/rpc#ankr)
* [Resources](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/rpc#resources)
* [Public Node](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/rpc#public-node)
* [Resources](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/rpc#resources-1)
* [Stakely](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/rpc#stakely)
* [Resources](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/rpc#resources-2)
* [Validation Cloud](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/rpc#validation-cloud)
* [Resources](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/rpc#resources-3)
---
# Wallet Providers | Somnia Docs
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/wallet-providers#privy)
[Privyarrow-up-right](https://docs.privy.io/welcome)
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Privy is a secure, embeddable wallet infrastructure provider that allows developers to authenticate users, manage sessions, and provide seamless wallet experiences within dApps.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/wallet-providers#resources)
Resources
* [Documentation](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy)
* * *
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/wallet-providers#sequence)
[Sequencearrow-up-right](https://sequence.build/)
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sequence is a smart contract wallet & account abstraction provider that enables gasless transactions and seamless user onboarding to dApps.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/wallet-providers#resources-1)
Resources
* [Documentationarrow-up-right](https://sequence.build/landing)
* * *
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/wallet-providers#thirdweb)
[Thirdwebarrow-up-right](https://thirdweb.com/)
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Thirdweb simplifies smart contract deployment, account abstraction, and Web3 development on Somnia. It offers gasless transactions, token management, and SDKs for seamless dApp building.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/wallet-providers#resources-2)
Resources
* [Thirdweb Somnia Docsarrow-up-right](https://thirdweb.com/somnia)
* Documentation
[PreviousSubgraphschevron-left](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/subgraphs)
[NextSafeschevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/safes)
Last updated 5 months ago
* [Privy](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/wallet-providers#privy)
* [Resources](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/wallet-providers#resources)
* [Sequence](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/wallet-providers#sequence)
* [Resources](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/wallet-providers#resources-1)
* [Thirdweb](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/wallet-providers#thirdweb)
* [Resources](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/wallet-providers#resources-2)
---
# Account Abstraction | Somnia Docs
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/account-abstraction#pimlico)
Pimlico
---------------------------------------------------------------------------------------------------------------------------------------------
Pimlico, the world's most advanced ERC-4337 account abstraction infrastructure platform. This guide will help you understand Pimlico's ecosystem and get you started with building applications using our tools and services.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/account-abstraction#resources)
Resources
* [Tutorialarrow-up-right](https://docs.pimlico.io/guides/tutorials/tutorial-1)
[PreviousOn Rampschevron-left](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/on-ramps)
[NextAPIschevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/apis)
Last updated 1 month ago
* [Pimlico](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/account-abstraction#pimlico)
* [Resources](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/account-abstraction#resources)
---
# Smart Wallet App with Thirdweb | Somnia Docs
The Somnia mission is to enable the development of mass-consumer real-time applications. The Somnia Network allows developers to build a unique experience by implementing Smart Contract Wallets with gasless transactions via Account Abstraction (ERC-4337). In this tutorial, we'll use the [Thirdweb React SDKarrow-up-right](https://portal.thirdweb.com/react/v5)
to:
* Connect Smart Wallets (Account Abstraction)
* Read Wallet Balance
* Send STT Tokens
[hashtag](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb#pre-requisites)
Pre-requisites
-----------------------------------------------------------------------------------------------------------------------------------------------------
Before we start, ensure you have:
* Basic knowledge of React
* A [Thirdwebarrow-up-right](https://thirdweb.com/)
account & Client ID
* Node.js & npm installed
[hashtag](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb#install-dependencies)
Install Dependencies
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
Run the following command to set up your project:
Copy
npx create-next-app@latest somnia-thirdweb
cd somnia-thirdweb
npm install thirdweb ethers viem dotenv
This installs:
`thirdweb` → The Thirdweb React SDK.
`ethers` → To interact with blockchain transactions.
`dotenv` → To securely store API keys.
[hashtag](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb#create-the-thirdweb-client)
Create the Thirdweb Client
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
The Thirdweb client allows the app to communicate with the blockchain. Create a `**client.ts**` file and add:
circle-info
Get your Client ID: Register at thirdweb.com/dashboard. 💡
[hashtag](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb#add-environment-variables)
Add Environment Variables
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Store API keys in a .env.local file:
Restart Next.js after modifying .env.local
[hashtag](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb#build-the-account-abstraction-app)
Build the Account Abstraction App
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
To ensure Thirdweb Components are available throughout the app, wrap the children's components inside ThirdwebProvider. Modify `**layout.ts**`:
[hashtag](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb#create-and-connect-smart-contract-wallet)
Create & Connect Smart Contract Wallet
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
The `**useActiveAccount**` hook allows us to detect the connected Smart Wallet Account. We use `**ConnectButton**` to handle authentication and connection to the blockchain.
This button will connect the user's Smart Contract Wallet and authenticate the user with Thirdweb. After connection, it will also display the wallet address.

click Connect

Connected Wallet
To show the connected address, we add the following UI component:
[hashtag](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb#token-transfer)
Token Transfer
-----------------------------------------------------------------------------------------------------------------------------------------------------
The `**useSendTransaction**` hook is used to send STT tokens to another address. The function `**sendTokens**` will check that the Smart Account is connected and then send 0.01 STT tokens to a recipient address. First, copy your Smart Wallet Address and request for Tokens on Discord in the **dev-chat,** you can also Transfer some from your EOA.
Log transaction success or failure messages to the console.
[hashtag](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb#button-integration)
Button Integration
-------------------------------------------------------------------------------------------------------------------------------------------------------------
The button UI provides a clear interaction for sending STT tokens. The button shows a loading state (Sending...) when the transaction is pending and displays a success or failure message once the transaction is complete.
chevron-rightComplete Code[hashtag](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb#complete-code)
The full implementation includes wallet connection, balance retrieval, and token transfer.

Smart Contract Wallet
[hashtag](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb#conclusion)
Conclusion
---------------------------------------------------------------------------------------------------------------------------------------------
Congratulations! 🎉 You have successfully connected a smart contract wallet using Thirdweb. Read wallet balances and Transferred STT tokens on Somnia Testnet. You can explore additional features such as gasless transactions, NFT integration, and DeFi applications.
[PreviousGasless Transactions with Thirdwchevron-left](https://docs.somnia.network/developer/building-dapps/account-abstraction/gasless-transactions-with-thirdw)
[NextData Indexing and Queryingchevron-right](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying)
Last updated 10 months ago
* [Pre-requisites](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb#pre-requisites)
* [Install Dependencies](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb#install-dependencies)
* [Create the Thirdweb Client](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb#create-the-thirdweb-client)
* [Add Environment Variables](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb#add-environment-variables)
* [Build the Account Abstraction App](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb#build-the-account-abstraction-app)
* [Create & Connect Smart Contract Wallet](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb#create-and-connect-smart-contract-wallet)
* [Token Transfer](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb#token-transfer)
* [Button Integration](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb#button-integration)
* [Conclusion](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb#conclusion)
Copy
import { createThirdwebClient } from "thirdweb";
export const client = createThirdwebClient({
clientId: process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID as string, // Replace with your actual Client ID
});
Copy
NEXT_PUBLIC_THIRDWEB_CLIENT_ID=your-client-id-here
NEXT_PUBLIC_SOMNIA_RPC_URL=https://dream-rpc.somnia.network/
Copy
import { ThirdwebProvider } from 'thirdweb/react';
{children}
Copy
import { useActiveAccount } from "thirdweb/react";
const smartAccount = useActiveAccount();
Copy
{smartAccount ? (
);
}
---
# Explorers | Somnia Docs
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/explorers#blockscout)
Blockscout
-----------------------------------------------------------------------------------------------------------------------------------------
Blockscout provides blockchain analytics & explorer services for Somnia. Developers and users can access on-chain transaction data, addresses, and smart contracts.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/explorers#resources)
Resources
* [Blockscout Explorerarrow-up-right](http://shannon-explorer.somnia.network/)
[PreviousSafeschevron-left](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/safes)
[NextSDKschevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/sdks)
Last updated 10 months ago
* [Blockscout](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/explorers#blockscout)
* [Resources](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/explorers#resources)
---
# Gas Configuration | Somnia Docs
Properly configuring gas parameters is critical for on-chain reactivity subscriptions. If gas values are too low, validators will silently skip your subscription — no error, no warning, just nothing happens.
triangle-exclamation
**This is the #1 cause of "reactivity not working."** Most developers set up their contracts and subscriptions correctly, but use gas values that are too low for validators to process.
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#understanding-the-parameters)
Understanding the Parameters
--------------------------------------------------------------------------------------------------------------------------------------------
On-chain reactivity subscriptions require three gas parameters:
Parameter
Description
Unit
`priorityFeePerGas`
Tip paid to validators to prioritize your handler execution
gwei (nanoSomi)
`maxFeePerGas`
Maximum total fee per gas (base fee + priority fee)
gwei (nanoSomi)
`gasLimit`
Maximum gas provisioned per handler invocation
gas units
###
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#how-they-work-together)
How They Work Together
1
###
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#priorityfeepergas)
priorityFeePerGas
This is essentially the "tip" for validators. The default value is 0 nanoSomi. Increase it to make sure your handler is executed before others.
2
###
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#maxfeepergas)
maxFeePerGas
The ceiling on what you'll pay per gas unit. The minimum is `baseFee + priorityFeePerGas` , where the base fee in Somnia is 6 nanoSomi. Setting this too low will cause your handler invocation to fail in peak times.
3
###
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#gaslimit)
gasLimit
How much gas your `_onEvent` handler is allowed to consume. If your handler runs out of gas, it reverts.
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#recommended-values)
Recommended Values
------------------------------------------------------------------------------------------------------------------------
###
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#standard-use-cases)
Standard Use Cases
Copy
import { parseGwei } from 'viem';
await sdk.createSoliditySubscription({
handlerContractAddress: '0x...',
emitter: '0x...',
eventTopics: [eventSignature],
priorityFeePerGas: parseGwei('0'), // 0 gwei — typically, no priority is required
maxFeePerGas: parseGwei('10'), // 10 gwei — comfortable ceiling
gasLimit: 2_000_000n, // Sufficient for simple state updates
isGuaranteed: true,
isCoalesced: false,
});
###
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#by-handler-complexity)
By Handler Complexity
Handler Type
`priorityFeePerGas`
`maxFeePerGas`
`gasLimit`
Example
**Simple** (state updates, emit event)
`parseGwei('0')`
`parseGwei('10')`
`2_000_000n`
Counter, token reward
**Medium** (cross-contract calls)
`parseGwei('0')`
`parseGwei('10')`
`3_000_000n`
Game logic with external calls
**Complex** (multiple external calls, loops)
`parseGwei('10')`
`parseGwei('20')`
`10_000_000n`
Settlement, multi-step workflows
###
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#quick-reference-table-raw-bigint-values)
Quick Reference Table (Raw BigInt Values)
If you prefer raw values instead of `parseGwei()`:
Level
`priorityFeePerGas`
`maxFeePerGas`
`gasLimit`
Minimum recommended
`0n`
`10_000_000_000n`
2`_000_000n`
Comfortable
`0n`
`10_000_000_000n`
3`_000_000n`
High priority
`10_000_000_000n`
`20_000_000_000n`
`10_000_000n`
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#common-mistakes)
Common Mistakes
------------------------------------------------------------------------------------------------------------------
###
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#using-wei-instead-of-gwei)
Using wei instead of gwei
circle-exclamation
`10n` is **10 wei** = 0.00000001 gwei. This is 200 million times less than the recommended 2 gwei. Always use `parseGwei()` to avoid unit confusion. See [SOMI coin](https://docs.somnia.network/developer/network-info/somi-coin)
for details on how this is calculated.
###
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#computing-from-gasprice-with-too-small-divisors)
Computing from gasPrice with too-small divisors
###
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#setting-gaslimit-too-low)
Setting gasLimit too low
Somnia operates on a different gas model to Ethereum. One of the key differences is that the 1,000,000 gas reserve is required for any storage operations, see [Storage EVM operations](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum#storage-evm-operations)
. It is safe to up your gas limit to meet the reserve requirements. If your handler reverts due to out-of-gas, the subscription still charges you but the state change doesn't happen.
###
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#forgetting-to-recreate-subscription-after-redeploying)
Forgetting to recreate subscription after redeploying
Subscriptions are tied to specific contract addresses. If you redeploy your contract, you get a new address. The old subscription won't trigger for the new contract.
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#cost-estimation)
Cost Estimation
------------------------------------------------------------------------------------------------------------------
The subscription owner pays for each handler invocation. The cost per invocation is:
Where `effectiveGasPrice` is at most `maxFeePerGas` and at least `baseFee + priorityFeePerGas`.
###
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#example)
Example
For a simple handler using ~50,000 gas at 10 gwei max fee:
The subscription owner must maintain at least **32 SOMI** balance. This is not spent — it's a minimum holding requirement. Actual costs are deducted per invocation.
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#debugging-gas-issues)
Debugging Gas Issues
----------------------------------------------------------------------------------------------------------------------------
If your subscription was created successfully but the handler is never invoked:
1
###
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#check-subscription-info)
Check subscription info
Verify that `priorityFeePerGas` is at least `2000000000` (2 gwei).
2
###
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#test-with-a-cli-script-first)
Test with a CLI script first
Before debugging frontend issues, confirm reactivity works via a Hardhat script:
3
###
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#look-for-validator-transactions)
Look for validator transactions
On-chain reactivity is executed by validators from the address `0x0000000000000000000000000000000000000100`. Check the block explorer for transactions from this address to your handler contract after your event was emitted.
[hashtag](https://docs.somnia.network/developer/reactivity/gas-configuration#summary)
Summary
--------------------------------------------------------------------------------------------------
Do
Don't
Use `parseGwei('2')` for priority fee
Use raw small numbers like `10n` or `100n`
Use `parseGwei('10')` for max fee
Compute from `gasPrice` with arbitrary divisors
Set gasLimit based on handler complexity
Use a one-size-fits-all low gasLimit
Recreate subscription after redeploying
Assume old subscription works with new contract
Test via CLI before building frontend
Debug reactivity issues through the browser
[PreviousCron subscriptions via SDKchevron-left](https://docs.somnia.network/developer/reactivity/tutorials/cron-subscriptions-via-sdk)
[NextFAQs & Troubleshootingchevron-right](https://docs.somnia.network/developer/reactivity/faqs-and-troubleshooting)
Last updated 27 days ago
* [Understanding the Parameters](https://docs.somnia.network/developer/reactivity/gas-configuration#understanding-the-parameters)
* [How They Work Together](https://docs.somnia.network/developer/reactivity/gas-configuration#how-they-work-together)
* [priorityFeePerGas](https://docs.somnia.network/developer/reactivity/gas-configuration#priorityfeepergas)
* [maxFeePerGas](https://docs.somnia.network/developer/reactivity/gas-configuration#maxfeepergas)
* [gasLimit](https://docs.somnia.network/developer/reactivity/gas-configuration#gaslimit)
* [Recommended Values](https://docs.somnia.network/developer/reactivity/gas-configuration#recommended-values)
* [Standard Use Cases](https://docs.somnia.network/developer/reactivity/gas-configuration#standard-use-cases)
* [By Handler Complexity](https://docs.somnia.network/developer/reactivity/gas-configuration#by-handler-complexity)
* [Quick Reference Table (Raw BigInt Values)](https://docs.somnia.network/developer/reactivity/gas-configuration#quick-reference-table-raw-bigint-values)
* [Common Mistakes](https://docs.somnia.network/developer/reactivity/gas-configuration#common-mistakes)
* [Using wei instead of gwei](https://docs.somnia.network/developer/reactivity/gas-configuration#using-wei-instead-of-gwei)
* [Computing from gasPrice with too-small divisors](https://docs.somnia.network/developer/reactivity/gas-configuration#computing-from-gasprice-with-too-small-divisors)
* [Setting gasLimit too low](https://docs.somnia.network/developer/reactivity/gas-configuration#setting-gaslimit-too-low)
* [Forgetting to recreate subscription after redeploying](https://docs.somnia.network/developer/reactivity/gas-configuration#forgetting-to-recreate-subscription-after-redeploying)
* [Cost Estimation](https://docs.somnia.network/developer/reactivity/gas-configuration#cost-estimation)
* [Example](https://docs.somnia.network/developer/reactivity/gas-configuration#example)
* [Debugging Gas Issues](https://docs.somnia.network/developer/reactivity/gas-configuration#debugging-gas-issues)
* [Check subscription info](https://docs.somnia.network/developer/reactivity/gas-configuration#check-subscription-info)
* [Test with a CLI script first](https://docs.somnia.network/developer/reactivity/gas-configuration#test-with-a-cli-script-first)
* [Look for validator transactions](https://docs.somnia.network/developer/reactivity/gas-configuration#look-for-validator-transactions)
* [Summary](https://docs.somnia.network/developer/reactivity/gas-configuration#summary)
Copy
// WRONG — these are 10 wei and 20 wei, essentially zero
priorityFeePerGas: 10n,
maxFeePerGas: 20n,
// CORRECT — these are 2 gwei and 10 gwei
priorityFeePerGas: parseGwei('2'), // = 2_000_000_000n
maxFeePerGas: parseGwei('10'), // = 10_000_000_000n
Copy
// RISKY — gas price fluctuates and dividing by 10 may yield too-low values
const gasPrice = await publicClient.getGasPrice();
priorityFeePerGas: gasPrice / 10n,
// SAFER — use fixed proven values
priorityFeePerGas: parseGwei('2'),
Copy
// TOO LOW for any storage operations
gasLimit: 100_000n,
// SAFE for most handlers
gasLimit: 2_000_000n,
// SAFE for complex handlers with external calls
gasLimit: 10_000_000n,
Copy
Deploy contract → address A
Create subscription → emitter: A, handler: A ✅
Redeploy contract → address B
Old subscription still points to A → ❌ won't work
Must create NEW subscription → emitter: B, handler: B ✅
Copy
cost = gasUsed × effectiveGasPrice
Copy
cost ≈ 50,000 × 10 gwei = 500,000 gwei = 0.0005 SOM per invocation
Copy
const info = await sdk.getSubscriptionInfo(subscriptionId);
console.log(JSON.stringify(info, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2));
Copy
// 1. Call your contract function that emits the event
const tx = await contract.myFunction();
await tx.wait();
// 2. Poll for state change
for (let i = 0; i < 15; i++) {
await new Promise(r => setTimeout(r, 2000));
const result = await contract.myStateVariable();
if (result !== previousValue) {
console.log('Reactivity worked!');
break;
}
}
---
# SDKs | Somnia Docs
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/sdks#sequence)
[Sequencearrow-up-right](https://sequence.build/)
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sequence is a smart contract wallet & account abstraction provider that enables gasless transactions and seamless user onboarding to dApps.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/sdks#resources)
Resources
* [Documentationarrow-up-right](https://sequence.build/landing)
* * *
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/sdks#thirdweb)
[Thirdwebarrow-up-right](https://thirdweb.com/)
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Thirdweb simplifies smart contract deployment, account abstraction, and Web3 development on Somnia. It offers gasless transactions, token management, and SDKs for seamless dApp building.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/sdks#resources-1)
Resources
* [Thirdweb Somnia Docsarrow-up-right](https://thirdweb.com/somnia-shannon-testnet)
* [Deploy Smart Contracts on Somnia](https://docs.somnia.network/developer/development-frameworks/deploy-with-thirdweb)
* [Account Abstraction with Somnia](https://docs.somnia.network/developer/building-dapps/account-abstraction/smart-wallet-app-with-thirdweb)
* [Thirdweb Faucetarrow-up-right](https://thirdweb.com/somnia-shannon-testnet)
[PreviousExplorerschevron-left](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/explorers)
[NextOn Rampschevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/on-ramps)
Last updated 1 month ago
* [Sequence](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/sdks#sequence)
* [Resources](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/sdks#resources)
* [Thirdweb](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/sdks#thirdweb)
* [Resources](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/sdks#resources-1)
---
# Elix.fi | Somnia Docs
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/elix.fi#id-1.-for-users)
1\. For Users
--------------------------------------------------------------------------------------------------------------------------------------------------
Elix is a CLOB (Central Limit Order Book) / AMM hybrid decentralized exchange (DEX) built on the Somnia Network, designed to bring the performance and trading experience of a centralized exchange (CEX) fully on-chain.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/elix.fi#how-to-trade-on-elix)
How to Trade on Elix?

1. **Go to the** [**Elix App**arrow-up-right](https://app.elix.fi/trade)
**:** Connect your wallet to the Elix trading platform.
2. **Select Market:** Choose the trading pair you are interested in, for example, WETH/USDC.
3. **Choose Order Type:** You can place a **Market Order** to trade at the current best price, or a **Limit Order** to specify the exact price at which you want to buy or sell.
4. **Enter Details:** For a limit order, enter the price and the amount you wish to trade. The interface will show you the total cost and fees.
5. **Place Order:** Submit your order. Limit orders will sit on the order book until the market price reaches your specified price, at which point they will be automatically executed.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/elix.fi#how-to-earn-yield-on-elix)
How to Earn Yield on Elix?

Elix offers innovative yield opportunities for liquidity providers through its "Earn" page.
1. **Navigate to Earn Page:** Find the "Earn" section in the Elix app.
2. **Explore Vaults:** Browse the available vaults. Each vault has a different strategy and risk profile, targeting specific token pairs.
3. **Deposit Liquidity:** Choose a vault that fits your strategy and deposit your assets. By depositing, you become a liquidity provider and start earning fees from trades, and potentially other rewards.

[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/elix.fi#id-2.-for-developers)
2\. For Developers
------------------------------------------------------------------------------------------------------------------------------------------------------------
Elix's revolutionary feature is that liquidity is not locked. Liquidity providers can use their funds in other protocols until their orders are matched. This opens up immense possibilities for "composable" DeFi strategies.
**Core Concepts for Integration**
* **Capital Efficiency & Composability:** Elix’s core innovation is that liquidity is not locked in the order book. As a developer, this means your users can use assets **such as aTokens supplied to** [**Tokos**arrow-up-right](https://app.tokos.fi/)
as collateral to provide liquidity on Elix. Users can earn both lending interest from [Tokosarrow-up-right](https://app.tokos.fi/)
and a share of trading fees from Elix simultaneously.
* **Smart Hooks (Programmable Orders):** Elix allows liquidity providers to post arbitrary Smart Contracts (_Hooks_) as offers. These programmable orders can execute custom logic directly on-chain. This unlocks new opportunities for automated trading, dynamic pricing, and cross-protocol yield strategies.
* **Off-Chain Order Posting, On-Chain Settlement:** To achieve CEX-level performance, Elix can utilize off-chain services for order matching while ensuring that all settlements and fund transfers are executed transparently and securely on the Somnia blockchain. Developers can build bots and strategies that interact with this high-frequency matching engine.
For more information, please check [Elix.fi Docsarrow-up-right](https://docs.elix.fi/)
.
[PreviousEcosystem Showcasechevron-left](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase)
[NextMeme Coinschevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/meme-coins)
Last updated 4 months ago
* [1\. For Users](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/elix.fi#id-1.-for-users)
* [How to Trade on Elix?](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/elix.fi#how-to-trade-on-elix)
* [How to Earn Yield on Elix?](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/elix.fi#how-to-earn-yield-on-elix)
* [2\. For Developers](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/elix.fi#id-2.-for-developers)
---
# Protofire Price Feeds | Somnia Docs
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#somnia-mainnet-price-feeds)
Somnia Mainnet Price Feeds
--------------------------------------------------------------------------------------------------------------------------------------------------------
**Asset Pair**
**OCR Aggregator**
**Proxy (read‑only)**
USDC / USD
[0x4b74EcA574Ce996448b485100e4FFf84866911dFarrow-up-right](https://explorer.somnia.network/address/0x4b74EcA574Ce996448b485100e4FFf84866911dF)
[0x843B6812E9Aa67b3773675d2836646BCbd216642arrow-up-right](https://explorer.somnia.network/address/0x843B6812E9Aa67b3773675d2836646BCbd216642)
ETH / USD
[0xa3060dd6Bb56EdfB2E0d78c88ef63A974a392D36arrow-up-right](https://explorer.somnia.network/address/0xa3060dd6Bb56EdfB2E0d78c88ef63A974a392D36)
[0xeC25a820A6F194118ef8274216a7F225Da019526arrow-up-right](https://explorer.somnia.network/address/0xeC25a820A6F194118ef8274216a7F225Da019526)
BTC / USD
[0x3cBdF7F02956c8e946192Bff64bb2Dd470dd589Carrow-up-right](https://explorer.somnia.network/address/0x3cBdF7F02956c8e946192Bff64bb2Dd470dd589C)
[0xa57d637618252669fD859B1F4C7bE6F52Bef67edarrow-up-right](https://explorer.somnia.network/address/0xa57d637618252669fD859B1F4C7bE6F52Bef67ed)
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#somnia-testnet-price-feeds)
Somnia Testnet Price Feeds
--------------------------------------------------------------------------------------------------------------------------------------------------------
**Token Pair**
**Contract Address**
USDC/USD
[0xa2515C9480e62B510065917136B08F3f7ad743B4arrow-up-right](http://shannon-explorer.somnia.network/address/0xa2515C9480e62B510065917136B08F3f7ad743B4)
ETH/USD
[0xd9132c1d762D432672493F640a63B758891B449earrow-up-right](http://shannon-explorer.somnia.network/address/0xd9132c1d762D432672493F640a63B758891B449e)
BTC/USD
[0x8CeE6c58b8CbD8afdEaF14e6fCA0876765e161fEarrow-up-right](http://shannon-explorer.somnia.network/address/0x8CeE6c58b8CbD8afdEaF14e6fCA0876765e161fE)
Use any of these when deploying the PriceConsumer Smart Contract for your dApp.
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#what-are-oracles-and-why-do-they-matter)
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.ioarrow-up-right](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.
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#prerequisites)
Prerequisites
------------------------------------------------------------------------------------------------------------------------------
1. This guide is not an introduction to JavaScript Programming; you are expected to understand JavaScript.
2. Basic knowledge of React & Next.js.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#solidity-contract-chainlink-oracle-consumer)
Solidity Contract (Chainlink Oracle Consumer)
###
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#code-breakdown)
Code Breakdown
Pulls in the AggregatorV3Interface from the Chainlink library. This interface allows interaction with Chainlink oracle contracts for Price Feeds.
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.
Instantiates the interface with the provided address, enabling function calls to the external oracle.
`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:
This function extracts only the price and ignores the rest using commas. It returns the latest price as an int256.
`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.
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#how-price-oracle-latestrounddata-works)
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:
* `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
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.
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#deploy-with-hardhat)
Deploy with Hardhat
------------------------------------------------------------------------------------------------------------------------------------------
Update ignition/modules/deploy.js:
Deploy using:
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#building-the-ui)
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.
Then, install required dependencies.
Add imports to the `index.js` file
* 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.
####
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#create-a-viem-client-for-somnia)
Create a Viem Client for Somnia
Creates a blockchain client configured for Somnia Testnet using its RPC URL and allows reading smart contract data without needing a wallet or signer.
####
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#declare-a-variable-for-the-deployed-smart-contracts-for-your-price-feed-addresses)
Declare a variable for the deployed Smart Contracts for your Price Feed Addresses
This will map token pairs to their corresponding Chainlink oracle contract addresses deployed on Somnia.
Parse the ABI
####
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#set-up-the-state)
Set up the State
The `price` state will store the formatted token price and `selectedToken` will track which token is selected from the dropdown (default: ETH).
####
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#fetch-the-latest-price)
Fetch the Latest Price
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.
####
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#fetch-price-on-load-and-every-10-seconds)
Fetch Price on Load & Every 10 Seconds
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.
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#complete-code)
Complete Code
------------------------------------------------------------------------------------------------------------------------------
chevron-rightindex.js[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#index.js)
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#conclusion)
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.
[PreviousDIA Price Feedschevron-left](https://docs.somnia.network/developer/building-dapps/oracles/dia-price-feeds)
[NextUsing Verifiable Randomness (VRF)chevron-right](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf)
Last updated 5 months ago
* [Somnia Mainnet Price Feeds](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#somnia-mainnet-price-feeds)
* [Somnia Testnet Price Feeds](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#somnia-testnet-price-feeds)
* [What Are Oracles and Why Do They Matter](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#what-are-oracles-and-why-do-they-matter)
* [Prerequisites](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#prerequisites)
* [Solidity Contract (Chainlink Oracle Consumer)](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#solidity-contract-chainlink-oracle-consumer)
* [Code Breakdown](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#code-breakdown)
* [How Price Oracle latestRoundData() works](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#how-price-oracle-latestrounddata-works)
* [Deploy with Hardhat](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#deploy-with-hardhat)
* [Building the UI](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#building-the-ui)
* [Complete Code](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#complete-code)
* [Conclusion](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds#conclusion)
Copy
// 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();
}
}
Copy
import `@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol
Copy
constructor(address _priceFeed) { }
Copy
priceFeed = AggregatorV3Interface(_priceFeed);
Copy
function getLatestPrice() public view returns (int256) {
(
/* uint80 roundID */,
int256 price,
/* uint startedAt */,
/* uint timeStamp */,
/* uint80 answeredInRound */
) = priceFeed.latestRoundData();
return price;
}
Copy
(uint80 roundId, int256 price, uint startedAt, uint timeStamp, uint80 answeredInRound)
Copy
function getDecimals() public view returns (uint8) {
return priceFeed.decimals();
}
Copy
(uint80 roundId, int256 answer, uint startedAt, uint timeStamp, uint80 answeredInRound)
Copy
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;
Copy
npx hardhat ignition deploy ./ignition/modules/Lock.js --network somnia
Copy
npx create-next-app@latest somnia-protofire-example
cd somnia-protofire-example
Copy
npm install viem
Copy
import { useEffect, useState } from 'react';
import { createPublicClient, http, parseAbi, formatUnits } from 'viem';
import { somniaTestnet } from 'viem/chains';
Copy
const client = createPublicClient({
chain: somniaTestnet,
transport: http(),
});
Copy
const FEEDS = { //Testnet Price Feeds
ETH: '0x604CF5063eC760A78d1C089AA55dFf29B90937f9',
BTC: '0x3dF17dbaa3BA861D03772b501ADB343B4326C676',
USDC: '0xA4a08Eb26f85A53d40E3f908B406b2a69B1A2441',
};
Copy
const abi = parseAbi([\
'function getLatestPrice() view returns (int256)',\
'function getDecimals() view returns (uint8)',\
]);
Copy
export default function PriceWidget() {
const [price, setPrice] = useState('');
const [selectedToken, setSelectedToken] = useState<'ETH' | 'BTC' | 'USDC'>('ETH');
Copy
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));
};
Copy
useEffect(() => {
fetchPrice();
const interval = setInterval(fetchPrice, 10000);
return () => clearInterval(interval);
}, [selectedToken]);
Copy
return (
...
);
}
---
# Integrate Chainlink Oracles | Somnia Docs
Somnia Data Streams provides a powerful, on-chain, and composable storage layer. [Chainlink Oraclesarrow-up-right](https://docs.chain.link/data-feeds/price-feeds/addresses?page=1&testnetPage=1&networkType=testnet&search=&testnetSearch=)
provide secure, reliable, and decentralized external data feeds.
When you combine them, you unlock a powerful new capability: **creating historical, queryable, on-chain data streams from real-world data.**
Chainlink Price Feeds are designed to provide the _latest_ price of an asset. They are not designed to provide a queryable history. You cannot easily ask a Price Feed, "What was the price of ETH 48 hours ago?"
By integrating Chainlink with Somnia Streams, you can build a "snapshot bot" that reads from Chainlink at regular intervals and appends the price to a Somnia Data Stream. This creates a permanent, verifiable, and historical on-chain feed that any other DApp or user can read and trust.
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#objectives-and-deliverable)
**Objectives & Deliverable**
----------------------------------------------------------------------------------------------------------------------------------------------------------------
* **Objective:** Fetch off-chain data (a price feed) with Chainlink and store it historically via Somnia Streams.
* **Key Takeaway:** Combining external "truth" sources with Somnia's composable storage to create new, valuable on-chain data products.
* **Deliverable:** A hybrid "Snapshot Bot" that reads from Chainlink on the Sepolia testnet and publishes to a historical price feed on the Somnia Testnet.
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#what-youll-build)
What You'll Build
-------------------------------------------------------------------------------------------------------------------------------------------
1. **A New Schema:** A `priceFeedSchema` to store price data.
2. **A Chainlink Reader:** A script using `viem` to read the `latestRoundData` from Chainlink's ETH/USD feed on the Sepolia testnet.
3. **A Snapshot Bot:** A script that reads from Chainlink (Sepolia) and writes to Somnia Data Streams (Somnia Testnet).
4. **A History Reader:** A script to read our new historical price feed from Somnia Data Streams.
This tutorial demonstrates a true hybrid-chain application.
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#prerequisites)
Prerequisites
------------------------------------------------------------------------------------------------------------------------------------
* Node.js 20+.
* `@somnia-chain/streams`, `viem`, and `dotenv` installed.
* A wallet with Somnia Testnet tokens (for publishing) and Sepolia testnet ETH (for gas, though we are only reading, so a public RPC is fine).
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#environment-setup)
**Environment Setup**
------------------------------------------------------------------------------------------------------------------------------------------------
Create a `.env` file. You will need RPC URLs for **both** chains and a private key for the Somnia Testnet (to pay for publishing).
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#project-setup)
Project Setup
------------------------------------------------------------------------------------------------------------------------------------
Set up your project with `viem` and the Streams SDK.
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#chain-configuration)
**Chain Configuration**
----------------------------------------------------------------------------------------------------------------------------------------------------
We need to define both chains we are interacting with.
`**src/lib/chain.ts**`
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#client-configuration)
**Client Configuration**
------------------------------------------------------------------------------------------------------------------------------------------------------
We will create two separate clients:
* A **Somnia SDK client** (with a wallet) to _write_ data.
* A **Sepolia Public Client** (read-only) to _read_ from Chainlink.
`**src/lib/clients.ts**`
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#define-the-price-feed-schema)
Define the Price Feed Schema
------------------------------------------------------------------------------------------------------------------------------------------------------------------
Our schema will store the core data from Chainlink's feed.
`**src/lib/schema.ts**`
* `timestamp`: The `updatedAt` time from Chainlink.
* `price`: The `answer` (e.g., ETH price).
* `roundId`: The Chainlink round ID, to prevent duplicates.
* `pair`: A string to identify the feed (e.g., "ETH/USD").
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#create-the-chainlink-reader)
Create the Chainlink Reader
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Let's create a dedicated file to handle fetching data from Chainlink. We will use the `ETH/USD` feed on Sepolia.
`**src/lib/chainlinkReader.ts**`
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#build-the-snapshot-bot-the-hybrid-app)
Build the Snapshot Bot (The Hybrid App)
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
This is the core of our project. This script will:
1. Fetch the latest price from Chainlink (using our module).
2. Encode this data using our `priceFeedSchema`.
3. Publish the data to Somnia Data Streams.
`**src/scripts/snapshotBot.ts**`
**To run your bot:**
Add a script to `package.json`: `"snapshot": "ts-node src/scripts/snapshotBot.ts"`
Run it: `npm run snapshot`
You can run this script multiple times. It will only add new data if Chainlink's `roundId` has changed.
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#read-your-historical-price-feed)
Read Your Historical Price Feed
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Now for the payoff. Let's create a script that reads our new on-chain history from Somnia Streams.
`**src/scripts/readHistory.ts**`
**To read the history:**
Add to `package.json`: `"history": "ts-node src/scripts/readHistory.ts"`
Run it: `npm run history`
**Expected Output:**
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#conclusion-key-takeaways)
Conclusion: Key Takeaways
-----------------------------------------------------------------------------------------------------------------------------------------------------------
You have successfully built a hybrid, cross-chain application.
* You combined an **external "truth source"** (Chainlink) with Somnia's **composable storage layer** (Somnia Data Streams).
* You created a new, valuable, on-chain data product: a **historical, queryable price feed** that any dApp on Somnia can now read from and trust.
* You demonstrated the power of the `publisher` address as a verifiable source. Any dApp can now consume your feed, knowing it was published by _your_ trusted bot.
This pattern can be extended to any external data source: weather, sports results, IoT data, and more. You can run the `snapshotBot.ts` script as a cron job or serverless function to create a truly autonomous, on-chain oracle.
[PreviousREAD Stream Data from a UI (Next.js Example)chevron-left](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example)
[NextWorking with Multiple Publishers in a Shared Streamchevron-right](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream)
Last updated 1 month ago
* [Objectives & Deliverable](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#objectives-and-deliverable)
* [What You'll Build](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#what-youll-build)
* [Prerequisites](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#prerequisites)
* [Environment Setup](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#environment-setup)
* [Project Setup](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#project-setup)
* [Chain Configuration](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#chain-configuration)
* [Client Configuration](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#client-configuration)
* [Define the Price Feed Schema](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#define-the-price-feed-schema)
* [Create the Chainlink Reader](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#create-the-chainlink-reader)
* [Build the Snapshot Bot (The Hybrid App)](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#build-the-snapshot-bot-the-hybrid-app)
* [Read Your Historical Price Feed](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#read-your-historical-price-feed)
* [Conclusion: Key Takeaways](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles#conclusion-key-takeaways)
Copy
# .env
RPC_URL_SOMNIA=[https://dream-rpc.somnia.network]
RPC_URL_SEPOLIA=[https://sepolia.drpc.org]
PRIVATE_KEY_SOMNIA=0xYOUR_SOMNIA_PRIVATE_KEY
Copy
npm i @somnia-chain/streams viem dotenv
npm i -D @types/node typescript ts-node
Copy
import { defineChain } from 'viem'
import { sepolia as sepoliaBase } from 'viem/chains'
// 1. Somnia Testnet
export const somniaTestnet = defineChain({
id: 50312,
name: 'Somnia Testnet',
network: 'somnia-testnet',
nativeCurrency: { name: 'STT', symbol: 'STT', decimals: 18 },
rpcUrls: {
default: { http: [process.env.RPC_URL_SOMNIA || ''] },
public: { http: [process.env.RPC_URL_SOMNIA || ''] },
},
} as const)
// 2. Sepolia Testnet (for Chainlink)
export const sepolia = sepoliaBase
Copy
import 'dotenv/config'
import { createPublicClient, createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { SDK } from '@somnia-chain/streams'
import { somniaTestnet, sepolia } from './chain'
function getEnv(key: string): string {
const value = process.env[key]
if (!value) throw new Error(`Missing environment variable: ${key}`)
return value
}
// === Client 1: Somnia SDK (Read/Write) ===
const somniaWalletClient = createWalletClient({
account: privateKeyToAccount(getEnv('PRIVATE_KEY_SOMNIA') as `0x${string}`),
chain: somniaTestnet,
transport: http(getEnv('RPC_URL_SOMNIA')),
})
const somniaPublicClient = createPublicClient({
chain: somniaTestnet,
transport: http(getEnv('RPC_URL_SOMNIA')),
})
export const somniaSdk = new SDK({
public: somniaPublicClient,
wallet: somniaWalletClient,
})
// === Client 2: Sepolia Public Client (Read-Only) ===
export const sepoliaPublicClient = createPublicClient({
chain: sepolia,
transport: http(getEnv('RPC_URL_SEPOLIA')),
})
Copy
// This schema will store historical price snapshots
export const priceFeedSchema =
'uint64 timestamp, int256 price, uint80 roundId, string pair'
Copy
import { parseAbi, Address } from 'viem'
import { sepoliaPublicClient } from './clients'
// Chainlink ETH/USD Feed on Sepolia Testnet
const CHAINLINK_FEED_ADDRESS: Address = '0x694AA1769357215DE4FAC081bf1f309aDC325306'
// Minimal ABI for AggregatorV3Interface
const CHAINLINK_ABI = parseAbi([\
'function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)',\
'function decimals() external view returns (uint8)',\
])
export interface PriceData {
roundId: bigint
price: bigint
timestamp: bigint
decimals: number
}
/**
* Fetches the latest price data from the Chainlink ETH/USD feed on Sepolia.
*/
export async function fetchLatestPrice(): Promise {
console.log('Fetching latest price from Chainlink on Sepolia...')
try {
const [roundData, decimals] = await Promise.all([\
sepoliaPublicClient.readContract({\
address: CHAINLINK_FEED_ADDRESS,\
abi: CHAINLINK_ABI,\
functionName: 'latestRoundData',\
}),\
sepoliaPublicClient.readContract({\
address: CHAINLINK_FEED_ADDRESS,\
abi: CHAINLINK_ABI,\
functionName: 'decimals',\
})\
])
const [roundId, answer, , updatedAt] = roundData
console.log(`Chainlink data received: Round ${roundId}, Price ${answer}`)
return {
roundId,
price: answer,
timestamp: updatedAt,
decimals,
}
} catch (error: any) {
console.error(`Failed to read from Chainlink: ${error.message}`)
throw error
}
}
Copy
import 'dotenv/config'
import { somniaSdk } from '../lib/clients'
import { priceFeedSchema } from '../lib/schema'
import { fetchLatestPrice } from '../lib/chainlinkReader'
import { SchemaEncoder, zeroBytes32 } from '@somnia-chain/streams'
import { toHex, Hex } from 'viem'
import { waitForTransactionReceipt } from 'viem/actions'
const PAIR_NAME = "ETH/USD"
async function main() {
console.log('--- Starting Snapshot Bot ---')
// 1. Initialize SDK and Encoder
const sdk = somniaSdk
const encoder = new SchemaEncoder(priceFeedSchema)
const publisherAddress = sdk.wallet.account?.address
if (!publisherAddress) throw new Error('Wallet client not initialized.')
// 2. Compute Schema ID and Register (idempotent)
const schemaId = await sdk.streams.computeSchemaId(priceFeedSchema)
if (!schemaId) throw new Error('Could not compute schemaId')
const ignoreAlreadyRegisteredSchemas = true
const regTx = await sdk.streams.registerDataSchemas([\
{ id: 'price-feed-v1', schema: priceFeedSchema }\
], ignoreAlreadyRegisteredSchemas)
if (!regTx) throw new Error('Failed to register schema')
await waitForTransactionReceipt(sdk.public, { hash: regTx })
// 3. Fetch data from Chainlink
const priceData = await fetchLatestPrice()
// 4. Encode data for Somnia Streams
const encodedData: Hex = encoder.encodeData([\
{ name: 'timestamp', value: priceData.timestamp.toString(), type: 'uint64' },\
{ name: 'price', value: priceData.price.toString(), type: 'int256' },\
{ name: 'roundId', value: priceData.roundId.toString(), type: 'uint80' },\
{ name: 'pair', value: PAIR_NAME, type: 'string' },\
])
// 5. Create a unique Data ID (using the roundId to prevent duplicates)
const dataId = toHex(`price-${PAIR_NAME}-${priceData.roundId}`, { size: 32 })
// 6. Publish to Somnia Data Streams
console.log(`Publishing price data to Somnia Streams...`)
const txHash = await sdk.streams.set([\
{ id: dataId, schemaId, data: encodedData }\
])
if (!txHash) throw new Error('Failed to publish to Streams')
await waitForTransactionReceipt(sdk.public, { hash: txHash })
console.log('\n--- Snapshot Complete! ---')
console.log(` Publisher: ${publisherAddress}`)
console.log(` Schema ID: ${schemaId}`)
console.log(` Data ID: ${dataId}`)
console.log(` Tx Hash: ${txHash}`)
}
main().catch((e) => {
console.error(e)
process.exit(1)
})
Copy
import 'dotenv/config'
import { somniaSdk } from '../lib/clients'
import { priceFeedSchema } from '../lib/schema'
import { SchemaDecodedItem } from '@somnia-chain/streams'
// Helper to decode the SDK's output
interface PriceRecord {
timestamp: number
price: bigint
roundId: bigint
pair: string
}
function decodePriceRecord(row: SchemaDecodedItem[]): PriceRecord {
const val = (field: any) => field?.value?.value ?? field?.value ?? ''
return {
timestamp: Number(val(row[0])),
price: BigInt(val(row[1])),
roundId: BigInt(val(row[2])),
pair: String(val(row[3])),
}
}
async function main() {
console.log('--- Reading Historical Price Feed from Somnia Streams ---')
const sdk = somniaSdk
// Use the *publisher address* from your .env file
const publisherAddress = sdk.wallet.account?.address
if (!publisherAddress) throw new Error('Wallet client not initialized.')
const schemaId = await sdk.streams.computeSchemaId(priceFeedSchema)
if (!schemaId) throw new Error('Could not compute schemaId')
console.log(`Reading all data for publisher: ${publisherAddress}`)
console.log(`Schema: ${schemaId}\n`)
// Fetch all data for this schema and publisher
const data = await sdk.streams.getAllPublisherDataForSchema(schemaId, publisherAddress)
if (!data || data.length === 0) {
console.log('No price history found. Run the snapshot bot first.')
return
}
const records = (data as SchemaDecodedItem[][]).map(decodePriceRecord)
// Sort by timestamp
records.sort((a, b) => a.timestamp - b.timestamp)
console.log(`Found ${records.length} historical price points:\n`)
records.forEach(record => {
// We assume the decimals are 8 for this ETH/USD feed
const priceFloat = Number(record.price) / 10**8
console.log(
`[${new Date(record.timestamp * 1000).toISOString()}] ${record.pair} - $${priceFloat.toFixed(2)} (Round: ${record.roundId})`
)
})
}
main().catch((e) => {
console.error(e)
process.exit(1)
})
Copy
--- Reading Historical Price Feed from Somnia Streams ---
...
Found 3 historical price points:
[2025-11-06T14:30:00.000Z] ETH/USD - $3344.50 (Round: 110...)
[2025-11-06T14:35:00.000Z] ETH/USD - $3362.12 (Round: 111...)
[2025-11-06T14:40:00.000Z] ETH/USD - $3343.90 (Round: 112...)
---
# On Ramps | Somnia Docs
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/on-ramps#banxa)
[Banxaarrow-up-right](https://banxa.com/)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
Seamlessly integrate crypto-fiat exchange, transfer, and compliance solutions to expand globally and unlock new revenue streams.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/on-ramps#resources)
Resources
[Documentationarrow-up-right](https://docs.banxa.com/docs/tutorial)
[PreviousSDKschevron-left](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/sdks)
[NextAccount Abstractionchevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/account-abstraction)
Last updated 5 months ago
* [Banxa](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/on-ramps#banxa)
* [Resources](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/on-ramps#resources)
---
# Somnia Gas Differences To Ethereum | Somnia Docs
Somnia's gas costs work very differently to Ethereum's.
For background: EVM gas fees are designed to approximate the load placed on the blockchain. They are used to charge users in proportion to the system resources they consume. A transaction's gas fee (in ETH or SOMI) is calculated as `number of units of gas * price per unit of gas`. The number of units of gas is just the sum of the gas costs of the transaction's operations.
Because the price of a unit of gas is variable, the absolute cost of an operation in units of gas does not ultimately matter. What matters is the relative difference between operations' gas costs. In other words, if you doubled the gas costs for all operations, and halved the price per unit of gas, there would be no functional change.
In regards to determining gas costs, there are two fundamental differences between Ethereum and Somnia:
1. Somnia's architecture and implementation has drastically reduced the real-world performance cost of most EVM operations, but not all. A few operations, such as third-party library precompiles, are just as performant on Ethereum as on Somnia - both use the same code. This relative difference requires us to adjust the relative gas costs of these operations. We could either reduce the gas costs of all operations that Somnia has improved, or increase the gas costs of all operations that Somnia has _not_ improved - the result is equivalent. Somnia does the latter, as it fits better into the Ethereum developer ecosystem.
2. Ethereum charges a flat gas cost to read any existing value from storage for the first time in a transaction. This ends up hitting a database whose latency for this read can vary by multiple orders of magnitude. This gas cost inaccuracy is a big problem when scaling these blockchains, as it requires them to leave massive amounts of unused buffer in their execution budget to account for these unpredictable latencies. Somnia has built a new database, IceDb, that is designed to fix this problem. Only the (rare) reads which actually take a long time have a high gas cost; reading from cached state is far cheaper.
These two factors mean that Somnia requires different gas semantics to Ethereum. The differences are detailed in this document.
[hashtag](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum#precompiles)
Precompiles
------------------------------------------------------------------------------------------------------------------------------------------
These precompiles have flat multiples applied to [the same gas calculation that Ethereum usesarrow-up-right](https://www.evm.codes/precompiled)
.
* `ecRecover (0x01)` costs **50** times the Ethereum calculated gas usage. (i.e. **150,000** gas instead of **3000** gas.)
* `SHA2-256 (0x02)` costs **50** times the Ethereum calculated gas usage.
* `RIPEMD-160 (0x03)` costs **10** times the Ethereum calculated gas usage.
* `modexp (0x05)` costs **10** times the Ethereum calculated gas usage.
* `ecAdd (0x06)` costs **50** times the Ethereum calculated gas usage.
* `ecMul (0x07)` costs **10** times the Ethereum calculated gas usage.
* `ecPairing (0x08)` costs **250** times the Ethereum calculated gas usage.
* `blake2f (0x09)` costs **10** times the Ethereum calculated gas usage.
* `point evaluation (0x0a)` costs **50** times the Ethereum calculated gas usage.
BLS operations:
* `BLS12_G1ADD (0x0b)` costs **220** times the Ethereum calculated gas usage.
* `BLS12_G1MSM (0x0c)` costs **110** times the Ethereum calculated gas usage.
* `BLS12_G2ADD (0x0d)` costs **330** times the Ethereum calculated gas usage.
* `BLS12_G2MSM (0x0e)` costs **50** times the Ethereum calculated gas usage.
* `BLS12_PAIRING_CHECK (0x0f)` costs **50** times the Ethereum calculated gas usage.
* `BLS12_MAP_FP_TO_G1 (0x10)` costs **120** times the Ethereum calculated gas usage.
* `BLS12_MAP_FP2_TO_G2 (0x11)` costs **50** times the Ethereum calculated gas usage.
[hashtag](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum#non-storage-evm-operations)
Non-storage EVM operations
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
* `SELFBALANCE` costs **305** gas instead of **5** gas.
* `KECCAK256` costs `1250 + 300 * minimum_word_size` gas instead of `30 + 6 * minimum_word_size` gas.
* `ADDMOD` costs **358** gas instead of **8** gas.
* `MULMOD` costs **358** gas instead of **8** gas.
[hashtag](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum#storage-evm-operations)
Storage EVM operations
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Ethereum uses an [access list based gas modelarrow-up-right](https://www.evm.codes/about#access_list)
for all storage operations, to charge extra gas depending on if the value being accessed has already been accessed _in the same transaction_, and if it is creating or deleting that state.
As discussed above, Somnia instead charges extra gas based on a more accurate model of the real world latency it takes to read or write the value in question:
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum#sload)
`SLOAD`
* If the storage slot key is in the set of most recently accessed **128 million** contract slot keys, **no extra gas is charged** (on top of the static op gas fee, which is **100** gas).
* If the key does not exist, the access requires **at least 1,000,000 gas remaining**, but that gas is **not charged**, so there is no extra cost.
* Otherwise, the read costs an additional **1,000,000** gas.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum#sstore)
`SSTORE`
* If the storage slot key is in the set of most recently accessed **128 million** contract slot keys, **no extra gas is charged** (on top of the static op gas fee, which is **100** gas).
* If the key does not exist and is being _written to zero_, the access requires **at least 1,000,000 gas remaining**, but that gas is **not charged**, so there is no extra cost.
* If the key does not exist and is being _written to a non-zero value_, the access requires **at least 1,000,000 gas remaining**, but the caller is charged **200,000** gas.
* Otherwise, the write costs an additional **1,000,000** gas.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum#account-operations)
Account operations
The following EVM operations access account state:
* `BALANCE`
* `EXTCODESIZE`
* `EXTCODECOPY`
* `EXTCODEHASH`
* `CALL`
* `CALLCODE`
* `DELEGATECALL`
* `STATICCALL`
* `CREATE`
* `CREATE2`
* `SELFDESTRUCT`
These have an additional storage gas fee (in place of Ethereum's dynamic access list gas fee):
* If the account is in the set of most recently accessed **32 million** accounts, **no extra gas is charged**.
* If the account does not exist and is being _read_, the access requires **at least 1,000,000 gas remaining**, but that gas is **not charged**, so there is no extra cost.
* If the account does not exist and is being _created_, the access requires **at least 1,000,000 gas remaining**, but the caller is charged **400,000** gas for the new account.
* Otherwise, the access costs **1,000,000** gas.
[hashtag](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum#evm-logs)
EVM Logs
------------------------------------------------------------------------------------------------------------------------------------
Somnia charges an increased amount of gas for EVM logs due to the historical storage requirements.
Ethereum charges: `375 + 375 * topic_count + 8 * size + memory_expansion_cost` gas
Somnia charges: `3200 + 5120 * topic_count + 160 * size + memory_expansion_cost` gas
To illustrate these changes with the five different EVM log operations:
EVM Op
Ethereum gas
Somnia gas
LOG0 with 32 bytes of data
631
8,320
LOG1 with 32 bytes of data
1,006
13,440
LOG2 with 32 bytes of data
1,381
18,560
LOG3 with 32 bytes of data
1,756
23,680
LOG4 with 32 bytes of data
2,131
28,800
[hashtag](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum#contract-bytecode-deployment-cost)
Contract bytecode deployment cost
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Ethereum charges **200** gas per byte of deployed bytecode. Somnia charges **3125** gas per byte of deployed bytecode.
[hashtag](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum#eip-7702-delegation-authorisations)
EIP-7702 delegation authorisations
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Ethereum charges **25,000** gas per authorisation in a [Set Code transactionarrow-up-right](https://eips.ethereum.org/EIPS/eip-7702)
. **12,500** gas is refunded if account creation isn't required.
Somnia charges **1,570,000** gas per authorisation in a Set Code transaction. **400,000** gas is refunded if account creation isn't required.
[PreviousExplorer API Health and Monitoringchevron-left](https://docs.somnia.network/developer/deployment-and-production/explorer-api-health-and-monitoring)
[NextEcosystemchevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem)
Last updated 14 days ago
* [Precompiles](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum#precompiles)
* [Non-storage EVM operations](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum#non-storage-evm-operations)
* [Storage EVM operations](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum#storage-evm-operations)
* [SLOAD](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum#sload)
* [SSTORE](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum#sstore)
* [Account operations](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum#account-operations)
* [EVM Logs](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum#evm-logs)
* [Contract bytecode deployment cost](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum#contract-bytecode-deployment-cost)
* [EIP-7702 delegation authorisations](https://docs.somnia.network/developer/deployment-and-production/somnia-gas-differences-to-ethereum#eip-7702-delegation-authorisations)
---
# READ Stream Data from a UI (Next.js Example) | Somnia Docs
In this guide, you’ll learn how to read data published to Somnia Data Streams directly from a Next.js frontend, the same way you’d use readContract with Viem.
We’ll build a simple HelloWorld schema and use it to demonstrate all the READ methods in the Somnia Data Streams SDK, from fetching the latest message to retrieving complete datasets or schema metadata.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#prerequisites)
Prerequisites
---------------------------------------------------------------------------------------------------------------------------------------------------
Before we begin, make sure you have:
Copy
npm i @somnia-chain/streams viem
Also ensure:
* Node.js 20+
* A Somnia Testnet wallet with STT test tokens
* `.env` file containing your wallet credentials:
* A working Next.js app (npx create-next-app somnia-streams-read)
* Access to a publisher address and schema ID (or one you’ve created earlier)
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#set-up-the-sdk-and-client)
Set up the SDK and Client
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
We’ll initialize the SDK using Viem’s createPublicClient to communicate with Somnia’s blockchain.
Copy
// lib/store.ts
import { SDK } from '@somnia-chain/streams'
import { createPublicClient, http } from 'viem'
import { somniaTestnet } from 'viem/chains'
const publicClient = createPublicClient({
chain: somniaTestnet,
transport: http(),
})
export const sdk = new SDK(publicClient)
This sets up the data-reading connection between your frontend and the Somnia testnet.
Think of it as the Streams version of [readContract()arrow-up-right](https://viem.sh/docs/contract/readContract#readcontract)
; it lets you pull structured data (not just variables) directly from the blockchain.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#define-schema-and-publisher)
Define Schema and Publisher
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
A schema describes the structure of data stored in Streams, just like how a smart contract defines the structure of state variables.
If you don’t have the schema ID handy, you can generate it from its definition:
This ensures that you’re referencing the same schema ID under which the data was published.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#fetch-latest-hello-world-message)
Fetch Latest “Hello World” Message
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
This is the most common use case: getting the most recent data point. For example, displaying the latest sensor reading or chat message.
This method retrieves the newest record from that schema-publisher combination.
It’s useful when:
* You’re showing a live dashboard
* You need real-time data polling
* You want to auto-refresh a view (e.g., “Last Updated at…”)
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#fetch-by-key-e.g.-message-id)
Fetch by Key (e.g., message ID)
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Each record can have a unique key, such as a message ID, sensor UUID, or user reference. When you know that key, you can fetch the exact record.
When to use:
* Fetching a message by its ID (e.g., “message #45a1”)
* Retrieving a transaction or sensor entry when you know its hash
* Building a detail view (e.g., /message/\[id\] route in Next.js)
Think of it like calling readContract for one item by ID.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#fetch-by-index-sequential-logs)
Fetch by Index (Sequential Logs)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
In sequential datasets such as logs, chat history, and telemetry, each record is indexed numerically. You can fetch a specific record by its position:
When to use:
* When looping through entries in order (0, 1, 2, ...)
* To replay logs sequentially
* To test pagination logic
Example: getAtIndex(schemaId, publisher, 0n) retrieves the very first message.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#fetch-a-range-of-records-paginated-view)
Fetch a Range of Records (Paginated View)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
You can fetch multiple entries at once using index ranges. This is perfect for pagination or time-series queries.
Example Use Cases:
* Displaying the last 10 chat messages: getBetweenRange(schemaId, publisher, 0n, 10n)
* Loading older telemetry data
* Implementing infinite scroll
circle-info
Tip: Treat start and end like array indices (inclusive start, exclusive end). `start` is inclusive and `end` is exclusive.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#fetch-all-publisher-data-for-a-schema)
Fetch All Publisher Data for a Schema
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
If you want to retrieve all content a publisher has ever posted to a given schema, use this.
When to use:
* Generating analytics or trend charts
* Migrating or syncing full datasets
* Debugging data integrity or history
You can think of this as:
“Give me the entire dataset under this schema from this publisher.”
It’s the Streams equivalent of querying all events from a contract.
circle-info
This should be used for small data sets. For larger, paginated reading, `getBetweenRange` is recommended not to overwhelm the node returning the data.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#count-total-entries)
Count Total Entries
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Sometimes, you just want to know how many entries exist.
When to use:
* To know the total record count for pagination
* To display dataset stats (“42 entries recorded”)
* To monitor the growth of a stream
This helps determine boundaries for getBetweenRange() or detect when new data arrives.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#inspect-schema-metadata)
Inspect Schema Metadata
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
Schemas define structure, and sometimes you’ll want to validate or inspect them before reading data. First, check that a schema exists when publishing a new schema:
This is critical to ensure your app doesn’t attempt to query a non-existent or unregistered schema — useful for user-facing dashboards.
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#retrieve-full-schema-information)
Retrieve Full Schema Information
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
This method retrieves both the base schema and its extended structure, if any. It automatically resolves inherited schemas, so you get the full picture of what fields exist.
Example output:
This is important when you’re visualizing or decoding raw stream data, you can use the schema structure to parse fields correctly (timestamp, string, address, etc.).
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#example-next.js-app)
Example Next.js App
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Now let’s render our fetched data in the UI.
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#project-setup)
Project Setup
* * *
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#folder-structure)
Folder Structure
* * *
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#lib-store.ts)
lib/store.ts
Sets up the Somnia SDK and connects to the testnet.
* * *
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#lib-schema.ts)
lib/schema.ts
Defines your schema and publisher.
If you don’t know your schema ID yet, you can compute it later using:
* * *
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#lib-read.ts)
lib/read.ts
Implements read helpers for your API and UI.
* * *
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#app-api-latest-route.ts)
app/api/latest/route.ts
A serverless route to fetch the latest message (you can add more routes for range or schema info).
* * *
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#components-streamviewer.tsx)
components/StreamViewer.tsx
A live component with interactive buttons for fetching data.
chevron-rightStreamViewer.tsx[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#streamviewer.tsx)
* * *
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#components-schemainfo.tsx)
components/SchemaInfo.tsx
Displays the schema metadata.
* * *
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#app-page.tsx)
app/page.tsx
Main dashboard combining both components.
* * *
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#app-layout.tsx)
app/layout.tsx
Wraps the layout globally.
* * *
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#run-the-app)
Run the App
Visit http://localhost:3000 to open your dashboard. You’ll see a **“Fetch Latest Message”** button that retrieves data via `/api/latest` and a **Schema Info** section (ready to expand)
* * *
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#summary-table)
Summary Table
---------------------------------------------------------------------------------------------------------------------------------------------------
Method
Purpose
Example
getByKey
Fetch a record by unique ID
getByKey(schemaId, publisher, dataId)
getAtIndex
Fetch record at position
getAtIndex(schemaId, publisher, 0n)
getBetweenRange
Retrieve records in range
getBetweenRange(schemaId, publisher, 0n, 10n)
getAllPublisherDataForSchema
Fetch all data by publisher
getAllPublisherDataForSchema(schemaRef, publisher)
getLastPublishedDataForSchema
Latest record only
getLastPublishedDataForSchema(schemaId, publisher)
totalPublisherDataForSchema
Count of entries
totalPublisherDataForSchema(schemaId, publisher)
isDataSchemaRegistered
Check if schema exists
isDataSchemaRegistered(schemaId)
schemaIdToId / idToSchemaId
Convert between Hex and readable
Useful for UI & schema mapping
getSchemaFromSchemaId
Inspect full schema definition
Retrieves base + extended schema
[PreviousStreams Case Study: Formula 1chevron-left](https://docs.somnia.network/developer/data-streams/tutorials/streams-case-study-formula-1)
[NextIntegrate Chainlink Oracleschevron-right](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles)
Last updated 1 month ago
* [Prerequisites](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#prerequisites)
* [Set up the SDK and Client](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#set-up-the-sdk-and-client)
* [Define Schema and Publisher](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#define-schema-and-publisher)
* [Fetch Latest “Hello World” Message](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#fetch-latest-hello-world-message)
* [Fetch by Key (e.g., message ID)](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#fetch-by-key-e.g.-message-id)
* [Fetch by Index (Sequential Logs)](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#fetch-by-index-sequential-logs)
* [Fetch a Range of Records (Paginated View)](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#fetch-a-range-of-records-paginated-view)
* [Fetch All Publisher Data for a Schema](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#fetch-all-publisher-data-for-a-schema)
* [Count Total Entries](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#count-total-entries)
* [Inspect Schema Metadata](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#inspect-schema-metadata)
* [Retrieve Full Schema Information](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#retrieve-full-schema-information)
* [Example Next.js App](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#example-next.js-app)
* [Project Setup](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#project-setup)
* [Folder Structure](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#folder-structure)
* [lib/store.ts](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#lib-store.ts)
* [lib/schema.ts](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#lib-schema.ts)
* [lib/read.ts](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#lib-read.ts)
* [app/api/latest/route.ts](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#app-api-latest-route.ts)
* [components/StreamViewer.tsx](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#components-streamviewer.tsx)
* [components/SchemaInfo.tsx](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#components-schemainfo.tsx)
* [app/page.tsx](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#app-page.tsx)
* [app/layout.tsx](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#app-layout.tsx)
* [Run the App](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#run-the-app)
* [Summary Table](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example#summary-table)
Copy
// lib/schema.ts
export const helloWorldSchema = 'uint64 timestamp, string message'
export const schemaId = '0xabc123...' // Example Schema ID
export const publisher = '0xF9D3...E5aC' // Example Publisher Address
Copy
const computedId = await sdk.streams.computeSchemaId(helloWorldSchema)
console.log('Computed Schema ID:', computedId)
Copy
// lib/read.ts
import { sdk } from './store'
import { schemaId, publisher } from './schema'
export async function getLatestMessage() {
const latest = await sdk.streams.getLastPublishedDataForSchema(schemaId, publisher)
console.log('Latest data:', latest)
return latest
}
Copy
export async function getMessageById(messageKey: `0x${string}`) {
const msg = await sdk.streams.getByKey(schemaId, publisher, messageKey)
console.log('Message by key:', msg)
return msg
}
Copy
export async function getMessageAtIndex(index: bigint) {
const record = await sdk.streams.getAtIndex(schemaId, publisher, index)
console.log(`Record at index ${index}:`, record)
return record
}
Copy
export async function getMessagesInRange(start: bigint, end: bigint) {
const records = await sdk.streams.getBetweenRange(schemaId, publisher, start, end)
console.log('Records in range:', records)
return records
}
Copy
export async function getAllPublisherData() {
const allData = await sdk.streams.getAllPublisherDataForSchema(schemaId, publisher)
console.log('All publisher data:', allData)
return allData
}
Copy
export async function getTotalEntries() {
const total = await sdk.streams.totalPublisherDataForSchema(schemaId, publisher)
console.log(`Total entries: ${total}`)
return Number(total)
}
Copy
const ignoreAlreadyRegistered = true
try {
const txHash = await sdk.streams.registerDataSchemas(
[\
{\
schemaName: 'hello_world',\
schema: helloSchema,\
parentSchemaId: zeroBytes32\
},\
],
ignoreAlreadyRegistered
)
if (txHash) {
await waitForTransactionReceipt(publicClient, { hash: txHash })
console.log(`Schema registered or confirmed, Tx: ${txHash}`)
} else {
console.log('Schema already registered — no action required.')
}
} catch (err) {
// fallback: if the SDK doesn’t support the flag yet
if (String(err).includes('SchemaAlreadyRegistered')) {
console.log('Schema already registered. Continuing...')
} else {
throw err
}
}
Copy
const schemaInfo = await sdk.streams.getSchemaFromSchemaId(schemaId)
console.log('Schema Info:', schemaInfo)
Copy
{
baseSchema: 'uint64 timestamp, string message',
finalSchema: 'uint64 timestamp, string message',
schemaId: '0xabc123...'
}
Copy
npx create-next-app somnia-streams-reader --typescript
cd somnia-streams-reader
npm install @somnia-chain/streams viem
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Copy
somnia-streams-reader/
├── app/
│ ├── api/
│ │ └── latest/route.ts
│ ├── page.tsx
│ ├── layout.tsx
│ └── globals.css
├── components/
│ ├── StreamViewer.tsx
│ └── SchemaInfo.tsx
├── lib/
│ ├── store.ts
│ ├── schema.ts
│ └── read.ts
├── tailwind.config.js
└── package.json
Copy
import { SDK } from "@somnia-chain/streams"
import { createPublicClient, http } from "viem"
import { somniaTestnet } from "viem/chains"
const publicClient = createPublicClient({
chain: somniaTestnet,
transport: http(),
})
export const sdk = new SDK(publicClient)
Copy
export const helloWorldSchema = "uint64 timestamp, string message"
export const schemaId = "0xabc123..." // replace with actual schemaId
export const publisher = "0xF9D3...E5aC" // replace with actual publisher
Copy
const computed = await sdk.streams.computeSchemaId(helloWorldSchema)
console.log("Schema ID:", computed)
Copy
import { sdk } from "./store"
import { schemaId, publisher } from "./schema"
export async function getLatestMessage() {
return await sdk.streams.getLastPublishedDataForSchema(schemaId, publisher)
}
export async function getMessagesInRange(start: bigint, end: bigint) {
return await sdk.streams.getBetweenRange(schemaId, publisher, start, end)
}
export async function getSchemaInfo() {
return await sdk.streams.getSchemaFromSchemaId(schemaId)
}
Copy
import { NextResponse } from "next/server"
import { getLatestMessage } from "@/lib/read"
export async function GET() {
const data = await getLatestMessage()
return NextResponse.json({ data })
}
Copy
"use client"
import { useState } from "react"
export default function StreamViewer() {
const [data, setData] = useState(null)
const [loading, setLoading] = useState(false)
const fetchLatest = async () => {
setLoading(true)
try {
const res = await fetch("/api/latest")
const { data } = await res.json()
setData(data)
} catch (err) {
console.error(err)
} finally {
setLoading(false)
}
}
return (
HelloWorld Stream Reader
{data && (
{JSON.stringify(data, null, 2)}
)}
)
}
Copy
"use client"
import { useState } from "react"
export default function SchemaInfo() {
const [info, setInfo] = useState(null)
const fetchInfo = async () => {
const res = await fetch("/api/latest") // just for demo; replace with /api/schema if separate route
const { data } = await res.json()
setInfo(data)
}
return (
Schema Information
{info && (
{JSON.stringify(info, null, 2)}
)}
)
}
Copy
import StreamViewer from "@/components/StreamViewer"
import SchemaInfo from "@/components/SchemaInfo"
export default function Home() {
return (
🛰️ Somnia Data Streams Reader
)
}
Copy
import "./globals.css"
export const metadata = {
title: "Somnia Streams Reader",
description: "Read on-chain data from Somnia Data Streams",
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
{children}
)
}
Copy
npm run dev
---
# DAO UI Tutorial p2 | Somnia Docs
This guide will focus exclusively on implementing Read Operations, which fetches data from your deployed [DAO Smart Contract](https://docs.somnia.network/developer/building-dapps/example-applications/dao-smart-contract)
. By the end of this article, you’ll be able to:
1. Understand how to read data from your smart contract using [viem](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library)
.
2. Implement functions to fetch the total number of proposals and specific proposal details.
3. Integrate these READ operations into your Next.js pages to display dynamic data.
Prerequisite: Ensure you’ve completed [Part 1](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p1)
of this series, where you initialized a Next.js project, set up a `**WalletContext**` for global state management, and added a global `**NavBar**`.
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#understand-read-operations)
Understand READ Operations
------------------------------------------------------------------------------------------------------------------------------------------------------------------
In decentralized applications (dApps), Read Operations involve fetching data from the blockchain without altering its state. In the example DAO Smart Contract, this is crucial for displaying dynamic information such as:
* **Total Number of Proposals**: How many proposals have been created.
* **Proposal Details**: Information about a specific proposal, including its description, votes, and execution status.
These operations are read-only and do not require the user to sign any transactions, making them free of gas costs.
We’ll use the viem library to interact with our smart contract and perform these READ operations.
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#expand-walletcontext.js-for-read-operations)
Expand walletcontext.js for Read Operations
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
`**walletcontext.js**` is the central hub for managing wallet connections and interacting with your Smart Contract. We’ll add two primary READ functions:
1. `**fetchTotalProposals()**`: Retrieves the total number of proposals created.
2. `**fetchProposal(proposalId)**`: Fetches details of a specific proposal by its ID.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#fetch-total-proposals)
Fetch Total Proposals
Functionality: This function calls the `**totalProposals**` method in your smart contract to determine how many proposals have been created so far.
chevron-rightcontexts/walletcontext.js[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#contexts-walletcontext.js)
`**fetchTotalProposals()**` uses `**publicClient.readContract**` to call the `**totalProposals**` function in the Smart Contract. This function returns a `BigInt` representing the total number of proposals. `**fetchProposal(proposalId)**` calls the proposals mapping in your contract to retrieve details of a specific proposal by its `ID`. It returns a struct containing the proposal's **description**, **deadline**, **votes**, **execution status**, and **proposer**.
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#integrate-read-operations-into-pages)
Integrate Read Operations into Pages
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
With the read functions in place, let’s integrate them into Next.js pages to display dynamic data.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#home-page)
Home Page
Update the `index.js` page to show the total number of proposals created in your DAO on the home page.
chevron-rightpages/index.js[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#pages-index.js)
Here we set the `totalProposals` state variable to store the fetched total number of proposals. The `**ConnectButton**` implements the MetaMask authentication. The useWallet hook parse the function from `**WalletContext.**`
The `**useEffect**` Hook is applied on component mount, `**fetchTotalProposals()**` is then called to retrieve the total number of proposals from the Smart Contract.
The page displays a loading message until totalProposals is fetched. Once fetched, it displays the total number of proposals. Users will have to click the **ConnectButton** to connect their wallets for WRITE operations. See part 3.
* * *
###
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#fetch-proposal-page)
Fetch-Proposal Page
This page allow users to input a proposal ID, fetch its details, and display them. Additionally, on the page users are provided options for voting on or executing the proposal.
Implementation:
chevron-rightpages/fetch-proposal.js[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#pages-fetch-proposal.js)
The following React states are implemented
* `proposalId`: Stores the user-inputted proposal ID.
* `proposalData`: Stores the fetched proposal details.
* `error`: Captures any errors during fetch, vote, or execute operations.
The `**handleSubmit**` function is used to validate the input and connection status. It then calls the `**fetchProposal(proposalId)**` to retrieve proposal details.
We use a **Form** element for users to input a proposal ID and fetch its details. The **Error** Display is implemented to show any errors that occur during operations. The **Proposal Details** displays the fetched proposal information in a styled card.
The card contains **Vote** and **Execute b**uttons for users to vote **YES/NO** or execute the proposal if eligible.
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#edge-cases-and-errors)
Edge Cases and Errors
--------------------------------------------------------------------------------------------------------------------------------------------------------
For better UX, consider adding loading indicators while fetching data or awaiting transaction confirmations.
Example:
chevron-rightpages/fetch-proposal.js[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#pages-fetch-proposal.js-1)
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#test-read-operations)
Test Read Operations
------------------------------------------------------------------------------------------------------------------------------------------------------
###
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#populate-some-data)
Populate Some Data
Before testing read operations, make sure there are some proposals created:
1. Load your Smart Contract on the [Remix IDE](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc20-tokens)
.
2. Deposit 0.001 ETH to gain voting power.
3. Create one or more proposals via the Create Proposal page.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#verify-read-operations)
Verify Read Operations
Run your application using the command:
Your application will be running on `**localhost:3000**` in your web browser. Check for the following in the User Interface:
1. Total Proposals: On the Home page, verify that the total number of proposals matches the number you’ve created via Remix IDE.
2. Fetch Proposal Details: - Navigate to the Fetch-Proposal page. - Input a valid proposalId (e.g., 0 for the first proposal). - Verify that all proposal details are accurately displayed.
Monitor the browser console for any errors or logs that can help in debugging.
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#conclusion-and-next-steps)
Conclusion and Next Steps
----------------------------------------------------------------------------------------------------------------------------------------------------------------
In Part 2, you successfully implemented Read Operations in your DAO front end:
* `**fetchTotalProposals()**`: Displayed the total number of proposals on the Home page.
* **fetchProposal(proposalId)**: Retrieved and displayed specific proposal details on the Fetch-Proposal page.
####
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#whats-next)
What's Next?
Stay tuned for Part 3 of this series, where we’ll dive into building UI Components—crafting forms, buttons, and enhancing event handling to create a more polished and user-friendly interface for your DAO dApp.
* * *
Congratulations! You’ve now built a robust foundation for reading data from your DAO smart contract within your Next.js front end. Keep experimenting and enhancing your dApp’s capabilities in the upcoming sections!
[PreviousDAO UI Tutorial p1chevron-left](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p1)
[NextDAO UI Tutorial p3chevron-right](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p3)
Last updated 1 month ago
* [Understand READ Operations](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#understand-read-operations)
* [Expand walletcontext.js for Read Operations](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#expand-walletcontext.js-for-read-operations)
* [Fetch Total Proposals](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#fetch-total-proposals)
* [Integrate Read Operations into Pages](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#integrate-read-operations-into-pages)
* [Home Page](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#home-page)
* [Fetch-Proposal Page](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#fetch-proposal-page)
* [Edge Cases and Errors](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#edge-cases-and-errors)
* [Test Read Operations](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#test-read-operations)
* [Populate Some Data](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#populate-some-data)
* [Verify Read Operations](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#verify-read-operations)
* [Conclusion and Next Steps](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p2#conclusion-and-next-steps)
Copy
import { createContext, useContext, useState } from "react";
import {
defineChain,
createPublicClient,
createWalletClient,
http,
custom,
parseEther,
} from "viem";
import { ABI } from "../../abi"; // Adjust the path as necessary
// Define Somnia Chain
const SOMNIA = defineChain({
id: 50312,
name: "Somnia Testnet",
nativeCurrency: {
decimals: 18,
name: "Ether",
symbol: "STT",
},
rpcUrls: {
default: {
http: ["https://dream-rpc.somnia.network"],
},
},
blockExplorers: {
default: { name: "Explorer", url: "https://somnia-devnet.socialscan.io" },
},
});
// Create a public client for read operations
const publicClient = createPublicClient({
chain: SOMNIA,
transport: http(),
});
const WalletContext = createContext();
export function WalletProvider({ children }) {
// ---------- STATE ------------
const [connected, setConnected] = useState(false);
const [address, setAddress] = useState("");
const [client, setClient] = useState(null);
// Fetch Total Proposals
async function fetchTotalProposals() {
try {
const result = await publicClient.readContract({
address: "0x7be249A360DB86E2Cf538A6893f37aFd89C70Ab4",
abi: ABI,
functionName: "totalProposals",
});
return result; // Returns a BigInt
} catch (error) {
console.error("Error fetching totalProposals:", error);
throw error;
}
}
// Fetch Proposal Details
async function fetchProposal(proposalId) {
try {
const result = await publicClient.readContract({
address: "0x7be249A360DB86E2Cf538A6893f37aFd89C70Ab4",
abi: ABI,
functionName: "proposals",
args: [parseInt(proposalId)],
});
console.log(result);
return result; // Returns the Proposal struct
} catch (error) {
console.error("Error fetching proposal:", error);
throw error;
}
}
// Provider's value
return (
{children}
);
}
// Custom hook to consume context
export function useWallet() {
return useContext(WalletContext);
}
Copy
import { useState, useEffect } from "react";
import ConnectButton from "../components/connectbutton";
import { useWallet } from "../contexts/walletcontext";
export default function Home() {
const { fetchTotalProposals } = useWallet();
const [totalProposals, setTotalProposals] = useState(null);
useEffect(() => {
async function loadData() {
try {
const count = await fetchTotalProposals();
setTotalProposals(count);
} catch (error) {
console.error("Failed to fetch total proposals:", error);
}
}
loadData();
}, [fetchTotalProposals]);
return (
{/* The NavBar is already rendered in _app.js */}
Welcome to MyDAO
{totalProposals !== null ? (
Total proposals created: {totalProposals.toString()}
) : (
Loading total proposals...
)}
);
}
Copy
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { useWallet } from "../contexts/walletcontext";
import { Button, Card, Label, TextInput } from "flowbite-react"; // Optional Flowbite imports
export default function FetchProposalPage() {
const [proposalId, setProposalId] = useState("");
const [proposalData, setProposalData] = useState(null);
const [error, setError] = useState("");
const { connected, fetchProposal, voteOnProposal, executeProposal } = useWallet();
const handleSubmit = async (e) => {
e.preventDefault();
setError(""); // Clear previous errors
if (!connected) {
alert("You must connect your wallet first!");
return;
}
if (!proposalId.trim()) {
setError("Please enter a proposal ID.");
return;
}
try {
// Fetch the proposal from the contract
const result = await fetchProposal(proposalId);
console.log("Fetched Proposal:", result);
setProposalData(result);
} catch (err) {
console.error("Error fetching proposal:", err);
setError("Failed to fetch proposal. Check console for details.");
}
};
useEffect(() => {
if (proposalData !== null) {
console.log("Updated Proposal Data:", proposalData);
}
}, [proposalData]);
return (
Fetch a Proposal
{/* Form to input Proposal ID */}
{/* Display Errors */}
{error &&
)}
);
}
Copy
const [loading, setLoading] = useState(false);
// In handleSubmit
const handleSubmit = async (e) => {
e.preventDefault();
setError("");
setLoading(true);
// ... rest of the code
setLoading(false);
};
// In the button
Copy
npm run dev
---
# Ecosystem Tools | Somnia Docs
Somnia is built with a robust ecosystem of infrastructure providers that enable developers, projects, and businesses to integrate seamlessly with the network.
With Somnia's ecosystem of infrastructure partners, developers have access to scalable RPCs, smart contract tools, oracles, account abstraction, identity solutions, and analytics.
Each partner provides critical tooling, APIs, and services to power decentralized applications (dApps) on Somnia. Below is an overview of our key Infrastructure Partners and resources to get started.
circle-check
**Developers who are deploying Smart Contracts and need Somnia Test Tokens, STT. Please join the** [**Discord**arrow-up-right](https://discord.com/invite/somnia)
**. Go to the #dev-chat channel, tag the Somnia DevRel,** `**@emma_odia**`**and request Test Tokens. You can also email** `**[[email protected]](https://docs.somnia.network/cdn-cgi/l/email-protection) **` **with a brief description of what you are building and your GitHub profile.**
Service
Provider
[RPC](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/rpc)
Ankr, Public Node, Stakely, Validation Cloud
[Oracles](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/oracles)
DIA
[Subgraphs](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/subgraphs)
Protofire, Ormi
[Wallet Provider](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/wallet-providers)
Privy, Thirdweb
[Safes](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/safes)
Palmera DAO
[Explorers](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/explorers)
Block Scout
[SDKs](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/sdks)
Sequence, Thirdweb
[On Ramps](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/on-ramps)
Banxa
[APIs](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/apis)
Ormi
[Account Abstraction](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/account-abstraction)
Pimlico
[PreviousTokoschevron-left](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/tokos)
[NextRPCchevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/rpc)
Last updated 1 month ago
---
# Using Native SOMI/STT | Somnia Docs
SOMI is the native token of the Somnia Network, similar to ETH on Ethereum. Unlike ERC20 tokens, SOMI is built into the protocol itself and does not have a contract address.
circle-exclamation
Kindly note that the Native Token for Somnia Testnet is STT.
This multi-part guide shows how to use SOMI for:
* Payments
* Escrow
* Donations & Tipping
* Sponsored gas via Account Abstraction
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/using-native-somi-stt#use-somi-for-payments-in-smart-contracts)
Use SOMI for Payments in Smart Contracts
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
A simple contract that accepts exact SOMI payments:
Use `msg.value` to access the native token sent in a transaction. No ERC20 functions are needed.
To withdraw collected SOMI:
Deploy using Hardhat Ignition or Viem and test with a `sendTransaction` call.
chevron-rightExample.sol[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/using-native-somi-stt#example.sol)
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/using-native-somi-stt#build-an-somi-escrow-contract)
Build an SOMI Escrow Contract
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
A secure escrow contract allows a buyer to deposit SOMI and later release or refund:
Release funds to the seller:
Deploy with Hardhat Ignition and pass SOMI as `value` during deployment.
chevron-rightExample.sol[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/using-native-somi-stt#example.sol-1)
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/using-native-somi-stt#somi-tip-jar)
SOMI Tip Jar
------------------------------------------------------------------------------------------------------------------------------------
Allow any wallet to send tips directly:
Withdraw all tips:
chevron-rightExample.sol[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/using-native-somi-stt#example.sol-2)
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/using-native-somi-stt#frontend-tip)
Frontend tip
------------------------------------------------------------------------------------------------------------------------------------
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/using-native-somi-stt#sponsor-somi-transactions-with-account-abstraction)
Sponsor SOMI Transactions with Account Abstraction
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Using a smart account + relayer (e.g. via Privy, Thirdweb), the dApp can cover gas fees:
The Smart Contract function can execute mint or other logic as usual. The paymaster or relayer pays SOMI.
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/using-native-somi-stt#conclusion)
Conclusion
--------------------------------------------------------------------------------------------------------------------------------
* **SOMI is native** and used via `msg.value`, `.transfer()`, and `payable`.
* **There is no contract address for SOMI (STT for Testnet)**.
* You can integrate SOMI into any Solidity or Viem app with no ERC20 logic.
* Account Abstraction enables gasless dApps using SOMI as sponsor currency.
[PreviousManaging NFT Metadata with IPFSchevron-left](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/managing-nft-metadata-with-ipfs)
[NextWallet Integration and Authchevron-right](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth)
Last updated 5 months ago
* [Use SOMI for Payments in Smart Contracts](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/using-native-somi-stt#use-somi-for-payments-in-smart-contracts)
* [Build an SOMI Escrow Contract](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/using-native-somi-stt#build-an-somi-escrow-contract)
* [SOMI Tip Jar](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/using-native-somi-stt#somi-tip-jar)
* [Frontend tip](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/using-native-somi-stt#frontend-tip)
* [Sponsor SOMI Transactions with Account Abstraction](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/using-native-somi-stt#sponsor-somi-transactions-with-account-abstraction)
* [Conclusion](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/using-native-somi-stt#conclusion)
Copy
function payToAccess() external payable {
require(msg.value == 0.01 ether, "Must send exactly 0.01 SOMI");
}
Copy
function withdraw() external onlyOwner {
payable(owner).transfer(address(this).balance);
}
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract SOMIPayment {
address public owner;
constructor() {
owner = msg.sender;
}
// Modifier to restrict access to the contract owner
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this");
_;
}
// User must send exactly 0.01 SOMI to access this feature
function payToAccess() external payable {
require(msg.value == 0.01 ether, "Must send exactly 0.01 SOMI");
// Logic for access: mint token, grant download, emit event, etc.
}
// Withdraw collected SOMI to owner
function withdraw() external onlyOwner {
payable(owner).transfer(address(this).balance);
}
}
Copy
constructor(address payable _seller) payable {
buyer = msg.sender;
seller = _seller;
amount = msg.value;
}
Copy
function release() external onlyBuyer {
seller.transfer(amount);
}
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract SOMIEscrow {
address public buyer;
address payable public seller;
uint256 public amount;
bool public isDeposited;
constructor(address payable _seller) payable {
buyer = msg.sender;
seller = _seller;
amount = msg.value;
require(amount > 0, "Must deposit SOMI");
isDeposited = true;
}
modifier onlyBuyer() {
require(msg.sender == buyer, "Only buyer can call this");
_;
}
function release() external onlyBuyer {
require(isDeposited, "No funds to release");
isDeposited = false;
seller.transfer(amount);
}
function refund() external onlyBuyer {
require(isDeposited, "No funds to refund");
isDeposited = false;
payable(buyer).transfer(amount);
}
}
Copy
receive() external payable {
emit Tipped(msg.sender, msg.value);
}
Copy
function withdraw() external onlyOwner {
payable(owner).transfer(address(this).balance);
}
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract SOMITipJar {
address public owner;
event Tipped(address indexed from, uint256 amount);
event Withdrawn(address indexed to, uint256 amount);
constructor() {
owner = msg.sender;
}
receive() external payable {
emit Tipped(msg.sender, msg.value);
}
function withdraw() external {
require(msg.sender == owner, "Only owner can withdraw");
uint256 balance = address(this).balance;
require(balance > 0, "No tips available");
payable(owner).transfer(balance);
emit Withdrawn(owner, balance);
}
}
Copy
await walletClient.sendTransaction({
to: '0xTipJarAddress',
value: parseEther('0.05'),
});
Copy
await sendTransaction({
to: contractAddress,
data: mintFunctionEncoded,
value: 0n, // user sends no SOMI
});
---
# Safes | Somnia Docs
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/safes#palmera-dao)
[Palmera DAOarrow-up-right](https://www.palmeradao.xyz/)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Palmera is a multi-safe and multi-chain treasury management platform designed to simplify on-chain financial operations. It offers a unified dashboard for managing multiple Safes across various supported chains, enhancing financial transparency and control for DAOs and organizations.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/safes#resources)
Resources
* [Documentationarrow-up-right](https://docs.palmeradao.xyz/palmera)
[PreviousWallet Providerschevron-left](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/wallet-providers)
[NextExplorerschevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/explorers)
Last updated 12 months ago
* [Palmera DAO](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/safes#palmera-dao)
* [Resources](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/safes#resources)
---
# Debug Playbook | Somnia Docs
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-1.-revert-decoding)
1\. Revert Decoding
-------------------------------------------------------------------------------------------------------------------------------------
Debugging smart contracts on the **Somnia Network** requires an understanding of how and why transactions fail. Every reverted transaction carries encoded data that can reveal the root cause of failure, whether it’s due to logic errors, insufficient gas, failed access checks, or internal Solidity panics.
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-1.1-anatomy-of-a-revert)
1.1 Anatomy of a Revert
When a transaction fails on Somnia, the EVM halts execution and returns **revert data**, an ABI-encoded payload that follows one of these three formats:
Type
Selector
Description
Example
`Error(string)`
`0x08c379a0`
Standard revert reason with message
`require(balance > 0, "Zero balance");`
`Panic(uint256)`
`0x4e487b71`
Internal error (e.g., overflow, div/0, invalid enum)
`assert(x > 0);`
Custom Errors
Function selector of custom error
`error Unauthorized(address caller);`
On Somnia, these behave identically to Ethereum but may differ in **gas costs** and **stack trace length** depending on the validator node configuration.
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-1.2-catching-and-displaying-reverts-in-hardhat)
1.2 Catching and Displaying Reverts in Hardhat
When running tests or scripts on Somnia, wrap calls in `try/catch` to capture the revert reason.
example.ts
Copy
try {
await treasury.withdraw(1000);
} catch (error: any) {
console.log('Revert reason:', error.reason || error.message);
console.log('Full error data:', error.data || error.error?.data);
}
In **Chai matchers**:
If your contract uses **custom errors**, Hardhat will not automatically print the name. Decode it manually:
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-1.3-decoding-panic-codes)
1.3 Decoding Panic Codes
Internal Solidity panics correspond to low-level EVM exceptions. Somnia propagates these codes like any other EVM chain.
Panic Code
Description
Typical Cause
`0x01`
Assertion failed
Logic invariant broken
`0x11`
Arithmetic overflow/underflow
Unchecked math operation
`0x12`
Division by zero
Incorrect math division
`0x21`
Invalid enum conversion
Out-of-bounds value
`0x31`
Storage array index out of bounds
Bad loop or mapping access
`0x32`
Memory array index out of bounds
Corrupt array operation
To detect Panic errors dynamically:
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-1.4-advanced-revert-inspection-with-hardhat-traces)
1.4 Advanced Revert Inspection with Hardhat Traces
Hardhat’s tracing layer can reveal the full execution path of a revert.
You’ll see nested calls, gas usage per function, and exactly where the failure occurred. This is invaluable for multi-contract interactions like on-chain governance or liquidity management.
Example output:
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-1.5-custom-error-decoding-for-verified-contracts)
1.5 Custom Error Decoding for Verified Contracts
If a Somnia contract is verified on the explorer, you can fetch its ABI dynamically to decode errors programmatically:
Then use `iface.parseError(error.data)` to decode reverts directly from on-chain logs or transactions.
* * *
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-2.-common-error-patterns-on-somnia)
2\. Common Error Patterns on Somnia
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
Even experienced developers encounter recurring issues. Below are the **most common EVM-level errors** observed when deploying or testing on Somnia Testnet (Shannon) and Mainnet.
Error Type
Cause
Fix
`execution reverted`
Fallback revert with no message
Add explicit revert messages or decode ABI data
`out of gas`
Gas exhausted mid-call
Use `estimateGas()` or increase gas limit
`invalid opcode`
Calling a non-existent function
Validate ABI and deployed bytecode
`nonce too low`
Pending transaction not mined yet
Wait for confirmation or reset nonce
`replacement underpriced`
Gas bump too small
Raise gas price by 10–20%
`static call violation`
State-changing call via `eth_call`
Use `.sendTransaction()` instead
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-2.1-example-catching-a-custom-error-in-somnia-treasury-contract)
2.1 Example: Catching a Custom Error in Somnia Treasury Contract
Decoding in JS:
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-2.2-handling-complex-contract-interactions)
2.2 Handling Complex Contract Interactions
When interacting with multi-layered DeFi protocols or bridging modules on Somnia, reverts can originate **several calls deep**. Use Hardhat’s trace or Foundry’s `-vvvv` verbosity to see the full stack.
Foundry example:
This reveals each opcode execution, event emission, and revert reason.
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-2.3-invalid-abi-or-proxy-conflicts)
2.3 Invalid ABI or Proxy Conflicts
Many Somnia projects use **upgradeable proxies**. Reverts from a proxy may originate in the implementation contract. If you get a generic `execution reverted`, verify you’re using the correct implementation ABI:
* * *
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.-transaction-simulation)
3\. Transaction Simulation
---------------------------------------------------------------------------------------------------------------------------------------------------
Simulating transactions allows developers to predict revert causes, estimate gas usage, and test behaviors **without risking real SOMI or STT**.
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.1-fork-somnia-networks-locally)
3.1 Fork Somnia Networks Locally
Create a local fork of Somnia Mainnet or Shannon Testnet:
Or in configuration:
This mirrors on-chain state locally, so you can safely replay any transaction.
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.2-using-callstatic-for-dry-run-simulation)
3.2 Using callStatic for Dry-Run Simulation
`callStatic` runs a transaction without broadcasting or altering state.
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.3-using-eth_call-manually)
3.3 Using eth\_call Manually
For raw RPC simulation:
If the call reverts, inspect `error.data` to decode it with the ABI.
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.4-impersonating-accounts-for-privileged-actions)
3.4 Impersonating Accounts for Privileged Actions
Simulate admin or contract-controlled operations:
Stop impersonation when finished:
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.5-snapshot-and-rollback-control)
3.5 Snapshot and Rollback Control
Snapshots let you test different outcomes quickly:
This resets the blockchain to its previous state instantly.
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.6-simulating-on-chain-transactions)
3.6 Simulating On-Chain Transactions
If you have a failed transaction hash from Somnia Mainnet:
This reproduces the failure locally and lets you inspect the revert reason directly in Hardhat.
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.7-advanced-fork-testing-with-foundry)
3.7 Advanced Fork Testing with Foundry
You can use cheatcodes like:
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.8-gas-profiling-and-cost-analysis)
3.8 Gas Profiling and Cost Analysis
Somnia gas costs can differ from Ethereum due to consensus differences. Always estimate gas usage per function:
Compare against Shannon and Mainnet to identify anomalies.
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.9-full-transaction-lifecycle-test)
3.9 Full Transaction Lifecycle Test
1
####
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#fork-somnia-testnet)
Fork Somnia Testnet
Create a fork of the testnet to reproduce on-chain state locally.
2
####
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#impersonate-the-deployer-account)
Impersonate the deployer account
Use impersonation to perform privileged actions and reproduce behavior.
3
####
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#run-callstatic-to-simulate-critical-functions)
Run callStatic to simulate critical functions
Dry-run core functions to inspect return values and revert reasons.
4
####
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#capture-reverts-and-decode-with-abi)
Capture reverts and decode with ABI
Decode revert data, custom errors, and panic codes to get actionable context.
5
####
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#use-snapshot-revert-to-iterate-quickly)
Use snapshot/revert to iterate quickly
Take snapshots to test multiple scenarios and revert between them.
6
####
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#once-clean-deploy-and-verify-on-testnet)
Once clean, deploy and verify on testnet
After local verification, deploy to the testnet and verify behavior.
7
####
[hashtag](https://docs.somnia.network/developer/development-frameworks/debug-playbook#run-same-steps-on-mainnet-fork-before-release)
Run same steps on mainnet fork before release
Final validation on a mainnet fork ensures production parity.
* * *
circle-info
Summary
* Always **decode revert data** rather than relying on generic error strings.
* Decode **custom errors** to get structured failure context.
* Use **forked local environments** for safe and realistic debugging.
* Combine **callStatic**, **trace**, and **snapshot/revert** for fast iteration.
* Validate gas behavior across testnet and mainnet for accurate production cost.
Debugging on Somnia means understanding the EVM intimately. Every revert, panic, and trace is a clue—decode them, simulate safely, and ship with confidence.
[PreviousVerifying via Explorerchevron-left](https://docs.somnia.network/developer/development-frameworks/verifying-via-explorer)
[NextBuilding DAppschevron-right](https://docs.somnia.network/developer/building-dapps)
Last updated 4 months ago
* [1\. Revert Decoding](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-1.-revert-decoding)
* [1.1 Anatomy of a Revert](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-1.1-anatomy-of-a-revert)
* [1.2 Catching and Displaying Reverts in Hardhat](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-1.2-catching-and-displaying-reverts-in-hardhat)
* [1.3 Decoding Panic Codes](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-1.3-decoding-panic-codes)
* [1.4 Advanced Revert Inspection with Hardhat Traces](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-1.4-advanced-revert-inspection-with-hardhat-traces)
* [1.5 Custom Error Decoding for Verified Contracts](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-1.5-custom-error-decoding-for-verified-contracts)
* [2\. Common Error Patterns on Somnia](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-2.-common-error-patterns-on-somnia)
* [2.1 Example: Catching a Custom Error in Somnia Treasury Contract](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-2.1-example-catching-a-custom-error-in-somnia-treasury-contract)
* [2.2 Handling Complex Contract Interactions](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-2.2-handling-complex-contract-interactions)
* [2.3 Invalid ABI or Proxy Conflicts](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-2.3-invalid-abi-or-proxy-conflicts)
* [3\. Transaction Simulation](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.-transaction-simulation)
* [3.1 Fork Somnia Networks Locally](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.1-fork-somnia-networks-locally)
* [3.2 Using callStatic for Dry-Run Simulation](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.2-using-callstatic-for-dry-run-simulation)
* [3.3 Using eth\_call Manually](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.3-using-eth_call-manually)
* [3.4 Impersonating Accounts for Privileged Actions](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.4-impersonating-accounts-for-privileged-actions)
* [3.5 Snapshot and Rollback Control](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.5-snapshot-and-rollback-control)
* [3.6 Simulating On-Chain Transactions](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.6-simulating-on-chain-transactions)
* [3.7 Advanced Fork Testing with Foundry](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.7-advanced-fork-testing-with-foundry)
* [3.8 Gas Profiling and Cost Analysis](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.8-gas-profiling-and-cost-analysis)
* [3.9 Full Transaction Lifecycle Test](https://docs.somnia.network/developer/development-frameworks/debug-playbook#id-3.9-full-transaction-lifecycle-test)
chai-example.ts
Copy
await expect(treasury.connect(user).withdraw(1000))
.to.be.revertedWith('Insufficient funds');
decode-custom-error.ts
Copy
const iface = new ethers.utils.Interface(contractABI);
try {
await treasury.connect(attacker).withdraw(9999);
} catch (error: any) {
const data = error.data || error.error?.data;
if (data) {
const decoded = iface.parseError(data);
console.log(`Custom Error: ${decoded.name}`);
console.log('Arguments:', decoded.args);
}
}
detect-panic.ts
Copy
if (error.data?.startsWith('0x4e487b71')) {
const code = parseInt(error.data.slice(10), 16);
console.log('Panic Code:', `0x${code.toString(16)}`);
}
trace
Copy
npx hardhat test --trace
Copy
CALL treasury.withdraw
└─ CALL token.transfer -> reverted with reason: 'Insufficient balance'
fetch-abi.ts
Copy
import axios from 'axios';
const abiURL = `https://explorer.somnia.network/api?module=contract&action=getabi&address=${address}`;
const { data } = await axios.get(abiURL);
const iface = new ethers.utils.Interface(JSON.parse(data.result));
Treasury.sol
Copy
error Unauthorized(address caller);
function mint(address to, uint amount) external {
if (msg.sender != owner) revert Unauthorized(msg.sender);
_mint(to, amount);
}
catch-unauthorized.ts
Copy
try {
await treasury.connect(randomUser).mint(addr, 100);
} catch (e: any) {
const iface = new ethers.utils.Interface(['error Unauthorized(address caller)']);
const decoded = iface.parseError(e.data);
console.log('Unauthorized address:', decoded.args[0]);
}
foundry-verbosity
Copy
forge test -vvvv
get-impl.ts
Copy
const implAddr = await provider.getStorageAt(proxyAddress, '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc');
const iface = new ethers.utils.Interface(implementationABI);
hardhat-fork
Copy
npx hardhat node --fork https://api.infra.mainnet.somnia.network
hardhat-config.ts
Copy
networks: {
hardhat: {
forking: {
url: process.env.SOMNIA_RPC_TESTNET,
blockNumber: 123456, //example
}
}
}
callstatic.ts
Copy
try {
const result = await treasury.callStatic.withdraw(1000);
console.log('Call successful:', result);
} catch (error: any) {
console.log('Simulation failed with reason:', error.reason);
}
eth-call.ts
Copy
const tx = {
to: contract.address,
data: contract.interface.encodeFunctionData('stake', [amount])
};
const result = await provider.call(tx);
console.log('Returned data:', result);
impersonate.ts
Copy
await network.provider.request({
method: 'hardhat_impersonateAccount',
params: ['0xAdminAddress']
});
const admin = await ethers.getSigner('0xAdminAddress');
await treasury.connect(admin).setFee(5);
stop-impersonate.ts
Copy
await network.provider.request({
method: 'hardhat_stopImpersonatingAccount',
params: ['0xAdminAddress']
});
snapshot.ts
Copy
const snapshot = await network.provider.send('evm_snapshot', []);
await treasury.mint(100);
await network.provider.send('evm_revert', [snapshot]);
replay-tx.ts
Copy
const tx = await provider.getTransaction('0x123...');
await provider.call({ to: tx.to!, data: tx.data });
anvil-forge
Copy
anvil --fork-url https://dream-rpc.somnia.network --fork-block-number 3456789
forge test -vvvv
foundry-cheatcodes.sol
Copy
vm.startPrank(admin);
contract.withdraw(1000);
vm.stopPrank();
estimate-gas.ts
Copy
const gas = await contract.estimateGas.executeTrade(orderId);
console.log('Estimated gas:', gas.toString());
---
# Authenticating with MetaMask | Somnia Docs
Somnia empowers developers to build applications for mass adoption. Developers who deploy their Smart Contracts on Somnia, will require a User Interface to Connect to the Smart Contract. To enable users connect via the User Interface, it is necessary to set up an authentication process where only authorized users can access the functionality on the deployed Smart Contracts, for example, to carry out WRITE operations. [MetaMaskarrow-up-right](https://docs.metamask.io/)
is a wallet library that developers can use to build login functionality for applications on the Somnia Network.
circle-check
Somnia Mainnet is LIVE. To deploy on Somnia Mainnet, you will need SOMI Tokens. Please refer to the [guide](https://docs.somnia.network/get-started/getting-started-for-mainnet)
on Moving from Testnet to Mainnet.
In this guide, you will learn how to use the MetaMask Library to set up authentication for your User Interface App and connect to the Somnia Network. We will build a simple NextJS application to walk through the process.
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-metamask#start-a-nextjs-project)
Start a NextJS Project
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Run the command below to start a NextJS project:
Copy
npx create-next-app metamask-example
Select Typescript, TailWind CSS, and Page Router in the build options.
Change the directory into the project folder. Delete the code inside of the `` tags and replace them with the following:
Copy
Hello, World!
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-metamask#install-viem)
Install Viem
-------------------------------------------------------------------------------------------------------------------------------------------------------
Copy
npm i viem
[Viemarrow-up-right](https://viem.sh/)
is a TypeScript interface for Ethereum that provides low-level stateless primitives for interacting with Ethereum. Viem sets up a “`transport`” infrastructure to connect with a node in the EVM Network and the deployed Smart Contracts. We will use some ViemJS methods to connect to your Smart Contract deployed on the Somnia Network. ViemJS has a \`createPublicClient\` and a \`createWalletClient\` method. The PublicClient is used to perform READ operations, while the WalletClient is used to perform WRITE operations.
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-metamask#import-methods)
Import Methods
-----------------------------------------------------------------------------------------------------------------------------------------------------------
The next step is to set up the React State methods and the ViemJS methods that we will require:
The `http` is the transport protocol for interacting with the Node of the Somnia Blockchain via RPC. It uses the default Somnia RPC URL: [`https://dream-rpc.somnia.network`arrow-up-right](https://dream-rpc.somnia.network/)
. In the future developers can use RPC providers to avoid rate limiting.
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-metamask#import-somnia)
Import Somnia
---------------------------------------------------------------------------------------------------------------------------------------------------------
Import Somnia Testnet
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-metamask#declare-react-states)
Declare React States
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
State allows us to manage changing data in the User Interface. For this example application, we are going to manage two states:
* When we can read the User's Address
* When a User is connected (Authorization)
Add the states inside the export statement:
Now that the States are declared, we can declare a function to handle the MetaMask authentication process on Somnia Network.
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-metamask#connect-metamask-function)
Connect MetaMask Function
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Add the function below inside the export statement:
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-metamask#update-the-ui)
Update the UI
---------------------------------------------------------------------------------------------------------------------------------------------------------
MetaMask connection is set up, and the final step is to test the connection via the User Interface. Update the `
Hello, World!
` in the return statement to the following:
Open your terminal and run the following command to start the app:
Go to `localhost:3000` in your Web Browser to interact with the app and connect to Somnia Network via MetaMask. You can read more about using Viem to interact with the deployed Smart Contract methods on Somnia Network [here](https://docs.somnia.network/developer/development-frameworks/using-the-viem-library)
. Congratulations, you have successfully connected from MetaMask to Somnia Network. 🎉
[PreviousWallet Integration and Authchevron-left](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth)
[NextAuthenticating with ConnectKitchevron-right](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-connectkit)
Last updated 6 months ago
* [Start a NextJS Project](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-metamask#start-a-nextjs-project)
* [Install Viem](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-metamask#install-viem)
* [Import Methods](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-metamask#import-methods)
* [Import Somnia](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-metamask#import-somnia)
* [Declare React States](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-metamask#declare-react-states)
* [Connect MetaMask Function](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-metamask#connect-metamask-function)
* [Update the UI](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-metamask#update-the-ui)
Copy
import { useState } from "react";
import {
createPublicClient,
http,
createWalletClient,
} from "viem";
Copy
import { somniaTestnet } from "viem/chains";
Copy
const [address, setAddress] = useState("");
const [connected, setConnected] = useState(false);
Copy
const connectToMetaMask = async () => {
if (typeof window !== "undefined" && window.ethereum !== undefined) {
try {
await window.ethereum.request({ method: "eth_requestAccounts" });
const walletClient = createWalletClient({
chain: SOMNIA,
transport: custom(window.ethereum),
});
const [userAddress] = await walletClient.getAddresses();
setClient(walletClient);
setAddress(userAddress);
setConnected(true);
console.log("Connected account:", userAddress);
} catch (error) {
console.error("User denied account access:", error);
}
} else {
console.log(
"MetaMask is not installed or not running in a browser environment!"
);
}
};
Copy
{!connected ? (
) : (
Connected as: {address}
)}
Copy
npm run dev
---
# Tokos | Somnia Docs
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/tokos#tokos)
Tokos
------------------------------------------------------------------------------------------------------------------------------
Tokos is a decentralized liquidity protocol operating on the Somnia network that enables users to earn interest income by staking their digital assets or borrow funds by providing collateral. The protocol operates in a decentralized and transparent manner.
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/tokos#how-to-supply-assets-and-earn-interest)
How to Supply Assets and Earn Interest?
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

1. **Go to the** [**Tokos App**arrow-up-right](https://app.tokos.fi/)
**:** Connect your wallet to the Tokos lending platform.
2. **Select an Asset:** Choose the digital asset you wish to supply from the list of available markets.
3. **Enter Amount:** Specify the amount of the asset you want to supply to the liquidity pool.
4. **Approve & Supply:** First, approve the Tokos smart contract to spend your tokens, then submit the supply transaction.
5. **Start Earning:** Once your transaction is confirmed, you will start earning interest on your supplied assets. You will receive a corresponding amount of aTokens (e.g., aUSDC), which represent your share in the pool and accrue interest in real-time.
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/tokos#how-to-borrow-assets)
How to Borrow Assets?
-------------------------------------------------------------------------------------------------------------------------------------------------------------

1. **Supply Collateral:** Before you can borrow, you must supply assets to be used as collateral.
2. **Enable as Collateral:** In your supply dashboard, make sure the asset you want to use as collateral is enabled.
3. **Choose Asset to Borrow:** Select the asset you wish to borrow from the "Borrow" section.
4. **Specify Amount:** Enter the amount you want to borrow. The interface will show your "Health Factor," which indicates the safety of your loan. A lower Health Factor increases your risk of liquidation.
5. **Confirm Transaction:** Submit the borrow transaction. The borrowed assets will be transferred to your wallet.
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/tokos#id-2.-for-developers)
2\. For Developers
----------------------------------------------------------------------------------------------------------------------------------------------------------
Tokos provides a base layer for developers to build their own DeFi applications. By interacting with the protocol's smart contracts, you can integrate borrowing/lending functions into your own platforms. You can access the Tokos smart contracts via the [linkarrow-up-right](https://docs.tokos.fi/deployed-contracts)
.
[PreviousSomnia Exchangechevron-left](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange)
[NextEcosystem Toolschevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools)
Last updated 4 months ago
* [Tokos](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/tokos#tokos)
* [How to Supply Assets and Earn Interest?](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/tokos#how-to-supply-assets-and-earn-interest)
* [How to Borrow Assets?](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/tokos#how-to-borrow-assets)
* [2\. For Developers](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/tokos#id-2.-for-developers)
---
# Building Subgraph UIs (NextJS/Fetch) | Somnia Docs
[Subgraphsarrow-up-right](https://somnia.chain.love/)
allow developers to efficiently query Somnia blockchain data using GraphQL, making it easy to index and retrieve real-time blockchain activity. In this tutorial, you’ll learn how to:
* Fetch blockchain data from a Subgraph API
* Fix CORS errors using a NextJS API route
* Display token transfers in a real-time UI
By the end of this guide, you'll have a fully functional UI that fetches and displays token transfers from Somnia’s Subgraph API.
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-nextjs-fetch#prerequisites)
Prerequisites
--------------------------------------------------------------------------------------------------------------------------------------------------------------
* Basic knowledge of React & Next.js.
* A deployed Subgraph API on Somnia ([or use an existing onearrow-up-right](https://somnia.chain.love/graph)
).
* Account on [https://somnia.chain.lovearrow-up-right](https://somnia.chain.love/)
see [guide](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/protofire-subgraph)
.
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-nextjs-fetch#create-a-nextjs-project)
Create a NextJS Project
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Start by creating a new Next.js app.
Copy
npx create-next-app@latest somnia-subgraph-ui
cd somnia-subgraph-ui
Then, install required dependencies.
Copy
npm install thirdweb react-query graphql
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-nextjs-fetch#define-the-subgraph-api-in-environment-variables)
Define the Subgraph API in Environment Variables
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Create a .env.local file in the root folder.
circle-info
💡 Note: Restart your development server after modifying .env.local:
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-nextjs-fetch#create-a-nextjs-api-route-for-the-subgraph)
Create a NextJS API Route for the Subgraph
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Since the Somnia Subgraph API has CORS restrictions, we’ll use a NextJS API route to act as a proxy.
Inside the app directory create the folder paths `**api/proxy**` and add a file `**route.ts**` Update the `**route.ts**` file with the following code:
This code allows your frontend to make requests without triggering CORS errors.
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-nextjs-fetch#fetch-and-display-token-transfers-in-the-ui)
Fetch and Display Token Transfers in the UI
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Now that we have the API set up, let’s build a React component that:
* Sends the GraphQL query using fetch()
* Stores the fetched data using useState()
* Displays the token transfers in a simple UI
To fetch the latest 10 token transfers, we’ll use this GraphQL query:
This code fetches the last 10 transfers (first: 10) and orders them by timestamp (latest first). It retrieves wallet addresses (from, to) and the amount transferred (value) and includes the transaction hash (used to generate an explorer link).
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-nextjs-fetch#fetch-and-display-data-in-a-react-component)
Fetch and Display Data in a React Component
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Now, let’s integrate the query into our NextJS frontend. Create a folder `**components**` and add a file `**TokenTransfer.ts**`
We use `**useState()**` to store transfer data and `**useEffect()**` to fetch it when the component loads.
Once data is fetched, we render it inside the UI.
The UI shows a loading message while data is being fetched. When data is ready, it displays the latest 10 token transfers. It formats transaction values (value / 1e18) to show the correct STT amount and provides a link to view each transaction on Somnia Explorer.
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-nextjs-fetch#add-the-component-to-the-nextjs-page)
Add the Component to the NextJS Page
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Update the page.tsx file:
Restart the development server:

Now your NextJS UI dynamically fetches and displays token transfers from Somnia’s Subgraph! 🔥
[PreviousProtofire Subgraphchevron-left](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/protofire-subgraph)
[NextBuilding Subgraph UIs (Apollo Client)chevron-right](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client)
Last updated 12 months ago
* [Prerequisites](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-nextjs-fetch#prerequisites)
* [Create a NextJS Project](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-nextjs-fetch#create-a-nextjs-project)
* [Define the Subgraph API in Environment Variables](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-nextjs-fetch#define-the-subgraph-api-in-environment-variables)
* [Create a NextJS API Route for the Subgraph](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-nextjs-fetch#create-a-nextjs-api-route-for-the-subgraph)
* [Fetch and Display Token Transfers in the UI](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-nextjs-fetch#fetch-and-display-token-transfers-in-the-ui)
* [Fetch and Display Data in a React Component](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-nextjs-fetch#fetch-and-display-data-in-a-react-component)
* [Add the Component to the NextJS Page](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-nextjs-fetch#add-the-component-to-the-nextjs-page)
Copy
NEXT_PUBLIC_SUBGRAPH_URL=https://proxy.somnia.chain.love/subgraphs/name/somnia-testnet/test-mytoken
NEXT_PUBLIC_SUBGRAPH_CLIENT_ID=YOUR_CLIENT_ID
Copy
npm run dev
Copy
import { NextResponse } from "next/server";
const SUBGRAPH_URL = process.env.NEXT_PUBLIC_SUBGRAPH_URL as string;
const CLIENT_ID = process.env.NEXT_PUBLIC_SUBGRAPH_CLIENT_ID as string;
export async function POST(req: Request) {
try {
const body = await req.json();
const response = await fetch(SUBGRAPH_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Client-ID": CLIENT_ID, // ✅ Pass the Subgraph Client ID
},
body: JSON.stringify(body),
});
const data = await response.json();
return NextResponse.json(data);
} catch (error) {
console.error("Proxy Error:", error);
return NextResponse.json({ error: "Failed to fetch from Subgraph" }, { status: 500 });
}
}
Copy
{
transfers(first: 10, orderBy: blockTimestamp, orderDirection: desc) {
id
from
to
value
blockTimestamp
transactionHash
}
}
Copy
"use client";
import { useEffect, useState } from "react";
export default function TokenTransfers() {
// Store transfers in state
const [transfers, setTransfers] = useState([]);
// Track loading state
const [loading, setLoading] = useState(true);
Next, we fetch the token transfer data when the component loads.
useEffect(() => {
async function fetchTransfers() {
setLoading(true); // Show loading state
const query = `
{
transfers(first: 10, orderBy: blockTimestamp, orderDirection: desc) {
id
from
to
value
blockTimestamp
transactionHash
}
}
`;
const response = await fetch("/api/proxy", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query }),
});
const { data } = await response.json();
setTransfers(data.transfers || []); // Store results in state
setLoading(false); // Hide loading state
}
fetchTransfers();
}, []);
Copy
return (
);
}
Copy
"use client";
import TokenTransfers from "../components/TokenTransfers";
export default function Home() {
return (
Welcome to MyToken Dashboard
);
}
Copy
npm run dev
---
# Authenticating with Privy | Somnia Docs
[Privyarrow-up-right](https://docs.privy.io/)
is a secure, embeddable wallet infrastructure provider that allows developers to authenticate users, manage sessions, and provide seamless wallet experiences within dApps. Privy embedded wallets can be made interoperable across apps. Somnia has adopted the global wallets setup to foster a cross-app ecosystem where users can easily port their wallets from one app to another in the Somnia Ecosystem.
Using **global wallets**, users can seamlessly move assets between different apps and easily prove ownership of, sign messages, or send transactions with their existing wallets. Developers do not have to worry that users will generate a new wallet to sign into different applications. Kindly read more [herearrow-up-right](https://docs.privy.io/wallets/global-wallets/overview)
. This guide will integrate Privy with the Somnia Testnet, enabling users to create and connect wallets effortlessly.

[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy#prerequisites)
Prerequisites
------------------------------------------------------------------------------------------------------------------------------------------------------
* This guide is not an introduction to JavaScript Programming; you are expected to understand JavaScript.
* To complete this guide, sign up for [Privyarrow-up-right](https://dashboard.privy.io/)
and get an AppID and get the Somnia Provider AppID.
* Familiarity with React and Next.js is assumed.
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy#installation)
Installation
----------------------------------------------------------------------------------------------------------------------------------------------------
###
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy#create-the-next.js-project)
Create the Next.js Project
Open your terminal and run the following commands to set up a new Next.js project:
Install the necessary packages
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy#set-up-privyprovider)
Set Up PrivyProvider
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
Go to [https://dashboard.privy.io/arrow-up-right](https://dashboard.privy.io/)
to set up an account.
Click "`**New App**`" to create a new application that will connect to the Somnia Provider AppID.

Open the newly created app and in the left side navigation menu navigate to:
`**User Management >>>> Global Wallet >>>> Integrations**`
Click the toggle to turn ON the Somnia Provider App.

Wrap your application `**layout.ts**` file with PrivyProvider and supply your PrivateKey from Privy and the Somnia Provider App ID to the `**loginMethods**`:
Add your environment variable in .env.local:
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy#privy-hooks)
Privy Hooks
--------------------------------------------------------------------------------------------------------------------------------------------------
These hooks make it easy to authenticate users, manage wallets, and interact with the Somnia Network using Privy Global Wallet
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy#authenticate)
Authenticate
----------------------------------------------------------------------------------------------------------------------------------------------------
Use the provided hooks to authenticate users and access their wallets.
chevron-rightpage.tsx[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy#page.tsx)
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy#send-transactions)
Send Transactions
--------------------------------------------------------------------------------------------------------------------------------------------------------------
Once authenticated, use the `**useSendTransaction**` hook from `useCrossAppAccount` method to interact with Somnia Testnet:
[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy#complete-code)
Complete Code
------------------------------------------------------------------------------------------------------------------------------------------------------
chevron-rightComplete `**page.tsx**` code[hashtag](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy#complete-page.tsx-code)
By using Privy Global Wallet on the Somnia Testnet, developers can offer a seamless onboarding and wallet experience. This setup is ideal for onboarding Web2 users into Web3 with embedded wallets, abstracting away traditional wallet complexities.
[PreviousAuthenticating with ConnectKitchevron-left](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-connectkit)
[NextAuthenticating with RainbowKitchevron-right](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-rainbowkit)
Last updated 9 months ago
* [Prerequisites](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy#prerequisites)
* [Installation](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy#installation)
* [Create the Next.js Project](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy#create-the-next.js-project)
* [Set Up PrivyProvider](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy#set-up-privyprovider)
* [Privy Hooks](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy#privy-hooks)
* [Authenticate](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy#authenticate)
* [Send Transactions](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy#send-transactions)
* [Complete Code](https://docs.somnia.network/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy#complete-code)
Copy
npx create-next-app@latest somnia-privy
cd somnia-privy
Copy
npm install @privy-io/react-auth viem
Copy
'use client';
import { PrivyProvider } from '@privy-io/react-auth';
import { somniaTestnet } from 'viem/chains';
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
{children}
);
}
Copy
NEXT_PUBLIC_PRIVY_APP_ID=your-privy-app-id
Copy
import { useCrossAppAccounts, usePrivy } from '@privy-io/react-auth';
Copy
export default function Home() {
const { loginWithCrossAppAccount } = useCrossAppAccounts();
const { ready, authenticated, user, logout } = usePrivy();
const disableLogin = !ready || (ready && authenticated);
const [loginError, setLoginError] = useState(null);
const [walletAddress, setWalletAddress] = useState(null);
const providerAppId = 'cm8d9yzp2013kkr612h8ymoq8';
const startCrossAppLogin = async () => {
try {
setLoginError(null);
const result = await loginWithCrossAppAccount({
appId: providerAppId,
});
setWalletAddress(result.wallet?.address)
console.log(
'Logged in via global wallet:',
result,
);
} catch (err) {
console.warn('Cross-app login failed:', err);
setLoginError('Failed to log in with Global Wallet.');
}
};
......
{!ready ? (
);
}
---
# Oracles | Somnia Docs
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/oracles#dia)
[DIAarrow-up-right](https://www.diadata.org/)
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
DIA (Decentralized Information Asset) provides real-time on-chain oracles and trusted price feeds for decentralized applications on Somnia. This is critical for DeFi, lending, and trading protocols.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/oracles#resources)
Resources
* [Somnia Price Oraclesarrow-up-right](https://docs.diadata.org/use-nexus-product/how-to-dia-nexus-oracles/access-the-oracle/somnia-price-oracles)
* [How to Integrate DIA Price Feeds](https://docs.somnia.network/developer/building-dapps/oracles/dia-price-feeds)
* * *
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/oracles#protofire)
[Protofirearrow-up-right](http://protofire.io/)
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Protofire deploys custom, compatible oracles using the same data providers and node operators, allowing protocols to connect to their network without modifying Smart Contracts.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/oracles#resources-1)
Resources
* [Price Feeds](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds)
* [Verifiable Randomness Function](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf)
[PreviousRPCchevron-left](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/rpc)
[NextSubgraphschevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/subgraphs)
Last updated 1 month ago
* [DIA](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/oracles#dia)
* [Resources](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/oracles#resources)
* [Protofire](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/oracles#protofire)
* [Resources](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/oracles#resources-1)
---
# Ormi Subgraph | Somnia Docs
The Graph is a decentralized indexing protocol that allows developers to query blockchain data using GraphQL. Instead of parsing complex raw logs and events directly from smart contracts, developers can build subgraphs that transform onchain activity into structured, queryable datasets.
This tutorial demonstrates how to deploy a subgraph on the Somnia Testnet using [Ormiarrow-up-right](https://ormilabs.com/)
, a powerful gateway that simplifies subgraph deployment through a hosted Graph Node and IPFS infrastructure.
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/ormi-subgraph#prerequisites)
Prerequisites
-----------------------------------------------------------------------------------------------------------------------------------------
* GraphQL is installed and set up on your local machine.
* A verified smart contract address deployed on Somnia.
* An Ormi account and Private Key.
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/ormi-subgraph#install-graph-cli-globally)
Install Graph CLI globally
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/ormi-subgraph#initialize-your-subgraph)
Initialize Your Subgraph
---------------------------------------------------------------------------------------------------------------------------------------------------------------
The Graph services rely on the existence of a deployed Smart Contract with onchain activity. The subgraph will be created based on indexing the events emitted from the Smart Contract. To set up a subgraph service for an example contract called `**MyToken**` run the following command to scaffold a new subgraph project:
`**mytoken**` is the folder that contains the subgraph files. Replace `0xYourTokenAddress` with your actual deployed Smart Contract address on Somnia.
This command will generate the following files:
* `subgraph.yaml`Defines the data sources and events to index
* `schema.graphql` Structure of your data
* `src/mytoken.ts`TypeScript logic to handle events
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/ormi-subgraph#define-the-subgraph-schema)
Define the Subgraph Schema
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
For the example`MyToken` Contract, which is an ERC20 Token, Edit `schema.graphql` to index all the Transfer events emitted from the Smart Contract.
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/ormi-subgraph#build-the-subgraph)
Build the Subgraph
---------------------------------------------------------------------------------------------------------------------------------------------------
After customizing your schema and mapping logic, build the subgraph by running the command:
This will generate the necessary `artifacts` for deployment.
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/ormi-subgraph#deploy-using-ormi)
Deploy Using Ormi
-------------------------------------------------------------------------------------------------------------------------------------------------
Open the Somnia Ormi website [https://subgraph.somnia.network/arrow-up-right](https://subgraph.somnia.network/)
and create an account.

On the left navigation menu, click the "key" icon to access your `privateKey.`

Deploy your subgraph to Ormi’s hosted infrastructure with the following command:
Replace yourPrivateKey with your Somnia Ormi account private key.
Once deployed, Ormi will return a GraphQL endpoint where you can begin querying your subgraph.
Return to the dashboard to find your list of deployed subgraphs.

Open the deployed subgraph in the explorer to interact with it:

[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/ormi-subgraph#conclusion)
Conclusion
-----------------------------------------------------------------------------------------------------------------------------------
You have successfully deployed a subgraph to index events emitted from your Smart Contract. To challenge yourself even further, you can extend your build:
* Expand your schema and mapping logic to cover more events.
* Connect your subgraph to a frontend UI or analytics dashboard.
For more information, visit the Ormi [docsarrow-up-right](https://docs.ormilabs.com/dedicated-env/somnia/subgraphs/overview)
.
[PreviousData Indexing and Queryingchevron-left](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying)
[NextProtofire Subgraphchevron-right](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/protofire-subgraph)
Last updated 5 months ago
* [Prerequisites](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/ormi-subgraph#prerequisites)
* [Install Graph CLI globally](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/ormi-subgraph#install-graph-cli-globally)
* [Initialize Your Subgraph](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/ormi-subgraph#initialize-your-subgraph)
* [Define the Subgraph Schema](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/ormi-subgraph#define-the-subgraph-schema)
* [Build the Subgraph](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/ormi-subgraph#build-the-subgraph)
* [Deploy Using Ormi](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/ormi-subgraph#deploy-using-ormi)
* [Conclusion](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/ormi-subgraph#conclusion)
Copy
npm install -g @graphprotocol/graph-cli
Copy
graph init --contract-name MyToken --from-contract 0xYourTokenAddress --network somnia-testnet mytoken
Copy
type Transfer @entity(immutable: true) {
id: Bytes!
from: Bytes!
to: Bytes!
value: BigInt!
blockNumber: BigInt!
blockTimestamp: BigInt!
transactionHash: Bytes!
}
Copy
graph codegen && graph build
Copy
graph deploy mytoken --node https://api.subgraph.somnia.network/deploy --ipfs https://api.subgraph.somnia.network/ipfs --deploy-key yourORMIPrivateKey
---
# Building Subgraph UIs (Apollo Client) | Somnia Docs
This guide will teach you how to create a minimal, functional UI that queries blockchain data from a Somnia subgraph using Next.js, Apollo Client, and GraphQL.
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#prerequisites)
Prerequisites
---------------------------------------------------------------------------------------------------------------------------------------------------------------
* Basic knowledge of React and Next.js
* Node.js 20+
* A deployed subgraph on Somnia (we'll use [SomFliparrow-up-right](https://shannon-explorer.somnia.network/address/0x014F851965F281d6112FC7F6dfe8c331C413Eb9b)
as an example)
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#what-youll-build)
What You'll Build
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
A clean, minimal interface that:
* Displays all coin flip results with pagination
* Shows a live feed that auto-refreshes every 5 seconds
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#create-a-next.js-project)
Create a Next.js Project
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Start by creating a new Next.js application with TypeScript and TailwindCSS:
Copy
npx create-next-app@latest somnia-subgraph-ui --typescript --tailwind --app
cd somnia-subgraph-ui
Install the required GraphQL dependencies:
Copy
npm install @apollo/client graphql
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#understand-the-architecture)
Understand the Architecture
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Before we code, let's understand how the pieces fit together:
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#set-up-apollo-client)
Set Up Apollo Client
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Apollo Client is a comprehensive GraphQL client that manages data fetching, caching, and state management. Create a `lib` directory and create a file `apollo-client.ts`
The `URI` is the endpoint where your subgraph is hosted, and `InMemoryCache` will store the query results in memory for fast access
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#create-the-apollo-provider-wrapper)
Create the Apollo Provider Wrapper
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
React components need access to the Apollo Client. We'll create a wrapper component that provides this access to the entire app. Create a `components` directory and create a file `ApolloWrapper.tsx`.
ApolloProvider: Makes the Apollo Client available to all child components
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#update-app-layout.tsx)
Update app/layout.tsx
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#create-graphql-queries)
Create GraphQL Queries
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
GraphQL queries define exactly what data you want from the subgraph. Let's create queries for our two main features. In the `lib` directory create a `queries.ts` file.
chevron-rightqueries.ts[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#queries.ts)
Note the following:
* `gql` is the template literal tag that parses GraphQL queries
* Variables start with $ and have types (Int!, String!, etc.)
* ! means the field is required (non-nullable)
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#build-the-all-flips-component)
Build the All Flips Component
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Let's build the All Flips component step by step, understanding each part in detail.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#set-up-the-component-file)
Set Up the Component File
In the `components` directory create `AllFlips.tsx` file and add the following imports.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#create-utility-functions)
Create Utility Functions
Add these helper functions at the top of your component:
###
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#component-function-and-state-management)
Component Function and State Management
`the number ofpage` tracks the current page number (starting at 0), which is used to calculate the number of results to skip. It updates when the user clicks the Previous/Next button.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#execute-the-graphql-query)
Execute the GraphQL Query
The `useQuery` function is set to `loading: true` while fetching `data` and the `data` contains the query results when successful.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#handle-query-states)
Handle Query States
This prevents rendering errors and andles edge cases gracefully
###
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#render-the-table-view)
Render the Table View
chevron-rightAllFlips.tsx - Table View[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#allflips.tsx-table-view)
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#build-the-live-feed-component)
Build the Live Feed Component
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Now let's build the Live Feed component that automatically refreshes to show new flips.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#set-up-the-component)
Set Up the Component
In the `components` directory create a `LiveFeed.tsx` file and update the imports.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#create-utility-functions-1)
Create Utility Functions
###
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#component-function-with-auto-refresh)
Component Function with Auto-Refresh
The `pollInterval` automatically re-executes the query every 5 seconds. New flips appear without user interaction with Apollo Client handling the refresh logic. You can set to 0 or remove to disable auto-refresh
###
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#handle-query-states-1)
Handle Query States
###
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#complete-live-feed-component)
Complete Live Feed Component
chevron-rightLiveFeed.tsx[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#livefeed.tsx)
The key differences from the AllFlips page are that there is no pagination (shows most recent only), and it auto-refreshes with pollInterval, with a visual emphasis on win/loss status.
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#update-the-main-page.tsx)
Update the Main Page.tsx
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
chevron-rightpage.tsx[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#page.tsx)
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#run-your-application)
Run Your Application
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Visit `http://localhost:3000` to see your UI in action.
[PreviousBuilding Subgraph UIs (NextJS/Fetch)chevron-left](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-nextjs-fetch)
[NextUsing Data APIs (Ormi)chevron-right](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi)
Last updated 1 month ago
* [Prerequisites](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#prerequisites)
* [What You'll Build](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#what-youll-build)
* [Create a Next.js Project](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#create-a-next.js-project)
* [Understand the Architecture](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#understand-the-architecture)
* [Set Up Apollo Client](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#set-up-apollo-client)
* [Create the Apollo Provider Wrapper](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#create-the-apollo-provider-wrapper)
* [Update app/layout.tsx](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#update-app-layout.tsx)
* [Create GraphQL Queries](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#create-graphql-queries)
* [Build the All Flips Component](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#build-the-all-flips-component)
* [Set Up the Component File](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#set-up-the-component-file)
* [Create Utility Functions](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#create-utility-functions)
* [Component Function and State Management](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#component-function-and-state-management)
* [Execute the GraphQL Query](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#execute-the-graphql-query)
* [Handle Query States](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#handle-query-states)
* [Render the Table View](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#render-the-table-view)
* [Build the Live Feed Component](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#build-the-live-feed-component)
* [Set Up the Component](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#set-up-the-component)
* [Create Utility Functions](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#create-utility-functions-1)
* [Component Function with Auto-Refresh](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#component-function-with-auto-refresh)
* [Handle Query States](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#handle-query-states-1)
* [Complete Live Feed Component](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#complete-live-feed-component)
* [Update the Main Page.tsx](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#update-the-main-page.tsx)
* [Run Your Application](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client#run-your-application)
Copy
User Interface (React Components)
↓
Apollo Client (GraphQL Client)
↓
GraphQL Queries
↓
Somnia Subgraph API
↓
Blockchain Data
Copy
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
// The URI of your subgraph endpoint
uri: 'https://proxy.somnia.chain.love/subgraphs/name/somnia-testnet/SomFlip',
// Apollo's caching layer - stores query results
cache: new InMemoryCache(),
});
export default client;
Copy
'use client'; // Next.js 13+ directive for client-side components
import { ApolloProvider } from '@apollo/client';
import client from '@/lib/apollo-client';
// This component wraps your app with Apollo's context provider
export default function ApolloWrapper({
children
}: {
children: React.ReactNode
}) {
return (
{children}
);
}
Copy
import ApolloWrapper from '@/components/ApolloWrapper';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
{children}
);
}
Copy
import { gql } from '@apollo/client';
// Query for paginated flip results
export const GET_FLIP_RESULTS = gql`
query GetFlipResults(
$first: Int!, # Number of results to fetch
$skip: Int!, # Number of results to skip (for pagination)
$orderBy: String!, # Field to sort by
$orderDirection: String! # 'asc' or 'desc'
) {
flipResults(
first: $first
skip: $skip
orderBy: $orderBy
orderDirection: $orderDirection
) {
id # Unique identifier
player # Wallet address of player
betAmount # Amount bet (in wei)
choice # Player's choice: HEADS or TAILS
result # Actual result: HEADS or TAILS
payout # Amount won (0 if lost)
blockNumber # Block when flip occurred
blockTimestamp # Unix timestamp
transactionHash # Transaction hash on blockchain
}
}
`;
// Query for recent flips (live feed)
export const GET_RECENT_FLIPS = gql`
query GetRecentFlips($first: Int!) {
flipResults(
first: $first
orderBy: blockTimestamp
orderDirection: desc # Most recent first
) {
id
player
betAmount
choice
result
payout
blockTimestamp
transactionHash
}
}
`;
Copy
'use client';
import { useState } from 'react';
import { useQuery } from '@apollo/client';
import { GET_FLIP_RESULTS } from '@/lib/queries';
Copy
/ Shortens long blockchain addresses for display
// Example: "0x1234567890abcdef" becomes "0x1234...cdef"
const truncateHash = (hash: string) => {
return `${hash.slice(0, 6)}...${hash.slice(-4)}`;
};
// Converts wei (smallest unit) to ether (display unit)
// 1 ether = 1,000,000,000,000,000,000 wei (10^18)
const formatEther = (wei: string) => {
const ether = parseFloat(wei) / 1e18;
return ether.toFixed(4); // Show 4 decimal places
};
// Converts Unix timestamp to readable date
// Blockchain stores time as seconds since Jan 1, 1970
const formatTime = (timestamp: string) => {
const milliseconds = parseInt(timestamp) * 1000;
const date = new Date(milliseconds);
return date.toLocaleString();
};
Copy
export default function AllFlips() {
// Track which page of results we're viewing
const [page, setPage] = useState(0);
const itemsPerPage = 30;
Copy
const { loading, error, data } = useQuery(GET_FLIP_RESULTS, {
variables: {
first: itemsPerPage, // How many results to fetch
skip: page * itemsPerPage, // How many to skip
orderBy: 'blockTimestamp', // Sort by time
orderDirection: 'desc', // Newest first
},
});
Copy
// Show loading spinner while fetching
if (loading) {
return
Loading...
;
}
// Show error message if query failed
if (error) {
return (
Error: {error.message}
);
}
// Check if we have results
if (!data?.flipResults?.length) {
return
No flips found
;
}
Copy
return (
{/* Makes table scrollable on mobile */}
Player
Bet
Choice
Result
Payout
Time
{data.flipResults.map((flip: any) => (
{/* Player address - truncated for readability */}
);
}
Copy
'use client';
import { useState } from 'react';
import AllFlips from '@/components/AllFlips';
import LiveFeed from '@/components/LiveFeed';
export default function Home() {
const [activeTab, setActiveTab] = useState('allFlips');
return (
SomFlip
{/* Tab Navigation */}
{/* Conditional Rendering Based on Active Tab */}
{activeTab === 'allFlips' ? : }
);
}
Copy
npm run dev
---
# Using Verifiable Randomness (VRF) | Somnia Docs
Protofire Chainlink’s Verifiable Random Function (VRF) allows developers to securely request random numbers in a tamper-proof and auditable way. It is ideal for gaming, NFT mints, and lotteries. This tutorial walks you through integrating Protofire's Chainlink VRF v2.5 on Somnia Network, using native STT (Somnia Token) as the payment currency.
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#mainnet-vrf-smart-contracts)
Mainnet VRF Smart Contracts
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
Contract
Address
VRFV2PlusWrapper
[0x606b2B36516AB7479D1445Ec14B6B39B44901bf8arrow-up-right](https://explorer.somnia.network/address/0x606b2B36516AB7479D1445Ec14B6B39B44901bf8)
LINK Token
[0x0a4Db7035284566F6f676991ED418140dC01A2aaarrow-up-right](https://explorer.somnia.network/address/0x0a4Db7035284566F6f676991ED418140dC01A2aa)
LINK/NATIVE oracle
[0xEBD41881413dD76F42DF2902ee865099af9099B4arrow-up-right](https://explorer.somnia.network/address/0xEBD41881413dD76F42DF2902ee865099af9099B4)
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#testnet-vrf-smart-contracts)
Testnet VRF Smart Contracts
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
Contract
Address
VRFV2PlusWrapper
[0x763cC914d5CA79B04dC4787aC14CcAd780a16BD2arrow-up-right](https://shannon-explorer.somnia.network/address/0x763cC914d5CA79B04dC4787aC14CcAd780a16BD2)
LINK Token
[0x30C75a2badF9b12733e831fcb5315C8f54e96f6darrow-up-right](https://shannon-explorer.somnia.network/address/0x30C75a2badF9b12733e831fcb5315C8f54e96f6d)
LINK/NATIVE oracle
[0xEc00df0e834AB878135b6554bb7438A2Ff66563barrow-up-right](https://shannon-explorer.somnia.network/address/0xEc00df0e834AB878135b6554bb7438A2Ff66563b)
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#understanding-vrf-and-why-it-matters)
Understanding VRF and Why It Matters
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Randomness is essential for many blockchain applications, such as Games, Lotteries, Raffles, and NFT drops, but blockchains are deterministic by nature. This means every node must produce the same output given the same inputs. If you try to use on-chain data like `block.timestamp` or `blockhash` as a random source, miners/validators can manipulate these values to influence the outcome. This is where VRF comes in.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#what-is-vrf)
What is VRF?
A Verifiable Random Function (VRF) is a cryptographic method of generating random numbers along with a proof that the result was not tampered with. When using Protofire Chainlink VRF:
1. You request a random number from the VRF service.
2. Protofire Chainlink’s decentralized oracle network generates a random value off-chain along with a cryptographic proof.
3. The proof is verified on-chain before the value is returned to your contract.
This ensures tamper-proof randomness and publicly verifiable results. Where the outcomes are fair.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#why-is-vrf-important-on-blockchain)
Why is VRF important on blockchain?
Without VRF, randomness in blockchain apps can be gamed. With VRF:
* No single party can manipulate the results
* Users can independently verify the randomness
* Applications gain trust from players, participants, and investors
Requesting VRF Data using Protofire Chainlink services relies on two methods: Subscription and Direct Funding
In the Subscription method, Chainlink VRF requests receive funding from subscription accounts. The [Subscription Managerarrow-up-right](https://vrf.chain.link/)
lets you create an account and pre-pay for your use of Chainlink VRF requests. You can learn more about the subscription method by referencing the Chainlink [documentationarrow-up-right](https://docs.chain.link/vrf/v2-5/overview/subscription)
. The Direct Funding method doesn't require a subscription and is optimal for one-off requests for randomness. This method also works best for applications where your end-users must pay the fees for VRF because the cost of the request is determined at request time. [Learn morearrow-up-right](https://docs.chain.link/vrf/v2-5/overview/direct-funding)
.
In this guide, we will build a Smart Contract called `RandomNumberConsumer` that:
* Inherits the Protofire Chainlink VRF Wrapper.
* Requests 3 secure random numbers
* Pays for randomness using native STT (no LINK subscription required)
* Emits events and exposes functions to retrieve the randomness
* Handles overpayments and pending request checks
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#prerequisites)
Prerequisites
----------------------------------------------------------------------------------------------------------------------------------------
Before getting started:
* You are familiar with Solidity (v0.8+)
* You have the VRF Wrapper address for [Protofire ChainLink VRF Wrapperarrow-up-right](https://shannon-explorer.somnia.network/address/0x763cC914d5CA79B04dC4787aC14CcAd780a16BD2)
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#tl-dr)
TL;DR
------------------------------------------------------------------------------------------------------------------------
1. Owner calls `requestRandomNumber()` and sends enough STT `(msg.value)` to cover the fee.
2. Contract uses **VRF Wrapper** to request 3 random words (in native STT).
3. When VRF is ready, the wrapper calls `fulfillRandomWords`, the contract:
1. verifies the request,
2. stores the 3 words,
3. toggles `fulfilled = true`,
4. emits RandomNumberFulfilled.
4. User Interfaces and Scripts can read `getLatestRandomWord()` or poll `getRequestStatus()`.
chevron-rightEXAMPLE - RandomNumberConsumer.sol[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#example-randomnumberconsumer.sol)
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#code-breakdown)
Code Breakdown
------------------------------------------------------------------------------------------------------------------------------------------
`VRFV2PlusWrapperConsumerBase` gives you the glue code for requesting randomness from the VRF Wrapper and receiving the callback `(fulfillRandomWords)`. It also exposes the wrapper instance `i_vrfV2PlusWrapper`.
`ConfirmedOwner` is a lightweight ownership module; it lets you restrict actions to the contract owner via onlyOwner.
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#state-variables)
State Variables
--------------------------------------------------------------------------------------------------------------------------------------------
`latestRequestId` tracks the most recent VRF request ID. Used to make sure the fulfillment we receive matches the last request. `latestRandomWord` stores the three random words returned by VRF for the latest request. `fulfilled` marks whether the latest request has finished (prevents overlapping requests and makes UI/state checks easy).
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#vrf-request-parameters-constants)
VRF Request Parameters (constants)
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
`CALLBACK_GAS_LIMIT` is the max gas VRF can use when calling your `fulfillRandomWords`. Must be large enough for your logic. This example uses headroom for 3 words. `REQUEST_CONFIRMATIONS` is how many blocks to wait before fulfillment (trade-off between speed and reorg safety). `NUM_WORDS` This is how many random numbers you want per request. Here, it’s 3.
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#events)
Events
--------------------------------------------------------------------------------------------------------------------------
`RandomNumberRequested` is emitted right after submitting a VRF request. Includes the paid cost in native STT. `RandomNumberFulfilled` is emitted when VRF returns the result, with the three random words.
Events make it easy to monitor behavior from explorers, indexers, or frontends.
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#custom-errors)
Custom errors
----------------------------------------------------------------------------------------------------------------------------------------
`InsufficientPayment` is thrown if msg.value doesn’t cover the VRF native fee at request time.
`RequestAlreadyPending` is thrown if you try to request again while the previous request hasn’t been fulfilled.
Errors are cheaper than require("string") and clearer to reason about.
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#constructor)
Constructor
------------------------------------------------------------------------------------------------------------------------------------
Takes the VRF V2+ Wrapper address (the on-chain contract that mediates VRF requests) and initializes ownership to the deployer.
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#function-requesting-randomness)
Function Requesting Randomness
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
The `requestRandomNumber()` function implements safeguards and processes to ensure reliable VRF operation. First, it enforces safety by preventing spam or overlapping requests, which ensures a predictable user experience and maintains simpler state management. The function then calculates the exact payment required by calling `getRequestPrice()` to determine how much STT the wrapper currently needs, rejecting any transaction with insufficient payment. To specify the payment method, it encodes `nativePayment: true` in the request parameters, instructing the wrapper to charge in native STT tokens rather than LINK.
Once validated, the function submits the request through `requestRandomnessPayInNative()`, which initiates the VRF request to Chainlink while storing the returned requestId and marking the fulfilled status as false to track the pending request.
Finally, the function implements automatic refund logic that returns any excess funds to the user if they overpaid, ensuring users never lose funds due to price variations.
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#read-operations)
READ OPERATIONS
--------------------------------------------------------------------------------------------------------------------------------------------
###
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#vrf-callback-fulfillment)
VRF callback (fulfillment)
Called by the VRF Wrapper (not by you) and validates that we actually received words, and the `requestId` matches the latest request (guards against stale/foreign callbacks). It then stores the 3 words and flips `fulfilled = true` and emits a completion event. If you need game logic, derive from these random words inside this function or store and consume later.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#getrequeststatus)
getRequestStatus()
For frontends/monitoring: see the last `request ID`, whether it’s still `pending`, and whether it was `fulfilled`.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#getlatestrandomword)
getLatestRandomWord()
Returns the three words from the most recent fulfilled request, which is actually a string on numbers for example: `93869141573160465677701763703933181905260360385351294458479680637737009096153`
###
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#pricing-helper)
Pricing Helper
Asks the wrapper how much STT (in wei) you need right now for a request with your chosen `CALLBACK_GAS_LIMIT` and `NUM_WORDS`. Use this in your UI or scripts to fill `msg.value`.
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#conclusion)
Conclusion
----------------------------------------------------------------------------------------------------------------------------------
You've successfully built a secure random number generator on Somnia using Chainlink VRF v2.5. Your `RandomNumberConsumer` Smart Contract provides tamper proof randomness with native STT payment, automatic refunds, and proper request management, everything needed for production use.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#real-world-vrf-use-cases)
Real-World VRF Use Cases
VRF powers a wide range of blockchain applications where fairness is critical. In gaming, it enables trustworthy dice rolls, loot drops, critical hit calculations, and procedurally generated maps. For NFT collections, VRF ensures unbiased trait assignment during minting, metadata reveals, and rarity distribution. This is crucial when traits can be worth thousands.
Lotteries and raffles benefit from transparent winner selection, whether for small community giveaways or million dollar prize pools. DeFi protocols use VRF for random liquidator selection, fair distribution, and variable reward mechanisms, while DAO governance applications include jury selection for disputes, randomized proposal ordering, and representative sampling for surveys.
With VRF integrated, you're ready to build applications where fairness is cryptographically guaranteed, not just promised. Whether for games, NFTs, or DeFi protocols, your users can verify that randomness is truly random and build trust through mathematics, not faith.
[PreviousProtofire Price Feedschevron-left](https://docs.somnia.network/developer/building-dapps/oracles/protofire-price-feeds)
[NextExample Applicationschevron-right](https://docs.somnia.network/developer/building-dapps/example-applications)
Last updated 1 month ago
* [Mainnet VRF Smart Contracts](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#mainnet-vrf-smart-contracts)
* [Testnet VRF Smart Contracts](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#testnet-vrf-smart-contracts)
* [Understanding VRF and Why It Matters](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#understanding-vrf-and-why-it-matters)
* [What is VRF?](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#what-is-vrf)
* [Why is VRF important on blockchain?](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#why-is-vrf-important-on-blockchain)
* [Prerequisites](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#prerequisites)
* [TL;DR](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#tl-dr)
* [Code Breakdown](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#code-breakdown)
* [State Variables](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#state-variables)
* [VRF Request Parameters (constants)](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#vrf-request-parameters-constants)
* [Events](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#events)
* [Custom errors](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#custom-errors)
* [Constructor](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#constructor)
* [Function Requesting Randomness](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#function-requesting-randomness)
* [READ OPERATIONS](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#read-operations)
* [VRF callback (fulfillment)](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#vrf-callback-fulfillment)
* [getRequestStatus()](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#getrequeststatus)
* [getLatestRandomWord()](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#getlatestrandomword)
* [Pricing Helper](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#pricing-helper)
* [Conclusion](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#conclusion)
* [Real-World VRF Use Cases](https://docs.somnia.network/developer/building-dapps/oracles/using-verifiable-randomness-vrf#real-world-vrf-use-cases)
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {VRFConsumerBaseV2Plus} from "@chainlink/[email protected]/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol";
import {VRFV2PlusClient} from "@chainlink/[email protected]/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol";
import {VRFV2PlusWrapperConsumerBase} from "@chainlink/[email protected]/src/v0.8/vrf/dev/VRFV2PlusWrapperConsumerBase.sol";
import {ConfirmedOwner} from "@chainlink/[email protected]/src/v0.8/shared/access/ConfirmedOwner.sol";
contract RandomNumberConsumer is VRFV2PlusWrapperConsumerBase, ConfirmedOwner {
uint256 public latestRequestId;
uint256[] public latestRandomWord;
bool public fulfilled;
uint32 public constant CALLBACK_GAS_LIMIT = 2_100_000;
uint16 public constant REQUEST_CONFIRMATIONS = 3;
uint32 public constant NUM_WORDS = 3;
event RandomNumberRequested(uint256 indexed requestId, address indexed requester, uint256 paid);
event RandomNumberFulfilled(uint256 indexed requestId, uint256[] randomWord);
error InsufficientPayment(uint256 required, uint256 sent);
error RequestAlreadyPending();
constructor(address wrapper)
ConfirmedOwner(msg.sender)
VRFV2PlusWrapperConsumerBase(wrapper)
{}
function requestRandomNumber() external payable onlyOwner {
// Check if there's already a pending request
if (latestRequestId != 0 && !fulfilled) {
revert RequestAlreadyPending();
}
// Calculate the required payment
uint256 requestPrice = getRequestPrice();
if (msg.value < requestPrice) {
revert InsufficientPayment(requestPrice, msg.value);
}
// Prepare the extra arguments for native payment
VRFV2PlusClient.ExtraArgsV1 memory extraArgs = VRFV2PlusClient.ExtraArgsV1({
nativePayment: true
});
bytes memory args = VRFV2PlusClient._argsToBytes(extraArgs);
// Request randomness
(uint256 requestId, uint256 paid) = requestRandomnessPayInNative(
CALLBACK_GAS_LIMIT,
REQUEST_CONFIRMATIONS,
NUM_WORDS,
args
);
latestRequestId = requestId;
fulfilled = false;
emit RandomNumberRequested(requestId, msg.sender, paid);
// Refund excess payment
if (msg.value > paid) {
(bool success, ) = msg.sender.call{value: msg.value - paid}("");
require(success, "Refund failed");
}
}
// This will be called by the VRF Wrapper
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
require(randomWords.length > 0, "No random word returned");
require(requestId == latestRequestId, "Unexpected request ID");
latestRandomWord = randomWords;
fulfilled = true;
emit RandomNumberFulfilled(requestId, randomWords);
}
function getRequestStatus() external view returns (
uint256 requestId,
bool isPending,
bool isFulfilled
) {
return (
latestRequestId,
latestRequestId != 0 && !fulfilled,
fulfilled
);
}
function getLatestRandomWord() external view returns (uint256[] memory) {
require(fulfilled, "No fulfilled request yet");
return latestRandomWord;
}
/**
* @notice Get the current price for a VRF request in native tokens
* @return The price in wei for requesting random numbers
*/
function getRequestPrice() public view returns (uint256) {
return i_vrfV2PlusWrapper.calculateRequestPriceNative(CALLBACK_GAS_LIMIT, NUM_WORDS);
}
/**
* @notice Withdraw any excess native tokens from the contract
* @dev Only callable by owner, useful for recovering overpayments
*/
function withdraw() external onlyOwner {
uint256 balance = address(this).balance;
require(balance > 0, "No balance to withdraw");
(bool success, ) = owner().call{value: balance}("");
require(success, "Withdrawal failed");
}
// Allow contract to receive STT for native payment
receive() external payable {}
}
Copy
contract RandomNumberConsumer
is VRFV2PlusWrapperConsumerBase, ConfirmedOwner
Copy
uint256 public latestRequestId;
uint256[] public latestRandomWord;
bool public fulfilled;
Copy
uint32 public constant CALLBACK_GAS_LIMIT = 2_100_000;
uint16 public constant REQUEST_CONFIRMATIONS = 3;
uint32 public constant NUM_WORDS = 3;
Copy
event RandomNumberRequested(uint256 indexed requestId, address indexed requester, uint256 paid);
event RandomNumberFulfilled(uint256 indexed requestId, uint256[] randomWord);
Copy
error InsufficientPayment(uint256 required, uint256 sent);
error RequestAlreadyPending();
Copy
constructor(address wrapper) ConfirmedOwner(msg.sender) VRFV2PlusWrapperConsumerBase(wrapper) {}
Copy
function requestRandomNumber() external payable onlyOwner {
// 1) block overlapping requests
if (latestRequestId != 0 && !fulfilled) revert RequestAlreadyPending();
// 2) compute required fee and validate payment
uint256 requestPrice = getRequestPrice();
if (msg.value < requestPrice) revert InsufficientPayment(requestPrice, msg.value);
// 3) signal native payment to the wrapper
bytes memory args = VRFV2PlusClient._argsToBytes(
VRFV2PlusClient.ExtraArgsV1({ nativePayment: true })
);
// 4) submit request (uses native STT)
(uint256 requestId, uint256 paid) = requestRandomnessPayInNative(
CALLBACK_GAS_LIMIT,
REQUEST_CONFIRMATIONS,
NUM_WORDS,
args
);
latestRequestId = requestId;
fulfilled = false;
emit RandomNumberRequested(requestId, msg.sender, paid);
// 5) refund any excess back to caller
if (msg.value > paid) {
(bool ok, ) = msg.sender.call{ value: msg.value - paid }("");
require(ok, "Refund failed");
}
}
Copy
function fulfillRandomWords(
uint256 requestId,
uint256[] memory randomWords
) internal override {
require(randomWords.length > 0, "No random word returned");
require(requestId == latestRequestId, "Unexpected request ID");
latestRandomWord = randomWords; // stores 3 words
fulfilled = true;
emit RandomNumberFulfilled(requestId, randomWords);
}
Copy
function getRequestStatus()
external
view
returns (uint256 requestId, bool isPending, bool isFulfilled)
{
return (latestRequestId, latestRequestId != 0 && !fulfilled, fulfilled);
}
Copy
function getLatestRandomWord() external view returns (uint256[] memory) {
require(fulfilled, "No fulfilled request yet");
return latestRandomWord;
}
Copy
function getRequestPrice() public view returns (uint256) {
return i_vrfV2PlusWrapper.calculateRequestPriceNative(CALLBACK_GAS_LIMIT, NUM_WORDS);
}
---
# Node/Infra Security | Somnia Docs
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#secure-rpc-key-management-and-environment-configuration-for-somnia-developers)
Secure RPC Key Management and Environment Configuration for Somnia Developers
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
This comprehensive guide teaches developers how to securely manage RPC keys, private keys, and environment variables when building applications on the Somnia blockchain. If you're deploying smart contracts, building dApps, or integrating with Somnia. Proper security practices are essential to protect your assets and maintain service reliability. By following this tutorial, you'll implement industry-standard security measures with practical code examples that seamlessly integrate into your development workflow.
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#prerequisites)
Prerequisites
--------------------------------------------------------------------------------------------------------------
Before starting this guide, ensure you have:
* Basic knowledge of blockchain development and EVM concepts
* Node.js (20+) installed
* A code editor (VS Code recommended)
* A Somnia wallet with Somnia Token (STT) for testing
* Familiarity with environment variables and package managers
* Basic understanding of Git and version control
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#rpc-key-security-fundamentals)
RPC Key Security Fundamentals
----------------------------------------------------------------------------------------------------------------------------------------------
RPC (Remote Procedure Call) keys and endpoints allow your application to interact with the blockchain. Using them securely is paramount.
A publicly accessible key, especially one with write permissions, can be exploited by an attacker to drain wallets or cause network congestion.
####
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#using-ankr-provider)
Using Ankr Provider
❌ **Bad Practice:**
✅ **Good Practice:**
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#environment-variable-management)
Environment Variable Management
--------------------------------------------------------------------------------------------------------------------------------------------------
####
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#private-rpc-endpoints)
Private RPC Endpoints
While public endpoints are convenient for basic queries, they are prone to unreliability and congestion during high-traffic events. **Private RPCs are premium services and perform significantly better than the public RPC**, offering more speed and reliability through dedicated connections.
####
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#environment-variable-best-practices)
Environment Variable Best Practices
A .env file is a standard way to manage environment-specific configuration:
####
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#never-commit-.env-files)
Never Commit .env Files
The .env file should be added to your .gitignore file.
####
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#create-separate-environment-files)
Create Separate Environment Files
Use separate configuration files for different environments.
####
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#reference-keys-in-code)
Reference Keys in Code
Always reference environment variables rather than hardcoding sensitive keys.
####
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#environment-variable-testing-for-applications)
Environment Variable Testing for Applications
**Note:** This testing approach is designed for application projects only, not system-wide configurations.
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#implementation-examples)
Implementation Examples
----------------------------------------------------------------------------------------------------------------------------------
####
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#complete-project-setup)
Complete Project Setup
####
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#secure-contract-interaction)
Secure Contract Interaction
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#rpc-key-management)
RPC Key Management
------------------------------------------------------------------------------------------------------------------------
####
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#ip-whitelisting)
IP Whitelisting
If your RPC provider supports it, restrict access to your API key by creating an allowlist of trusted IP addresses.
####
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#key-rotation-and-expiration)
Key Rotation and Expiration
Regularly rotate your RPC keys and immediately revoke any that are no longer in use.
chevron-rightManual key rotation implementation[hashtag](https://docs.somnia.network/developer/security/node-infra-security#manual-key-rotation-implementation)
####
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#secrets-management-for-production)
Secrets Management for Production
For production environments, use a dedicated secrets management platform.
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#private-key-security)
Private Key Security
----------------------------------------------------------------------------------------------------------------------------
Private keys authorize all transactions on a blockchain and should be protected with the utmost vigilance.
####
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#secure-key-generation)
Secure Key Generation
Use reputable tools that follow industry standards for cryptographically random key generation.
####
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#access-control)
Access Control
Private keys should never be shared. For team access, use multisig wallets or role-based access control.
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#error-handling-and-logging)
Error Handling and Logging
----------------------------------------------------------------------------------------------------------------------------------------
Proper error handling and logging are crucial for maintaining security and debugging issues in production environments. When implementing logging for blockchain applications, it's essential to balance transparency with security, ensuring that sensitive information like private keys and API secrets are never exposed in logs.
####
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#secure-logging-practices)
Secure Logging Practices
chevron-rightSecure logging implementation[hashtag](https://docs.somnia.network/developer/security/node-infra-security#secure-logging-implementation)
####
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#error-recovery-strategies)
Error Recovery Strategies
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#security-checklist)
Security Checklist
------------------------------------------------------------------------------------------------------------------------
* Store all sensitive keys in environment variables
* Use private RPC endpoints for better performance and reliability
* Implement IP whitelisting where supported
* Set up regular key rotation schedules
* Use hardware wallets for high-value operations
* Implement proper error handling and logging
* Never commit secrets to version control
* Use role-based access control for team environments
* Monitor and audit key usage regularly
* Test environment variable loading in application context
[hashtag](https://docs.somnia.network/developer/security/node-infra-security#conclusion)
Conclusion
--------------------------------------------------------------------------------------------------------
By following these security practices, you'll significantly reduce the risk of key compromise and ensure your Somnia blockchain applications operate securely and reliably. Security is an ongoing process, and you should regularly review and update your practices as new threats emerge and best practices evolve.
[PreviousAudit Checklistchevron-left](https://docs.somnia.network/developer/security/audit-checklist)
[NextResponsible Disclosure Policychevron-right](https://docs.somnia.network/developer/security/responsible-disclosure-policy)
Last updated 1 month ago
* [Secure RPC Key Management and Environment Configuration for Somnia Developers](https://docs.somnia.network/developer/security/node-infra-security#secure-rpc-key-management-and-environment-configuration-for-somnia-developers)
* [Prerequisites](https://docs.somnia.network/developer/security/node-infra-security#prerequisites)
* [RPC Key Security Fundamentals](https://docs.somnia.network/developer/security/node-infra-security#rpc-key-security-fundamentals)
* [Environment Variable Management](https://docs.somnia.network/developer/security/node-infra-security#environment-variable-management)
* [Implementation Examples](https://docs.somnia.network/developer/security/node-infra-security#implementation-examples)
* [RPC Key Management](https://docs.somnia.network/developer/security/node-infra-security#rpc-key-management)
* [Private Key Security](https://docs.somnia.network/developer/security/node-infra-security#private-key-security)
* [Error Handling and Logging](https://docs.somnia.network/developer/security/node-infra-security#error-handling-and-logging)
* [Security Checklist](https://docs.somnia.network/developer/security/node-infra-security#security-checklist)
* [Conclusion](https://docs.somnia.network/developer/security/node-infra-security#conclusion)
Copy
// Do not call your api Provider directly in your script with the api-keys!
// Setup provider AnkrProvider
const provider = new AnkrProvider('https://rpc.ankr.com/somnia_testnet/your-private-key');
Copy
// Create a .env file
SOMNIA_ANKR_RPC_URL=https://rpc.ankr.com/somnia_testnet/your-private-key
// Use environment variables
// Setup provider AnkrProvider
const provider = new AnkrProvider(process.env.SOMNIA_ANKR_RPC_URL);
Copy
// Example configuration for private endpoint
const config = {
testnet: {
url: process.env.SOMNIA_TESTNET_RPC_URL,
accounts: [process.env.TESTNET_PRIVATE_KEY]
}
};
Copy
# .env file
SOMNIA_TESTNET_RPC_URL=https://rpc.ankr.com/somnia_testnet/your-private-key
TESTNET_PRIVATE_KEY=your_private_key_here
NODE_ENV=development
Copy
# .gitignore
.env
.env.local
.env.*.local
node_modules/
dist/
Copy
# Project structure
├── .env.example # Template file (safe to commit)
├── .env.development # Development secrets
├── .env.test # Test environment
├── .env.staging # Staging environment
└── .env.production # Production secrets (never commit)
Copy
// config/environment.js
const dotenv = require('dotenv');
const path = require('path');
const environment = process.env.NODE_ENV || 'development';
const envFile = `.env.${environment}`;
dotenv.config({ path: path.resolve(process.cwd(), envFile) });
module.exports = {
rpcUrl: process.env.SOMNIA_RPC_URL,
privateKey: process.env.PRIVATE_KEY,
environment
};
Copy
// utils/blockchain.js
const { ethers } = require('ethers');
const config = require('../config/environment');
class BlockchainService {
constructor() {
this.provider = new ethers.JsonRpcProvider(config.rpcUrl);
this.wallet = new ethers.Wallet(config.privateKey, this.provider);
}
async getBalance(address) {
return await this.provider.getBalance(address);
}
async sendTransaction(to, value) {
const tx = {
to,
value: ethers.parseEther(value.toString())
};
return await this.wallet.sendTransaction(tx);
}
}
module.exports = BlockchainService;
Copy
// test-env.js - For application projects only
require('dotenv').config();
const testEnvironmentVariables = () => {
const requiredVars = [\
'SOMNIA_TESTNET_RPC_URL',\
'TESTNET_PRIVATE_KEY'\
];
const missing = requiredVars.filter(varName => !process.env[varName]);
if (missing.length > 0) {
console.error('Missing required environment variables:', missing);
process.exit(1);
}
console.log('All required environment variables are loaded');
};
testEnvironmentVariables();
Copy
# 1. Initialize project
npm init -y
npm install ethers dotenv
npm install -D nodemon
# 2. Create environment template
echo "SOMNIA_RPC_URL=https://rpc.ankr.com/somnia_testnet/your-key-here" > .env.example
echo "PRIVATE_KEY=your-private-key-here" >> .env.example
echo "CONTRACT_ADDRESS=0x..." >> .env.example
# 3. Add to .gitignore
echo ".env*" >> .gitignore
echo "!.env.example" >> .gitignore
Copy
// contracts/SomniaContract.js
const { ethers } = require('ethers');
const config = require('../config/environment');
class SomniaContract {
constructor(contractAddress, abi) {
this.provider = new ethers.JsonRpcProvider(config.rpcUrl);
this.wallet = new ethers.Wallet(config.privateKey, this.provider);
this.contract = new ethers.Contract(contractAddress, abi, this.wallet);
}
async safeCall(methodName, ...args) {
try {
// Estimate gas first
const gasEstimate = await this.contract[methodName].estimateGas(...args);
// Add 20% buffer
const gasLimit = gasEstimate * 120n / 100n;
const tx = await this.contract[methodName](...args, { gasLimit });
console.log(`Transaction sent: ${tx.hash}`);
const receipt = await tx.wait();
console.log(`Transaction confirmed: ${receipt.transactionHash}`);
return receipt;
} catch (error) {
console.error('Transaction failed:', error.message);
throw error;
}
}
}
module.exports = SomniaContract;
Copy
# Example: Configure IP allowlist in your provider dashboard
# Allowed IPs: 203.0.113.1, 203.0.113.2
# This ensures only requests from your servers can use the key
Copy
// Manual key rotation implementation
// Note: RPC providers typically require manual key generation through their dashboard
// This implementation helps manage the rotation process once you have new keys
const updateEnvironmentVariable = async (key, value) => {
// Update .env file or environment configuration
const fs = require('fs').promises;
const envPath = '.env';
try {
let envContent = await fs.readFile(envPath, 'utf8');
const regex = new RegExp(`^${key}=.*$`, 'm');
if (regex.test(envContent)) {
envContent = envContent.replace(regex, `${key}=${value}`);
} else {
envContent += `\n${key}=${value}`;
}
await fs.writeFile(envPath, envContent);
console.log(`Updated ${key} in environment file`);
} catch (error) {
console.error('Failed to update environment variable:', error);
throw error;
}
};
// Manual key rotation helper
const rotateApiKey = async (newKey) => {
try {
// Validate the new key format
if (!newKey || typeof newKey !== 'string') {
throw new Error('Invalid API key provided');
}
// Store old key for reference
const oldKey = process.env.SOMNIA_TESTNET_RPC_URL;
console.log('Rotating API key...');
// Update environment variable
await updateEnvironmentVariable('SOMNIA_TESTNET_RPC_URL', newKey);
console.log('API key rotated successfully');
console.log('Please manually revoke the old key in your RPC provider dashboard');
console.log('Old key (first 10 chars):', oldKey?.substring(0, 10) + '...');
} catch (error) {
console.error('Key rotation failed:', error);
}
};
// Key rotation reminder system
const setupRotationReminder = () => {
const NINETY_DAYS = 90 * 24 * 60 * 60 * 1000;
setInterval(() => {
console.log('\n🔑 SECURITY REMINDER: Consider rotating your RPC API keys');
console.log('1. Generate new key in your RPC provider dashboard');
console.log('2. Call rotateApiKey(newKey) with the new key');
console.log('3. Manually revoke old key in provider dashboard\n');
}, NINETY_DAYS);
};
// Usage example:
// rotateApiKey('https://rpc.ankr.com/somnia_testnet/your-new-private-key');
// setupRotationReminder();
Copy
// AWS Secrets Manager example
const AWS = require('aws-sdk');
const secretsManager = new AWS.SecretsManager();
const getRpcKey = async () => {
const secret = await secretsManager.getSecretValue({
SecretId: 'somnia-rpc-key'
}).promise();
return JSON.parse(secret.SecretString).rpcUrl;
};
Copy
// Example: Secure key generation with ethers.js
const { Wallet } = require('ethers');
const { randomBytes } = require('crypto');
// Generate cryptographically secure random wallet
const generateSecureWallet = () => {
const randomWallet = Wallet.createRandom();
return {
address: randomWallet.address,
privateKey: randomWallet.privateKey,
mnemonic: randomWallet.mnemonic.phrase
};
};
Copy
// Example: Role-based access pattern
class SecureWalletManager {
constructor() {
this.roles = new Map();
this.permissions = {
'admin': ['deploy', 'transfer', 'read'],
'developer': ['deploy', 'read'],
'viewer': ['read']
};
}
assignRole(address, role) {
this.roles.set(address, role);
}
canExecute(address, action) {
const role = this.roles.get(address);
return this.permissions[role]?.includes(action) || false;
}
}
Copy
// Secure logging implementation
const winston = require('winston');
// Create logger with security considerations
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json(),
// Custom format to redact sensitive information
winston.format.printf(({ timestamp, level, message, ...meta }) => {
// Redact sensitive data
const sanitized = JSON.stringify(meta).replace(
/(private_key|api_key|secret)":\s*"[^"]+"/gi,
'$1": "[REDACTED]"'
);
return `${timestamp} [${level}]: ${message} ${sanitized}`;
})
),
transports: [\
new winston.transports.File({ filename: 'error.log', level: 'error' }),\
new winston.transports.File({ filename: 'combined.log' })\
]
});
// Error handling for RPC calls
const safeRpcCall = async (provider, method, params) => {
try {
const result = await provider.send(method, params);
logger.info('RPC call successful', { method, success: true });
return result;
} catch (error) {
// Log error without exposing sensitive information
logger.error('RPC call failed', {
method,
error: error.message,
code: error.code
});
throw new Error(`RPC call failed: ${error.message}`);
}
};
Copy
// Implement retry logic with exponential backoff
const retryRpcCall = async (provider, method, params, maxRetries = 3) => {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await safeRpcCall(provider, method, params);
} catch (error) {
if (attempt === maxRetries) {
logger.error('Max retries exceeded', { method, attempts: attempt });
throw error;
}
const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
logger.warn('Retrying RPC call', { method, attempt, delay });
await new Promise(resolve => setTimeout(resolve, delay));
}
}
};
---
# Building a Simple DEX on Somnia | Somnia Docs
This tutorial will guide you through building a simple Decentralized Exchange (DEX) on Somnia, inspired by Uniswap V2's core mechanics. We'll implement the essential components: Liquidity Pools, Automated Market Maker (AMM) logic, and Token Swapping functionality.
circle-check
Somnia Mainnet is LIVE. To deploy on Somnia Mainnet, you will need SOMI Tokens. Please refer to the [guide](https://docs.somnia.network/get-started/getting-started-for-mainnet)
on Moving from Testnet to Mainnet.
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#prerequisites)
Prerequisites
-----------------------------------------------------------------------------------------------------------------------------------------------------
1. This guide is not an introduction to Solidity Programming; you are expected to understand Basic Solidity Programming.
2. You can deploy the Smart Contracts using our [Hardhat](https://docs.somnia.network/developer/development-frameworks/deploy-with-hardhat)
or [Foundry](https://docs.somnia.network/developer/development-frameworks/deploy-with-foundry)
guides.
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#core-concepts)
Core Concepts
-----------------------------------------------------------------------------------------------------------------------------------------------------
###
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#automated-market-maker-amm)
Automated Market Maker (AMM)
At the core of an AMM is a Liquidity Pool, a Smart Contract that holds reserves of two (or more) tokens (e.g., STT and USDC). Users trade directly against this pool instead of with other users.
Most AMMs (like Uniswap v2) use the constant product formula:
x⋅y=kx \\cdot y = kx⋅y=k
* `x` = amount of Token A in the pool
* `y` = amount of Token B in the pool
* `k` = constant (must remain unchanged)
This ensures price adjustment based on supply and demand.
If a user wants to buy Token A with Token B:
* They send Token B into the Pool
* The Smart Contract calculates how much Token A to send out to maintain `x * y = k`
* As more Token A is withdrawn, its price increases (slippage)
Anyone can deposit an equal value of both tokens into the pool to become a Liquidity Provider (LP) and earn a share of the trading fees (e.g., 0.3%).
> ####
>
> [hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#amms-are-fully-decentralized-with-no-need-for-counterparties-and-are-open-and-permissionless-to-use)
>
> AMMs are fully decentralized with no need for counterparties and are Open and permissionless to use and contribute liquidity.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#liquidity-pools)
Liquidity Pools
Pairs of tokens locked in Smart Contracts that facilitate trading without traditional order books.
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#smart-contract-architecture)
Smart Contract Architecture
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
We'll build three main contracts:
1. `SomniaFactory`: Creates and manages pair contracts
2. `SomniaPair`: Individual liquidity pool for token pairs
3. `SomniaRouter`: User-facing contract for swaps and liquidity management
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#implementation)
Implementation
-------------------------------------------------------------------------------------------------------------------------------------------------------
###
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#erc-20-interface)
ERC-20 Interface
First, let's define the ERC-20 interface we'll use.
chevron-rightSomniaPair.sol[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#somniapair.sol)
###
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#somniapair-contract)
SomniaPair Contract
The pair contract manages individual Liquidity Pools.
chevron-rightSomniaPair.sol[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#somniapair.sol-1)
###
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#somniafactory-contract)
SomniaFactory Contract
The factory creates and tracks all pair contracts.
chevron-rightSomniaFactory.sol[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#somniafactory.sol)
###
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#somniarouter-contract)
SomniaRouter Contract
The router provides user-friendly functions for swapping and liquidity management.
chevron-rightSomniaRouter.sol[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#somniarouter.sol)
You can deploy the Smart Contracts in the order they have been created above. Create `Token A` and `Token B` or, for example, use `wSTT` and `USDC` Token Pairs. The next step will be to create a Pool Pair for `Token A` and `Token B`. Deploy the Pair and Add Liquidity. Then users will be able to create `SWAP` using the `Router` Smart Contract.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#usage-example)
Usage Example
chevron-rightAdd Liquidity[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#add-liquidity)
chevron-rightSwap Tokens[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#swap-tokens)
chevron-rightRemove Liquidity[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#remove-liquidity)
###
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#test-your-dex)
Test Your DEX
Create a test script to verify functionality:
chevron-rightTestDEX.sol[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#testdex.sol)
[hashtag](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#conclusion)
Conclusion
-----------------------------------------------------------------------------------------------------------------------------------------------
This tutorial has walked you through implementing a fully functional decentralized exchange (DEX)on Somnia, demonstrating the core Smart Contract architecture that powers Automated Market Makers - AMM. By building these contracts from scratch, you've gained hands-on experience with the fundamental mechanics of DEXs: 1. How Liquidity Pools maintain token reserves. 2. How the Constant Product Formula enables permissionless trading. 3. How router contracts abstract complex operations into user-friendly interfaces. The implementation covers essential features including liquidity provision with LP token minting, atomic swaps with built-in slippage protection, and multi-hop routing for indirect trading pairs. While this represents a complete Smart Contract foundation, production deployments would benefit from additional features such as concentrated liquidity (as seen in Uniswap V3), dynamic fee tiers, flash loan functionality, and comprehensive governance mechanisms. The modular architecture we've implemented makes these enhancements straightforward to integrate. As you continue developing on Somnia, remember that the Smart Contracts presented here are just one layer of a complete DEX ecosystem; You'll need to consider frontend interfaces, liquidity incentives, and integration with other DeFi protocols to create a thriving exchange. Most importantly, ensure thorough testing on Somnia's testnet and seek professional security audits before deploying any contracts handling real value. This foundation provides you with the knowledge and code necessary to contribute to Somnia's DeFi ecosystem, whether by deploying your own DEX or building innovative features on top of existing protocols.
[PreviousDAO UI Tutorial p3chevron-left](https://docs.somnia.network/developer/building-dapps/example-applications/dao-ui-tutorial-p3)
[NextSecuritychevron-right](https://docs.somnia.network/developer/security)
Last updated 1 month ago
* [Prerequisites](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#prerequisites)
* [Core Concepts](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#core-concepts)
* [Automated Market Maker (AMM)](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#automated-market-maker-amm)
* [Liquidity Pools](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#liquidity-pools)
* [Smart Contract Architecture](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#smart-contract-architecture)
* [Implementation](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#implementation)
* [ERC-20 Interface](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#erc-20-interface)
* [SomniaPair Contract](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#somniapair-contract)
* [SomniaFactory Contract](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#somniafactory-contract)
* [SomniaRouter Contract](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#somniarouter-contract)
* [Usage Example](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#usage-example)
* [Test Your DEX](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#test-your-dex)
* [Conclusion](https://docs.somnia.network/developer/building-dapps/example-applications/building-a-simple-dex-on-somnia#conclusion)
Copy
// IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
Copy
// SomniaPair.sol
pragma solidity ^0.8.0;
import "./IERC20.sol";
contract SomniaPair is IERC20 {
uint256 public constant MINIMUM_LIQUIDITY = 10**3;
address public factory;
address public token0;
address public token1;
uint112 private reserve0;
uint112 private reserve1;
uint32 private blockTimestampLast;
uint256 public kLast;
uint256 private unlocked = 1;
modifier lock() {
require(unlocked == 1, 'LOCKED');
unlocked = 0;
_;
unlocked = 1;
}
// ERC-20 Implementation
string public constant name = "Somnia LP Token";
string public constant symbol = "SLP";
uint8 public constant decimals = 18;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
event Mint(address indexed sender, uint256 amount0, uint256 amount1);
event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to);
event Swap(
address indexed sender,
uint256 amount0In,
uint256 amount1In,
uint256 amount0Out,
uint256 amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
constructor() {
factory = msg.sender;
}
function initialize(address _token0, address _token1) external {
require(msg.sender == factory, 'FORBIDDEN');
token0 = _token0;
token1 = _token1;
}
function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
_reserve0 = reserve0;
_reserve1 = reserve1;
_blockTimestampLast = blockTimestampLast;
}
function _safeTransfer(address token, address to, uint256 value) private {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TRANSFER_FAILED');
}
function _update(uint256 balance0, uint256 balance1, uint112 _reserve0, uint112 _reserve1) private {
require(balance0 <= type(uint112).max && balance1 <= type(uint112).max, 'OVERFLOW');
uint32 blockTimestamp = uint32(block.timestamp % 2**32);
reserve0 = uint112(balance0);
reserve1 = uint112(balance1);
blockTimestampLast = blockTimestamp;
emit Sync(reserve0, reserve1);
}
function mint(address to) external lock returns (uint256 liquidity) {
(uint112 _reserve0, uint112 _reserve1,) = getReserves();
uint256 balance0 = IERC20(token0).balanceOf(address(this));
uint256 balance1 = IERC20(token1).balanceOf(address(this));
uint256 amount0 = balance0 - _reserve0;
uint256 amount1 = balance1 - _reserve1;
uint256 _totalSupply = totalSupply;
if (_totalSupply == 0) {
liquidity = sqrt(amount0 * amount1) - MINIMUM_LIQUIDITY;
_mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
} else {
liquidity = min(amount0 * _totalSupply / _reserve0, amount1 * _totalSupply / _reserve1);
}
require(liquidity > 0, 'INSUFFICIENT_LIQUIDITY_MINTED');
_mint(to, liquidity);
_update(balance0, balance1, _reserve0, _reserve1);
kLast = uint256(reserve0) * reserve1;
emit Mint(msg.sender, amount0, amount1);
}
function burn(address to) external lock returns (uint256 amount0, uint256 amount1) {
(uint112 _reserve0, uint112 _reserve1,) = getReserves();
address _token0 = token0;
address _token1 = token1;
uint256 balance0 = IERC20(_token0).balanceOf(address(this));
uint256 balance1 = IERC20(_token1).balanceOf(address(this));
uint256 liquidity = balanceOf[address(this)];
uint256 _totalSupply = totalSupply;
amount0 = liquidity * balance0 / _totalSupply;
amount1 = liquidity * balance1 / _totalSupply;
require(amount0 > 0 && amount1 > 0, 'INSUFFICIENT_LIQUIDITY_BURNED');
_burn(address(this), liquidity);
_safeTransfer(_token0, to, amount0);
_safeTransfer(_token1, to, amount1);
balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));
_update(balance0, balance1, _reserve0, _reserve1);
kLast = uint256(reserve0) * reserve1;
emit Burn(msg.sender, amount0, amount1, to);
}
function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external lock {
require(amount0Out > 0 || amount1Out > 0, 'INSUFFICIENT_OUTPUT_AMOUNT');
(uint112 _reserve0, uint112 _reserve1,) = getReserves();
require(amount0Out < _reserve0 && amount1Out < _reserve1, 'INSUFFICIENT_LIQUIDITY');
uint256 balance0;
uint256 balance1;
{
address _token0 = token0;
address _token1 = token1;
require(to != _token0 && to != _token1, 'INVALID_TO');
if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out);
if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out);
balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));
}
uint256 amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
uint256 amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
require(amount0In > 0 || amount1In > 0, 'INSUFFICIENT_INPUT_AMOUNT');
{
uint256 balance0Adjusted = balance0 * 1000 - amount0In * 3;
uint256 balance1Adjusted = balance1 * 1000 - amount1In * 3;
require(balance0Adjusted * balance1Adjusted >= uint256(_reserve0) * _reserve1 * 1000**2, 'K');
}
_update(balance0, balance1, _reserve0, _reserve1);
emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
}
// Helper functions
function sqrt(uint256 y) internal pure returns (uint256 z) {
if (y > 3) {
z = y;
uint256 x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = x < y ? x : y;
}
// ERC-20 functions
function _mint(address to, uint256 value) internal {
totalSupply += value;
balanceOf[to] += value;
emit Transfer(address(0), to, value);
}
function _burn(address from, uint256 value) internal {
balanceOf[from] -= value;
totalSupply -= value;
emit Transfer(from, address(0), value);
}
function approve(address spender, uint256 value) external returns (bool) {
allowance[msg.sender][spender] = value;
emit Approval(msg.sender, spender, value);
return true;
}
function transfer(address to, uint256 value) external returns (bool) {
balanceOf[msg.sender] -= value;
balanceOf[to] += value;
emit Transfer(msg.sender, to, value);
return true;
}
function transferFrom(address from, address to, uint256 value) external returns (bool) {
if (allowance[from][msg.sender] != type(uint256).max) {
allowance[from][msg.sender] -= value;
}
balanceOf[from] -= value;
balanceOf[to] += value;
emit Transfer(from, to, value);
return true;
}
}
Copy
// SomniaFactory.sol
pragma solidity ^0.8.0;
import "./SomniaPair.sol";
contract SomniaFactory {
mapping(address => mapping(address => address)) public getPair;
address[] public allPairs;
event PairCreated(address indexed token0, address indexed token1, address pair, uint256);
function allPairsLength() external view returns (uint256) {
return allPairs.length;
}
function createPair(address tokenA, address tokenB) external returns (address pair) {
require(tokenA != tokenB, 'IDENTICAL_ADDRESSES');
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0), 'ZERO_ADDRESS');
require(getPair[token0][token1] == address(0), 'PAIR_EXISTS');
bytes memory bytecode = type(SomniaPair).creationCode;
bytes32 salt = keccak256(abi.encodePacked(token0, token1));
assembly {
pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
}
SomniaPair(pair).initialize(token0, token1);
getPair[token0][token1] = pair;
getPair[token1][token0] = pair;
allPairs.push(pair);
emit PairCreated(token0, token1, pair, allPairs.length);
}
}
Copy
// SomniaRouter.sol
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./SomniaPair.sol";
import "./SomniaFactory.sol";
contract SomniaRouter {
address public immutable factory;
modifier ensure(uint256 deadline) {
require(deadline >= block.timestamp, 'EXPIRED');
_;
}
constructor(address _factory) {
factory = _factory;
}
// Add liquidity
function addLiquidity(
address tokenA,
address tokenB,
uint256 amountADesired,
uint256 amountBDesired,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
) external ensure(deadline) returns (uint256 amountA, uint256 amountB, uint256 liquidity) {
(amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);
address pair = pairFor(tokenA, tokenB);
_safeTransferFrom(tokenA, msg.sender, pair, amountA);
_safeTransferFrom(tokenB, msg.sender, pair, amountB);
liquidity = SomniaPair(pair).mint(to);
}
function _addLiquidity(
address tokenA,
address tokenB,
uint256 amountADesired,
uint256 amountBDesired,
uint256 amountAMin,
uint256 amountBMin
) internal returns (uint256 amountA, uint256 amountB) {
if (SomniaFactory(factory).getPair(tokenA, tokenB) == address(0)) {
SomniaFactory(factory).createPair(tokenA, tokenB);
}
(uint256 reserveA, uint256 reserveB) = getReserves(tokenA, tokenB);
if (reserveA == 0 && reserveB == 0) {
(amountA, amountB) = (amountADesired, amountBDesired);
} else {
uint256 amountBOptimal = quote(amountADesired, reserveA, reserveB);
if (amountBOptimal <= amountBDesired) {
require(amountBOptimal >= amountBMin, 'INSUFFICIENT_B_AMOUNT');
(amountA, amountB) = (amountADesired, amountBOptimal);
} else {
uint256 amountAOptimal = quote(amountBDesired, reserveB, reserveA);
assert(amountAOptimal <= amountADesired);
require(amountAOptimal >= amountAMin, 'INSUFFICIENT_A_AMOUNT');
(amountA, amountB) = (amountAOptimal, amountBDesired);
}
}
}
// Remove liquidity
function removeLiquidity(
address tokenA,
address tokenB,
uint256 liquidity,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
) public ensure(deadline) returns (uint256 amountA, uint256 amountB) {
address pair = pairFor(tokenA, tokenB);
SomniaPair(pair).transferFrom(msg.sender, pair, liquidity);
(uint256 amount0, uint256 amount1) = SomniaPair(pair).burn(to);
(address token0,) = sortTokens(tokenA, tokenB);
(amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
require(amountA >= amountAMin, 'INSUFFICIENT_A_AMOUNT');
require(amountB >= amountBMin, 'INSUFFICIENT_B_AMOUNT');
}
// Swap functions
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external ensure(deadline) returns (uint256[] memory amounts) {
amounts = getAmountsOut(amountIn, path);
require(amounts[amounts.length - 1] >= amountOutMin, 'INSUFFICIENT_OUTPUT_AMOUNT');
_safeTransferFrom(
path[0], msg.sender, pairFor(path[0], path[1]), amounts[0]
);
_swap(amounts, path, to);
}
function swapTokensForExactTokens(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to,
uint256 deadline
) external ensure(deadline) returns (uint256[] memory amounts) {
amounts = getAmountsIn(amountOut, path);
require(amounts[0] <= amountInMax, 'EXCESSIVE_INPUT_AMOUNT');
_safeTransferFrom(
path[0], msg.sender, pairFor(path[0], path[1]), amounts[0]
);
_swap(amounts, path, to);
}
// Internal functions
function _swap(uint256[] memory amounts, address[] memory path, address _to) internal {
for (uint256 i; i < path.length - 1; i++) {
(address input, address output) = (path[i], path[i + 1]);
(address token0,) = sortTokens(input, output);
uint256 amountOut = amounts[i + 1];
(uint256 amount0Out, uint256 amount1Out) = input == token0 ? (uint256(0), amountOut) : (amountOut, uint256(0));
address to = i < path.length - 2 ? pairFor(output, path[i + 2]) : _to;
SomniaPair(pairFor(input, output)).swap(
amount0Out, amount1Out, to, new bytes(0)
);
}
}
// Library functions
function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
require(tokenA != tokenB, 'IDENTICAL_ADDRESSES');
(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0), 'ZERO_ADDRESS');
}
function pairFor(address tokenA, address tokenB) internal view returns (address pair) {
(address token0, address token1) = sortTokens(tokenA, tokenB);
pair = SomniaFactory(factory).getPair(token0, token1);
require(pair != address(0), 'PAIR_DOES_NOT_EXIST');
}
function getReserves(address tokenA, address tokenB) internal view returns (uint256 reserveA, uint256 reserveB) {
(address token0,) = sortTokens(tokenA, tokenB);
(uint256 reserve0, uint256 reserve1,) = SomniaPair(pairFor(tokenA, tokenB)).getReserves();
(reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
}
function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) internal pure returns (uint256 amountB) {
require(amountA > 0, 'INSUFFICIENT_AMOUNT');
require(reserveA > 0 && reserveB > 0, 'INSUFFICIENT_LIQUIDITY');
amountB = amountA * reserveB / reserveA;
}
function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) internal pure returns (uint256 amountOut) {
require(amountIn > 0, 'INSUFFICIENT_INPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0, 'INSUFFICIENT_LIQUIDITY');
uint256 amountInWithFee = amountIn * 997;
uint256 numerator = amountInWithFee * reserveOut;
uint256 denominator = reserveIn * 1000 + amountInWithFee;
amountOut = numerator / denominator;
}
function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut) internal pure returns (uint256 amountIn) {
require(amountOut > 0, 'INSUFFICIENT_OUTPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0, 'INSUFFICIENT_LIQUIDITY');
uint256 numerator = reserveIn * amountOut * 1000;
uint256 denominator = (reserveOut - amountOut) * 997;
amountIn = (numerator / denominator) + 1;
}
function getAmountsOut(uint256 amountIn, address[] memory path) public view returns (uint256[] memory amounts) {
require(path.length >= 2, 'INVALID_PATH');
amounts = new uint256[](path.length);
amounts[0] = amountIn;
for (uint256 i; i < path.length - 1; i++) {
(uint256 reserveIn, uint256 reserveOut) = getReserves(path[i], path[i + 1]);
amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
}
}
function getAmountsIn(uint256 amountOut, address[] memory path) public view returns (uint256[] memory amounts) {
require(path.length >= 2, 'INVALID_PATH');
amounts = new uint256[](path.length);
amounts[amounts.length - 1] = amountOut;
for (uint256 i = path.length - 1; i > 0; i--) {
(uint256 reserveIn, uint256 reserveOut) = getReserves(path[i - 1], path[i]);
amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
}
}
function _safeTransferFrom(address token, address from, address to, uint256 value) private {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TRANSFER_FROM_FAILED');
}
}
Copy
// Approve router to spend tokens
tokenA.approve(router.address, amountA);
tokenB.approve(router.address, amountB);
// Add liquidity
router.addLiquidity(
tokenA.address,
tokenB.address,
amountA,
amountB,
minAmountA,
minAmountB,
userAddress,
deadline
);
Copy
// Approve router to spend input token
tokenA.approve(router.address, amountIn);
// Swap exact tokens for tokens
address[] memory path = new address[](2);
path[0] = tokenA.address;
path[1] = tokenB.address;
router.swapExactTokensForTokens(
amountIn,
minAmountOut,
path,
userAddress,
deadline
);
Copy
// Approve router to spend LP tokens
pair.approve(router.address, lpTokenAmount);
// Remove liquidity
router.removeLiquidity(
tokenA.address,
tokenB.address,
lpTokenAmount,
minAmountA,
minAmountB,
userAddress,
deadline
);
Copy
pragma solidity ^0.8.0;
import "./SomniaFactory.sol";
import "./SomniaRouter.sol";
import "./IERC20.sol";
contract TestDEX {
SomniaFactory public factory;
SomniaRouter public router;
constructor() {
factory = new SomniaFactory();
router = new SomniaRouter(address(factory));
}
function testCreatePair(address tokenA, address tokenB) external returns (address) {
return factory.createPair(tokenA, tokenB);
}
function testAddLiquidity(
address tokenA,
address tokenB,
uint256 amountA,
uint256 amountB
) external {
IERC20(tokenA).transferFrom(msg.sender, address(this), amountA);
IERC20(tokenB).transferFrom(msg.sender, address(this), amountB);
IERC20(tokenA).approve(address(router), amountA);
IERC20(tokenB).approve(address(router), amountB);
router.addLiquidity(
tokenA,
tokenB,
amountA,
amountB,
0,
0,
msg.sender,
block.timestamp + 3600
);
}
}
---
# Streams Case Study: Formula 1 | Somnia Docs
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/streams-case-study-formula-1#schemas)
Schemas
Driver schema
Copy
uint32 number, string name, string abbreviation, string teamName, string teamColor
Cartesian 3D coordinates schema
Copy
int256 x, int256 y, int256 z
The driver schema can extend the cartesian coordinates since the 3D coordinates will be used widely for other applications. Again this promotes re-usability of schemas.
###
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/streams-case-study-formula-1#schema-registration-and-re-use)
Schema registration and re-use
Copy
const { SDK, zeroBytes32, SchemaEncoder } = require("@somnia-chain/streams");
const {
createPublicClient,
http,
createWalletClient,
toHex,
defineChain,
} = require("viem");
const { privateKeyToAccount } = require("viem/accounts");
const dreamChain = defineChain({
id: 50312,
name: "Somnia Testnet",
network: "testnet",
nativeCurrency: {
decimals: 18,
name: "STT",
symbol: "STT",
},
rpcUrls: {
default: {
http: [\
"https://dream-rpc.somnia.network",\
],
},
public: {
http: [\
"https://dream-rpc.somnia.network",\
],
},
},
})
async function main() {
// Connect to the blockchain to read data with the public client
const publicClient = createPublicClient({
chain: dreamChain,
transport: http(),
})
const walletClient = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY),
chain: dreamChain,
transport: http(),
})
// Connect to the SDK
const sdk = new SDK({
public: publicClient,
wallet: walletClient,
})
// Setup the schemas
const coordinatesSchema = `int256 x, int256 y, int256 z`
const driverSchema = `uint32 number, string name, string abbreviation, string teamName, string teamColor`
// Derive Etherbase schema metadata
const coordinatesSchemaId = await sdk.streams.computeSchemaId(
coordinatesSchema
)
if (!coordinatesSchemaId) {
throw new Error("Unable to compute the schema ID for the coordinates schema")
}
const driverSchemaId = await sdk.streams.computeSchemaId(
driverSchema
)
if (!driverSchemaId) {
throw new Error("Unable to compute the schema ID for the driver schema")
}
const extendedSchema = `${driverSchema}, ${coordinatesSchema}`
console.log("Schemas in use", {
coordinatesSchemaId,
driverSchemaId,
coordinatesSchema,
driverSchema,
extendedSchema
})
const isCoordinatesSchemaRegistered = await sdk.streams.isDataSchemaRegistered(coordinatesSchemaId)
if (!isCoordinatesSchemaRegistered) {
// We want to publish the driver schema but we need to publish the coordinates schema first before it can be extended
const registerCoordinatesSchemaTxHash =
await sdk.streams.registerDataSchemas([\
{ schemaName: "coords", schema: coordinatesSchema }\
])
if (!registerCoordinatesSchemaTxHash) {
throw new Error("Failed to register coordinates schema")
}
console.log("Registered coordinates schema on-chain", {
registerCoordinatesSchemaTxHash
})
await publicClient.waitForTransactionReceipt({
hash: registerCoordinatesSchemaTxHash
})
}
const isDriverSchemaRegistered = await sdk.streams.isDataSchemaRegistered(driverSchemaId)
if (!isDriverSchemaRegistered) {
// Now, publish the driver schema but extend the coordinates schema!
const registerDriverSchemaTxHash = sdk.streams.registerDataSchemas([\
{ schemaName: "driver", schema: driverSchema, parentSchemaId: coordinatesSchemaId }\
])
if (!registerDriverSchemaTxHash) {
throw new Error("Failed to register schema on-chain")
}
console.log("Registered driver schema on-chain", {
registerDriverSchemaTxHash,
})
await publicClient.waitForTransactionReceipt({
hash: registerDriverSchemaTxHash
})
}
// Publish some data!!
const schemaEncoder = new SchemaEncoder(extendedSchema)
const encodedData = schemaEncoder.encodeData([\
{ name: "number", value: "44", type: "uint32" },\
{ name: "name", value: "Lewis Hamilton", type: "string" },\
{ name: "abbreviation", value: "HAM", type: "string" },\
{ name: "teamName", value: "Ferrari", type: "string" },\
{ name: "teamColor", value: "#F91536", type: "string" },\
{ name: "x", value: "-1513", type: "int256" },\
{ name: "y", value: "0", type: "int256" },\
{ name: "z", value: "955", type: "int256" },\
])
console.log("encodedData", encodedData)
const dataStreams = [{\
// Data id: DRIVER number - index will be a helpful lookup later and references ./data/f1-coordinates.js Cube 4 coordinates (driver 44) - F1 telemetry data\
id: toHex(`44-0`, { size: 32 }),\
schemaId: driverSchemaId,\
data: encodedData\
}]
const publishTxHash = await sdk.streams.set(dataStreams)
console.log("\nPublish Tx Hash", publishTxHash)
}
[PreviousBuild Your First Schemachevron-left](https://docs.somnia.network/developer/data-streams/tutorials/build-your-first-schema)
[NextREAD Stream Data from a UI (Next.js Example)chevron-right](https://docs.somnia.network/developer/data-streams/tutorials/read-stream-data-from-a-ui-next.js-example)
Last updated 1 month ago
* [Schemas](https://docs.somnia.network/developer/data-streams/tutorials/streams-case-study-formula-1#schemas)
* [Schema registration and re-use](https://docs.somnia.network/developer/data-streams/tutorials/streams-case-study-formula-1#schema-registration-and-re-use)
---
# Working with Multiple Publishers in a Shared Stream | Somnia Docs
The core architecture of Somnia Data Streams decouples data schemas from publishers. This allows multiple different accounts (or devices) to publish data using the **same schema**. The data conforms to the same data structure, regardless of who published it.
This model is perfect for multi-source data scenarios, such as:
* **Multi-User Chat:** Multiple users sending messages under the same `chatMessage` schema.
* **IoT (Internet of Things):** Hundreds of sensors submitting data under the same `telemetry` schema.
* **Gaming:** All players in a game publishing their positions and scores under the same `playerUpdate` schema.
In this tutorial, we will demonstrate how to build an "aggregator" application that collects and merges data from two different "devices" (two separate wallet accounts) publishing to the same "telemetry" schema.
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream#prerequisites)
Prerequisites
------------------------------------------------------------------------------------------------------------------------------------------------------------
* Node.js 20+
* `@somnia-chain/streams` and `viem` libraries installed
* An `RPC_URL` for access to the Somnia Testnet
* **Two (2)** funded Somnia Testnet wallets for publishing data.
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream#what-youll-build)
What You’ll Build
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
In this tutorial, we will build two main components:
1. A **Publisher Script** that simulates two different wallets sending data to the same telemetry schema.
2. An **Aggregator Script** that fetches _all_ data from a specified list of publishers, merges them into a single list, and sorts them by timestamp.
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream#project-setup)
Project Setup
------------------------------------------------------------------------------------------------------------------------------------------------------------
Create a new directory for your application and install the necessary packages.
Create a `tsconfig.json` file in your project root:
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream#configure-environment-variables)
Configure Environment Variables
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Create a `.env` file in your project root. For this tutorial, we will need **two** different private keys.
circle-info
**IMPORTANT:** Never expose private keys in client-side (browser) code or public repositories. The scripts in this tutorial are intended to be run server-side.
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream#chain-and-client-configuration)
Chain and Client Configuration
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Create a folder named `src/lib` and set up your `chain.ts` and `clients.ts` files.
`**src/lib/chain.ts**`
`**src/lib/clients.ts**`
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream#define-the-shared-schema)
Define the Shared Schema
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Let's define the common schema that all publishers will use.
`**src/lib/schema.ts**`
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream#create-the-publisher-script)
Create the Publisher Script
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Now, let's create a script that simulates how two different publishers will send data to this schema. This script will take which publisher to use as a command-line argument.
`**src/scripts/publishData.ts**`
**To run this script:**
Add the following scripts to your `package.json` file:
Now, open two different terminals and send data from each:
Repeat this a few times to build up a dataset from both publishers.
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream#create-the-aggregator-script)
Create the Aggregator Script
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Now that we have published our data, we can write the "aggregator" script that collects, merges, and sorts all data from these two (or more) publishers.
`**src/scripts/aggregateData.ts**`
**To run this script:**
Add the script to your `package.json` file:
And run it:
[hashtag](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream#conclusion)
Conclusion
------------------------------------------------------------------------------------------------------------------------------------------------------
In this tutorial, you learned how to manage a multi-publisher architecture with Somnia Data Streams.
* **Publisher Side:** The logic remained unchanged. Each publisher independently published its data using its wallet and the `sdk.streams.set()` method.
* **Aggregator Side:** This is where the main logic came in.
1. We maintained a list of publishers we were interested in.
2. We fetched the data for each publisher separately using the `getAllPublisherDataForSchema` method.
3. We combined the incoming data into a single array (`allRecords.push(...)`).
4. Finally, we sorted all the data on the client-side to display them in a meaningful order (e.g., by timestamp).
This pattern can be scaled to support any number of publishers and provides a robust foundation for building decentralized, multi-source applications.
[PreviousIntegrate Chainlink Oracleschevron-left](https://docs.somnia.network/developer/data-streams/tutorials/integrate-chainlink-oracles)
[NextThe DApp Publisher Proxy Patternchevron-right](https://docs.somnia.network/developer/data-streams/tutorials/the-dapp-publisher-proxy-pattern)
Last updated 1 month ago
* [Prerequisites](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream#prerequisites)
* [What You’ll Build](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream#what-youll-build)
* [Project Setup](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream#project-setup)
* [Configure Environment Variables](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream#configure-environment-variables)
* [Chain and Client Configuration](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream#chain-and-client-configuration)
* [Define the Shared Schema](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream#define-the-shared-schema)
* [Create the Publisher Script](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream#create-the-publisher-script)
* [Create the Aggregator Script](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream#create-the-aggregator-script)
* [Conclusion](https://docs.somnia.network/developer/data-streams/tutorials/working-with-multiple-publishers-in-a-shared-stream#conclusion)
Copy
mkdir somnia-aggregator
cd somnia-aggregator
npm init -y
npm i @somnia-chain/streams viem dotenv
npm i -D @types/node typescript ts-node
Copy
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist"
},
"include": ["src/**/*"]
}
Copy
# .env
RPC_URL=https://dream-rpc.somnia.network/
# Simulates two different devices/publishers
PUBLISHER_1_PK=0xPUBLISHER_ONE_PRIVATE_KEY
PUBLISHER_2_PK=0xPUBLISHER_TWO_PRIVATE_KEY
Copy
import { defineChain } from 'viem'
export const somniaTestnet = defineChain({
id: 50312,
name: 'Somnia Testnet',
network: 'somnia-testnet',
nativeCurrency: { name: 'STT', symbol: 'STT', decimals: 18 },
rpcUrls: {
default: { http: ['[https://dream-rpc.somnia.network]'] },
public: { http: ['[https://dream-rpc.somnia.network]'] },
},
} as const)
Copy
import 'dotenv/config'
import { createPublicClient, createWalletClient, http, PublicClient } from 'viem'
import { privateKeyToAccount, PrivateKeyAccount } from 'viem/accounts'
import { somniaTestnet } from './chain'
function getEnv(key: string): string {
const value = process.env[key]
if (!value) {
throw new Error(`Missing environment variable: ${key}`)
}
return value
}
// A single Public Client for read operations
export const publicClient: PublicClient = createPublicClient({
chain: somniaTestnet,
transport: http(getEnv('RPC_URL')),
})
// Two different Wallet Clients for simulation
export const walletClient1 = createWalletClient({
account: privateKeyToAccount(getEnv('PUBLISHER_1_PK') as `0x${string}`),
chain: somniaTestnet,
transport: http(getEnv('RPC_URL')),
})
export const walletClient2 = createWalletClient({
account: privateKeyToAccount(getEnv('PUBLISHER_2_PK') as `0x${string}`),
chain: somniaTestnet,
transport: http(getEnv('RPC_URL')),
})
Copy
// This schema will be used by multiple devices
export const telemetrySchema =
'uint64 timestamp, string deviceId, int32 x, int32 y, uint32 speed'
Copy
import 'dotenv/config'
import { SDK, SchemaEncoder, zeroBytes32 } from '@somnia-chain/streams'
import { publicClient, walletClient1, walletClient2 } from '../lib/clients'
import { telemetrySchema } from '../lib/schema'
import { toHex, Hex, WalletClient } from 'viem'
import { waitForTransactionReceipt } from 'viem/actions'
// Select which publisher to use
async function getPublisher(): Promise<{ client: WalletClient, deviceId: string }> {
const arg = process.argv[2] // 'p1' or 'p2'
if (arg === 'p2') {
console.log('Using Publisher 2 (Device B)')
return { client: walletClient2, deviceId: 'device-b-002' }
}
console.log('Using Publisher 1 (Device A)')
return { client: walletClient1, deviceId: 'device-a-001' }
}
// Helper function to encode the data
function encodeTelemetry(encoder: SchemaEncoder, deviceId: string): Hex {
const now = Date.now().toString()
return encoder.encodeData([\
{ name: "timestamp", value: now, type: "uint64" },\
{ name: "deviceId", value: deviceId, type: "string" },\
{ name: "x", value: Math.floor(Math.random() * 1000).toString(), type: "int32" },\
{ name: "y", value: Math.floor(Math.random() * 1000).toString(), type: "int32" },\
{ name: "speed", value: Math.floor(Math.random() * 120).toString(), type: "uint32" },\
])
}
async function main() {
const { client, deviceId } = await getPublisher()
const publisherAddress = client.account.address
console.log(`Publisher Address: ${publisherAddress}`)
const sdk = new SDK({ public: publicClient, wallet: client })
const encoder = new SchemaEncoder(telemetrySchema)
// 1. Compute the Schema ID
const schemaId = await sdk.streams.computeSchemaId(telemetrySchema)
if (!schemaId) throw new Error('Could not compute schemaId')
console.log(`Schema ID: ${schemaId}`)
// 2. Register Schema (Updated for new API)
console.log('Registering schema (if not already registered)...')
const ignoreAlreadyRegisteredSchemas = true
const regTx = await sdk.streams.registerDataSchemas([\
{ \
schemaName: 'telemetry', // Updated: 'id' is now 'schemaName'\
schema: telemetrySchema, \
parentSchemaId: zeroBytes32 \
}\
], ignoreAlreadyRegisteredSchemas)
if (regTx) {
console.log('Schema registration transaction sent:', regTx)
await waitForTransactionReceipt(publicClient, { hash: regTx })
console.log('Schema registered successfully!')
} else {
console.log('Schema was already registered. No transaction sent.')
}
// 3. Encode the data
const encodedData = encodeTelemetry(encoder, deviceId)
// 4. Publish the data
// We make the dataId unique with a timestamp and device ID
const dataId = toHex(`${deviceId}-${Date.now()}`, { size: 32 })
const txHash = await sdk.streams.set([\
{ id: dataId, schemaId, data: encodedData }\
])
if (!txHash) throw new Error('Failed to publish data')
console.log(`Publishing data... Tx: ${txHash}`)
await waitForTransactionReceipt(publicClient, { hash: txHash })
console.log('Data published successfully!')
}
main().catch((e) => {
console.error(e)
process.exit(1)
})
Copy
"scripts": {
"publish:p1": "ts-node src/scripts/publishData.ts p1",
"publish:p2": "ts-node src/scripts/publishData.ts p2"
}
Copy
# Terminal 1
npm run publish:p1
# Terminal 2
npm run publish:p2
Copy
import 'dotenv/config'
import { SDK, SchemaDecodedItem } from '@somnia-chain/streams'
import { publicClient, walletClient1, walletClient2 } from '../lib/clients'
import { telemetrySchema } from '../lib/schema'
import { Address } from 'viem'
// LIST OF PUBLISHERS TO TRACK
// You could also fetch this list dynamically (e.g., from a contract or database).
const TRACKED_PUBLISHERS: Address[] = [\
walletClient1.account.address,\
walletClient2.account.address,\
]
// Helper function to convert SDK data into a cleaner object
// (Similar to the 'val' function in the Minimal On-Chain Chat App Tutorial)
function decodeTelemetryRecord(row: SchemaDecodedItem[]): TelemetryRecord {
const val = (field: any) => field?.value?.value ?? field?.value ?? ''
return {
timestamp: Number(val(row[0])),
deviceId: String(val(row[1])),
x: Number(val(row[2])),
y: Number(val(row[3])),
speed: Number(val(row[4])),
}
}
// Type definition for our data
interface TelemetryRecord {
timestamp: number
deviceId: string
x: number
y: number
speed: number
publisher?: Address // We will add this field later
}
async function main() {
// The aggregator doesn't need to write data, so it only uses the publicClient
const sdk = new SDK({ public: publicClient })
const schemaId = await sdk.streams.computeSchemaId(telemetrySchema)
if (!schemaId) throw new Error('Could not compute schemaId')
console.log(`Aggregator started. Tracking ${TRACKED_PUBLISHERS.length} publishers...`)
console.log(`Schema ID: ${schemaId}\n`)
const allRecords: TelemetryRecord[] = []
// 1. Loop through each publisher
for (const publisherAddress of TRACKED_PUBLISHERS) {
console.log(`--- Fetching data for ${publisherAddress} ---`)
// 2. Fetch all data for the publisher based on the schema
// Note: The SDK automatically decodes the data if the schema is registered
const data = await sdk.streams.getAllPublisherDataForSchema(schemaId, publisherAddress)
if (!data || data.length === 0) {
console.log('No data found for this publisher.\n')
continue
}
// 3. Transform the data and add the 'publisher' field
const records: TelemetryRecord[] = (data as SchemaDecodedItem[][]).map(row => ({
...decodeTelemetryRecord(row),
publisher: publisherAddress // To know where the data came from
}))
console.log(`Found ${records.length} records.`)
// 4. Add all records to the main list
allRecords.push(...records)
}
// 5. Sort all data by timestamp
console.log('\n--- Aggregation Complete ---')
console.log(`Total records fetched: ${allRecords.length}`)
allRecords.sort((a, b) => a.timestamp - b.timestamp)
// 6. Display the result
console.log('\n--- Combined and Sorted Telemetry Log ---')
allRecords.forEach(record => {
console.log(
`[${new Date(record.timestamp).toISOString()}] [${record.publisher}] - Device: ${record.deviceId}, Speed: ${record.speed}`
)
})
}
main().catch((e) => {
console.error(e)
process.exit(1)
})
Copy
"scripts": {
"publish:p1": "ts-node src/scripts/publishData.ts p1",
"publish:p2": "ts-node src/scripts/publishData.ts p2",
"aggregate": "ts-node src/scripts/aggregateData.ts"
}
Copy
npm run aggregate
---
# Local Testing and Forking | Somnia Docs
circle-info
This page shows how to spin up a local EVM node with **Hardhat** (and optionally **Anvil**) and fork **Somnia Testnet (Shannon)** or **Somnia Mainnet** for realistic testing.
* * *
[hashtag](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#prerequisites)
Prerequisites
----------------------------------------------------------------------------------------------------------------------------------
* Node.js 20+
* Hardhat (or Foundry/Anvil if you prefer)
* A `.env` file with Somnia RPCs:
.env (example)
Copy
# .env (example)
SOMNIA_RPC_MAINNET=https://api.infra.mainnet.somnia.network/
SOMNIA_RPC_TESTNET=https://dream-rpc.somnia.network/
# Optional: pin block numbers for reproducible forks
FORK_BLOCK_MAINNET=
FORK_BLOCK_TESTNET=
> Do not commit real keys/tokens. Use environment variables.
* * *
[hashtag](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#quick-start-hardhat)
Quick Start (Hardhat)
------------------------------------------------------------------------------------------------------------------------------------------------
1
Install packages
Copy
npm i -D hardhat @nomicfoundation/hardhat-ethers ethers dotenv
npx hardhat # if project not initialized yet
cp .env.example .env || true
2
hardhat.config.ts
Copy
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-ethers";
import * as dotenv from "dotenv";
dotenv.config();
const cfgFromEnv = {
mainnetUrl: process.env.SOMNIA_RPC_MAINNET || "https://api.infra.mainnet.somnia.network/",
testnetUrl: process.env.SOMNIA_RPC_TESTNET || "https://dream-rpc.somnia.network/",
forkMainnetBlock: process.env.FORK_BLOCK_MAINNET ? Number(process.env.FORK_BLOCK_MAINNET) : undefined,
forkTestnetBlock: process.env.FORK_BLOCK_TESTNET ? Number(process.env.FORK_BLOCK_TESTNET) : undefined,
};
const config: HardhatUserConfig = {
solidity: "0.8.19",
networks: {
hardhat: {
chainId: 31337,
},
localhost: {
url: "http://127.0.0.1:8545",
chainId: 31337,
},
somnia_testnet: {
url: cfgFromEnv.testnetUrl,
chainId: 50312,
},
somnia_mainnet: {
url: cfgFromEnv.mainnetUrl,
chainId: 5031,
},
},
};
export default config;
* * *
[hashtag](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#testing-your-dapp-locally)
Testing Your DApp Locally
----------------------------------------------------------------------------------------------------------------------------------------------------------
Once your Hardhat environment is set up, you can write and run tests for your smart contracts. This ensures your code works as expected before deploying it.
**Create a Simple Smart Contract**
First, create a basic smart contract to test. The `Counter.sol` contract below includes fundamental functions for incrementing a counter and retrieving its current value. Save this file in your project's `contracts` directory.
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#write-a-test-file)
**Write a Test File**
Next, write a test file to verify the functionality of your smart contract. The `Counter.test.ts` file below deploys the `Counter` contract on a local test network, calls the `increment` function, and checks whether the outcome is as expected. Save this file in your project's `test` directory.
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#run-your-tests)
**Run Your Tests**
To run your tests, navigate to your project's root directory in the terminal and use the following command. This command will execute your test scripts using Hardhat's built-in test network and display the results.
* * *
[hashtag](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#forking-somnia-testnet-vs-mainnet)
Forking Somnia (Testnet vs Mainnet)
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
_Forking is the process of copying the state of a live network, like Somnia Mainnet or Testnet, at a specific block and creating a simulation of it on your local machine. This powerful feature allows you to test how your contract will interact with other deployed contracts on the live network (such as a DEX, oracle, or NFT marketplace) using real-world data, but without any of the risk or cost._
You can fork Shannon Testnet for faster iteration or Mainnet for production-like state. For deterministic CI, always pin a blockNumber.
Fork Testnet (Shannon)
Fork Mainnet
> Testnet (STT) is ideal for validating flows and cheaper RPC limits; Mainnet (SOMI) reflects real contract/state and gas rules.
* * *
[hashtag](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#handy-rpc-tricks-hardhat)
Handy RPC Tricks (Hardhat)
----------------------------------------------------------------------------------------------------------------------------------------------------------
These RPC methods provided by Hardhat Network allow you to manipulate the blockchain state for advanced testing scenarios.
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#impersonate-an-account)
**Impersonate an account**
This allows you to execute transactions from any wallet address on the forked chain, which is perfect for testing functions with admin privileges or interacting with contracts using an account that holds a large amount of tokens ("whale").
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#time-travel)
**Time travel**
This feature lets you change the timestamp of future blocks. It's incredibly useful for testing time-dependent smart contract logic, such as vesting schedules, lock-up periods, or any functionality that relies on `block.timestamp`.
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#snapshot-and-revert)
**Snapshot and Revert**
This allows you to save the current state of the blockchain and later restore it instantly. It's an efficient way to isolate your tests, ensuring that each test case starts from the same clean state without needing to restart the local node.
###
[hashtag](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#reset-fork)
**Reset fork**
This command resets the local Hardhat node to a fresh state forked from the blockchain. You can use it to switch to a different block number or even a different RPC endpoint without restarting your entire testing process.
* * *
[hashtag](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#alternative-anvil-foundry)
Alternative: Anvil (Foundry)
-------------------------------------------------------------------------------------------------------------------------------------------------------------
Fork Testnet
Fork Mainnet
Notes
Point your app/tests to `http://127.0.0.1:8546`.
Anvil JSON-RPC is Hardhat-compatible for most calls (`evm_*`). Replace `hardhat_*` with Anvil equivalents where needed.
* * *
[hashtag](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#tips-and-best-practices)
Tips & Best Practices
----------------------------------------------------------------------------------------------------------------------------------------------------
* Always pin block numbers in forks for reproducibility.
* Prefer `localhost` network when you need state persistence across multiple test files.
* Keep Anvil/Hardhat on separate ports if you run both simultaneously.
* Use labels/comments in tests to describe assumptions tied to a specific fork block.
* Avoid draining RPC rate limits: cache fixtures, use snapshots, and fork testnet for most flows.
* * *
[hashtag](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#common-issues)
Common Issues
----------------------------------------------------------------------------------------------------------------------------------
chevron-rightI**nsufficient funds for gas**[hashtag](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#insufficient-funds-for-gas)
Top up native balance with \`hardhat\_setBalance\`.
chevron-rightN**once too high / replacement underpriced**[hashtag](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#nonce-too-high-replacement-underpriced)
Reset account state (new snapshot) or use a fresh signer.
chevron-right**Contract already deployed at address**[hashtag](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#contract-already-deployed-at-address)
On persistent localhost, restart node or change deployer nonce.
[PreviousDevelopment Frameworkschevron-left](https://docs.somnia.network/developer/development-frameworks)
[NextDeploy with RemixIDEchevron-right](https://docs.somnia.network/developer/development-frameworks/deploy-with-remixide)
Last updated 1 month ago
* [Prerequisites](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#prerequisites)
* [Quick Start (Hardhat)](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#quick-start-hardhat)
* [Testing Your DApp Locally](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#testing-your-dapp-locally)
* [Write a Test File](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#write-a-test-file)
* [Run Your Tests](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#run-your-tests)
* [Forking Somnia (Testnet vs Mainnet)](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#forking-somnia-testnet-vs-mainnet)
* [Handy RPC Tricks (Hardhat)](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#handy-rpc-tricks-hardhat)
* [Impersonate an account](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#impersonate-an-account)
* [Time travel](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#time-travel)
* [Snapshot and Revert](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#snapshot-and-revert)
* [Reset fork](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#reset-fork)
* [Alternative: Anvil (Foundry)](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#alternative-anvil-foundry)
* [Tips & Best Practices](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#tips-and-best-practices)
* [Common Issues](https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking#common-issues)
contracts/Counter.sol
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract Counter {
uint256 private count;
event CountedTo(uint256 number);
function getCount() public view returns (uint256) {
return count;
}
function increment() public {
count += 1;
emit CountedTo(count);
}
}
test/Counter.test.ts
Copy
import { expect } from "chai";
import { ethers } from "hardhat";
describe("Counter Contract", function () {
it("Should increment the count by 1", async function () {
const Counter = await ethers.getContractFactory("Counter");
const counter = await Counter.deploy();
await counter.waitForDeployment();
expect(await counter.getCount()).to.equal(0);
const tx = await counter.increment();
await tx.wait();
expect(await counter.getCount()).to.equal(1);
});
it("Should emit a CountedTo event", async function () {
const Counter = await ethers.getContractFactory("Counter");
const counter = await Counter.deploy();
await counter.waitForDeployment();
await expect(counter.increment()).to.emit(counter, "CountedTo").withArgs(1);
});
});
Copy
# Run tests on the default in-process Hardhat network
npx hardhat test
# Or, start a local node in a separate terminal
npx hardhat node
# Then run tests against it
npx hardhat test --network localhost
Copy
hardhat: {
forking: {
url: process.env.SOMNIA_RPC_TESTNET!,
blockNumber: process.env.FORK_BLOCK_TESTNET ? Number(process.env.FORK_BLOCK_TESTNET) : undefined,
},
}
Copy
# in-process fork
npx hardhat test
# persistent node
npx hardhat node --fork $SOMNIA_RPC_TESTNET ${FORK_BLOCK_TESTNET:+--fork-block-number $FORK_BLOCK_TESTNET}
Copy
hardhat: {
forking: {
url: process.env.SOMNIA_RPC_MAINNET!,
blockNumber: process.env.FORK_BLOCK_MAINNET ? Number(process.env.FORK_BLOCK_MAINNET) : undefined,
},
}
Copy
# in-process fork
npx hardhat test
# persistent node
npx hardhat node --fork $SOMNIA_RPC_MAINNET ${FORK_BLOCK_MAINNET:+--fork-block-number $FORK_BLOCK_MAINNET}
impersonate.ts
Copy
import { ethers, network } from "hardhat";
async function main() {
const target = "0xYourSomniaAddress"; // account to impersonate
await network.provider.request({ method: "hardhat_impersonateAccount", params: [target] });
const signer = await ethers.getSigner(target);
await network.provider.send("hardhat_setBalance", [\
target,\
"0x152d02c7e14af6800000" // 1000 ether in wei\
]);
console.log("Impersonating:", await signer.getAddress());
}
main().catch((e) => { console.error(e); process.exit(1); });
Copy
await network.provider.send("evm_setNextBlockTimestamp", [Math.floor(Date.now()/1000) + 3600]);
await network.provider.send("evm_mine");
Copy
const id = await network.provider.send("evm_snapshot");
// ... run actions ...
await network.provider.send("evm_revert", [id]);
Copy
await network.provider.request({
method: "hardhat_reset",
params: [{ forking: { url: process.env.SOMNIA_RPC_TESTNET!, blockNumber: Number(process.env.FORK_BLOCK_TESTNET||0) || undefined } }]
});
Copy
anvil --fork-url $SOMNIA_RPC_TESTNET ${FORK_BLOCK_TESTNET:+--fork-block-number $FORK_BLOCK_TESTNET} --port 8546
Copy
anvil --fork-url $SOMNIA_RPC_MAINNET ${FORK_BLOCK_MAINNET:+--fork-block-number $FORK_BLOCK_MAINNET} --port 8546
---
# Subgraphs | Somnia Docs
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/subgraphs#protofire)
[Protofirearrow-up-right](http://protofire.io/)
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Protofire deploys custom, compatible oracles using the same data providers and node operators, allowing protocols to connect to their network without modifying Smart Contracts.
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/subgraphs#resources)
Resources
* [Create Subgraphsarrow-up-right](http://somnia.chain.love/)
* [Documentation](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/protofire-subgraph)
* * *
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/subgraphs#ormi)
[Ormiarrow-up-right](https://ormilabs.com/)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
Ormi is the only unified Web3 data layer to supercharge live, historical, and AI-powered blockchain data applications
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/subgraphs#resources-1)
Resources
* [Create Subgraphsarrow-up-right](https://subgraph.somnia.network/)
* [Documentation](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/ormi-subgraph)
[PreviousOracleschevron-left](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/oracles)
[NextWallet Providerschevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/wallet-providers)
Last updated 6 months ago
* [Protofire](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/subgraphs#protofire)
* [Resources](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/subgraphs#resources)
* [Ormi](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/subgraphs#ormi)
* [Resources](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-tools/subgraphs#resources-1)
---
# Create ERC721 NFT Collections | Somnia Docs
ERC721 is the EVM compatible standard for Non Fungible Tokens, NFTs. NFTS are digital assets where each token is unique. Unlike ERC20 (fungible) tokens that are interchangeable, ERC721 tokens represent distinct items such as game assets, collectibles, tickets, certificates, or onchain identities.
Somnia, being EVM-compatible, supports the ERC721 standard natively. ERC721 has the following functionalities:
* Every token has a distinct `tokenId` and (optionally) distinct metadata, making it unique.
* Wallets can own, transfer, and approve NFTs using a common interface.
* NFTs are composable; therefore, Wallets, marketplaces, and dApps understand ERC-721 uniformly, enabling easy listing, trading, and display.
* ERC721 has a clean base that can be extended with metadata, enumeration, royalties (ERC-2981), permit (EIP-4494), etc.
This guide will teach you how to connect to and deploy your ERC20 Smart Contract to the Somia Network using Hardhat.
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#pre-requisite)
Pre-requisite
----------------------------------------------------------------------------------------------------------------------------------------------
* This guide is not an introduction to Solidity Programming; you are expected to have a basic understanding of Solidity Programming.
* To complete this guide, you will need MetaMask installed and the Somnia Network added to the list of Networks. If you have yet to install MetaMask, please follow this guide to [Connect Your Wallet](https://docs.somnia.network/get-started/connect-your-wallet-to-mainnet)
.
The Smart Contract is minimal, production-friendly ERC-721 without royalties (no ERC-2981). Per-token tokenURI set during mint (works great with IPFS). It also demonstrates how to deploy, mint, and verify on Somnia networks.
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#erc721-smart-contract)
ERC721 Smart Contract
--------------------------------------------------------------------------------------------------------------------------------------------------------------
chevron-right`NFTTest.sol`[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#nfttest.sol)
Copy
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.4.0
pragma solidity ^0.8.27;
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {ERC721URIStorage} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
contract NFTTest is ERC721, ERC721URIStorage, Ownable {
uint256 private _nextTokenId;
constructor(address initialOwner)
ERC721("NFTTest", "NFTT")
Ownable(initialOwner)
{}
function _baseURI() internal pure override returns (string memory) {
return "https://ipfs.io";
}
function safeMint(address to, string memory uri)
public
onlyOwner
returns (uint256)
{
uint256 tokenId = _nextTokenId++;
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
return tokenId;
}
function tokenURI(uint256 tokenId)
public
view
override(ERC721, ERC721URIStorage)
returns (string memory)
{
return super.tokenURI(tokenId);
}
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721, ERC721URIStorage)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
}
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#code-breakdown)
Code Breakdown
------------------------------------------------------------------------------------------------------------------------------------------------
Below is a breakdown explanation of the code:
###
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#imports)
Imports
`ERC721` is the Core NFT standard that has all the methods that control `ownership`, `transfers`, `approvals`, and `metadata` hook.
`ERC721URIStorage` adds per-token URI storage via `_setTokenURI` and overrides `tokenURI`. ERC721URIStorage extends ERC721 to store URIs per token (costs storage gas, but very flexible).
`Ownable` is a simple access control library that enables the Smart Contract owner to set one account as the “owner”.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#contract-declaration)
Contract Declaration
`_nextTokenId` is an Internal counter for new token IDs. Starts at `0` by default (since not set), so your first mint is `tokenId = 0`.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#constructor)
Constructor
Calls `ERC721` constructor with collection name "NFTTest" and symbol "NFTT". Initializes Ownable with `initialOwner` the address allowed to mint tokens. In this instance, the body is empty because all setup is done via parent constructors.
This ERC-721 contract follows a straightforward ownership and metadata model designed for simplicity and marketplace compatibility. The contract owner is the sole minter, and each mint assigns the next sequential identifier—beginning at 0—to the specified recipient. This ensures that token IDs remain predictable (0, 1, 2, …) and facilitates aligning your metadata files with minted tokens.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#base-uri)
Base URI
Returns a prefix used by `ERC721.tokenURI` when a token’s stored URI is relative (e.g., "/ipfs//1.json").
In this contract you set full URIs at mint (commonly ipfs://...), which are returned as-is by `ERC721URIStorage`. So this Base URI only matters if you mint with relative paths. An example Base URI when using IPFS is then `https://ipfs.io/`
###
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#minting)
Minting
In this contract, minting is restricted by `onlyOwner`, ensuring that only the contract owner can create new tokens. Token IDs are assigned sequentially using `_nextTokenId++`, starting from 0 and incrementing by one with each mint. The `_safeMint` function adds an extra layer of safety by checking if the recipient is a contract and, if so, requiring it to implement `IERC721Receiver` to prevent tokens from being locked. For metadata, `_setTokenURI` stores the exact URI string for each token, such as `ipfs:///1.json`, making it easy to reference unique files. The function also emits and returns the new `tokenId` upon minting. However, it’s worth noting that storing a full string per token consumes more gas; for larger drops with sequential files, a cheaper alternative is to use a base URI combined with the token ID rather than storing individual URIs.
###
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#required-overrides)
Required overrides
Because both ERC721 and ERC721URIStorage define tokenURI, Solidity requires you to pick which implementation to use. `super.tokenURI(tokenId)` resolves to ERC721URIStorage’s version, which:
* Returns the stored URI if present.
* Otherwise falls back to ERC721’s baseURI and tokenId behavior.
Also resolves multiple inheritance for supportsInterface (ERC165) and ensures the contract correctly reports ERC721 and metadata support.
Transfers and approvals behave exactly as the ERC721 standard specifies. Use `safeTransferFrom` by default, so the contract checks that recipients can handle NFTs, which prevents tokens from being sent to incompatible contracts. `transferFrom` is available when you are certain the recipient can accept NFTs without the receiver check, and approvals can be granted either per token with approve or globally with `setApprovalForAll`. Interface support is advertised through supportsInterface, allowing wallets, marketplaces, and indexers to recognize ERC721 core and metadata compatibility automatically.
A few best practices will keep deployments robust. Favor the `ipfs://` scheme for `metadata` and media to avoid locking yourself to a single HTTP gateway. If you expect very large drops, remember that `ERC721URIStorage` stores a full string per token, which is convenient but more expensive at scale; collections with sequential filenames can reduce costs by adopting a `baseURI` and `tokenId` pattern instead of per token URI storage.
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#initialize-hardhat)
Initialize Hardhat
--------------------------------------------------------------------------------------------------------------------------------------------------------
Create `.env`:
chevron-right`hardhat.config.ts`[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#hardhat.config.ts)
Add the `Smart Contract` to the Contract directory
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#deploy-with-ignition)
Deploy with Ignition
------------------------------------------------------------------------------------------------------------------------------------------------------------
Create `ignition/modules/NFTTest.ts`:
Deploy:
Copy the **deployed address** from the output.
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#verify-smart-contract)
Verify Smart Contract
--------------------------------------------------------------------------------------------------------------------------------------------------------------
> Ensure compiler version, optimizer, and constructor args match.
* * *
###
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#mint-your-collection)
Mint your collection
We’ll mint by calling `safeMint(to, uri)` **10 times**, matching your uploaded metadata files.
chevron-right`scripts/mint.ts`[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#scripts-mint.ts)
Run the project:
* * *
[hashtag](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#inspect-token-metadata)
Inspect Token metadata
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Read a token’s URI (e.g., tokenId `0`) and open it in your browser.
`scripts/read-uri.ts`:
Open the printed URL (it should be `https://ipfs.io/ipfs//0.json`) in your browser to confirm JSON and `image` render correctly.
Congratulations. 🎉 You have deployed your first ERC721 Smart Contract to the Somnia Network. 🎉
[PreviousCreate ERC20 Tokenschevron-left](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc20-tokens)
[NextManaging NFT Metadata with IPFSchevron-right](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/managing-nft-metadata-with-ipfs)
Last updated 5 months ago
* [Pre-requisite](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#pre-requisite)
* [ERC721 Smart Contract](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#erc721-smart-contract)
* [Code Breakdown](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#code-breakdown)
* [Imports](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#imports)
* [Contract Declaration](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#contract-declaration)
* [Constructor](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#constructor)
* [Base URI](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#base-uri)
* [Minting](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#minting)
* [Required overrides](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#required-overrides)
* [Initialize Hardhat](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#initialize-hardhat)
* [Deploy with Ignition](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#deploy-with-ignition)
* [Verify Smart Contract](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#verify-smart-contract)
* [Mint your collection](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#mint-your-collection)
* [Inspect Token metadata](https://docs.somnia.network/developer/building-dapps/tokens-and-nfts/create-erc721-nft-collections#inspect-token-metadata)
Copy
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {ERC721URIStorage} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
Copy
contract NFTTest is ERC721, ERC721URIStorage, Ownable {
uint256 private _nextTokenId;
...
}
Copy
constructor(address initialOwner)
ERC721("NFTTest", "NFTT")
Ownable(initialOwner)
{}
Copy
function _baseURI() internal pure override returns (string memory) {
return "https://ipfs.io";
}
Copy
function safeMint(address to, string memory uri)
public
onlyOwner
returns (uint256)
{
uint256 tokenId = _nextTokenId++;
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
return tokenId;
}
Copy
function tokenURI(uint256 tokenId)
public
view
override(ERC721, ERC721URIStorage)
returns (string memory)
{
return super.tokenURI(tokenId);
}
Copy
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721, ERC721URIStorage)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
Copy
mkdir somnia-nft && cd somnia-nft
npm init -y
npm install --save-dev hardhat typescript ts-node @types/node
npx hardhat
npm install @openzeppelin/contracts
npm install --save-dev @nomicfoundation/hardhat-verify @nomicfoundation/hardhat-ignition @nomicfoundation/hardhat-ignition-ethers
Copy
PRIVATE_KEY=0xYourPrivateKey
SOMNIA_RPC_HTTPS=https://dream-rpc.somnia.network
Copy
import "dotenv/config";
import "@nomicfoundation/hardhat-verify";
import "@nomicfoundation/hardhat-ignition-ethers";
import { HardhatUserConfig } from "hardhat/config";
const config: HardhatUserConfig = {
solidity: "0.8.28",
networks: {
somnia: {
url: process.env.SOMNIA_RPC_HTTPS!,
accounts: [process.env.PRIVATE_KEY!],
},
},
sourcify: { enabled: false },
etherscan: {
apiKey: { somnia: process.env.SOMNIA_EXPLORER_API_KEY || "" },
customChains: [\
{\
network: "somnia",\
chainId: 50312,\
urls: {\
apiURL: "https://shannon-explorer.somnia.network/api",\
browserURL: "https://shannon-explorer.somnia.network",\
},\
},\
],
},
};
export default config;
Copy
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
const NFTTestModule = buildModule("NFTTestModule", (m) => {
const initialOwner = m.getParameter("initialOwner", "0xYourOwnerAddress");
const nft = m.contract("NFTTest", [initialOwner]);
return { nft };
});
export default NFTTestModule;
Copy
npx hardhat ignition deploy ignition/modules/NFTTest.ts --network somnia
Copy
npx hardhat verify --network somnia 0xYourOwnerAddress
Copy
import { ethers } from "hardhat";
async function main() {
const contractAddr = "";
const nft = await ethers.getContractAt("NFTTest", contractAddr);
const owner = (await ethers.getSigners())[0];
// Example: 10 tokens at /ipfs//{0..9}.json
const CID = "";
for (let i = 0; i < 10; i++) {
const uri = `/ipfs/${CID}/${i}.json`;
const tx = await nft.safeMint(owner.address, uri);
console.log(`Mint tx ${i}:`, tx.hash);
await tx.wait();
}
const lastId = await nft.callStatic.safeMint(owner.address, `/ipfs/${CID}/999.json`).catch(()=>null);
console.log("Minted 10 tokens. Next simulated ID (no state change):", lastId ?? "N/A");
}
main().catch((e) => {
console.error(e);
process.exit(1);
});
Copy
npx hardhat run scripts/mint.ts --network somnia
Copy
import { ethers } from "hardhat";
async function main() {
const contractAddr = "";
const nft = await ethers.getContractAt("NFTTest", contractAddr);
const uri = await nft.tokenURI(0);
console.log("tokenURI(0):", uri);
}
main().catch(console.error);
Copy
npx hardhat run scripts/read-uri.ts --network somnia
---
# Somnia Exchange | Somnia Docs
Somnia Exchange is a gamified decentralized exchange (DEX) on Somnia Network. Trade tokens, provide liquidity, and earn rewards through gaming features.
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#how-to-trade-tokens)
How to Trade Tokens
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#token-swapping)
Token Swapping
1
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#connect-wallet)
Connect Wallet
Visit [somnia.exchangearrow-up-right](https://somnia.exchange/)
and connect your Web3 wallet.
2
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#select-tokens)
Select Tokens
Choose "From" and "To" tokens for your swap.
3
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#enter-amount)
Enter Amount
Input the amount you want to trade.
4
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#review-trade)
Review Trade
Check exchange rate and slippage tolerance.
5
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#execute-swap)
Execute Swap
Confirm transaction in your wallet.
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#available-trading-pairs)
Available Trading Pairs
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
* **SOMI/USDC.e**: Main trading pair with highest liquidity
* **USDC.e/NIA**: Secondary major pair
* **SOMI/NIA**: Alternative SOMI trading option
* **Various Meme Tokens**: CHICK, JELLU, BOB, and others
_Available trading pairs list_

[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#how-to-add-liquidity-pools)
How to Add Liquidity Pools
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#step-by-step-liquidity-provision)
Step-by-Step Liquidity Provision
1
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#access-pools)
Access Pools
Go to [somnia.exchange/poolsarrow-up-right](https://somnia.exchange/pools)
2
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#choose-pool)
Choose Pool
Select existing pool or create new pair.\\
3
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#add-liquidity)
Add Liquidity
Click "Add Liquidity" on desired pool.
4
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#deposit-tokens)
Deposit Tokens
Provide equal value of both tokens in the pair.
5
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#confirm-transaction)
Confirm Transaction
Approve token spending and confirm liquidity addition.
6
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#receive-lp-tokens)
Receive LP Tokens
Get LP tokens representing your pool share.
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#liquidity-rewards)
Liquidity Rewards
----------------------------------------------------------------------------------------------------------------------------------------------------------------
* **Trading Fees**: Earn 0.3% of all trades in your pool 4
* **Liquidity Points**: Participate in points program for additional rewards 1
* **Pool Multipliers**: Different pools have varying reward multipliers based on risk 1
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#portfolio-management)
Portfolio Management
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#track-your-holdings)
Track Your Holdings
1
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#connect-wallet-1)
Connect Wallet
Link your wallet to view portfolio.
2
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#view-balances)
View Balances
See all token holdings and values.
3
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#transaction-history)
Transaction History
Review all trading and liquidity activities.
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#play-game-features)
Play Game Features
------------------------------------------------------------------------------------------------------------------------------------------------------------------
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#gamified-trading-experience)
Gamified Trading Experience
Access the gaming features at [game.somnia.exchangearrow-up-right](https://game.somnia.exchange/)
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#energy-stations)
Energy Stations
1
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#stake-lp-tokens)
Stake LP Tokens
Deposit your LP tokens in Energy Stations 2
2
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#generate-energy)
Generate Energy
Earn energy points from staked positions.
3
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#unlock-rewards)
Unlock Rewards
Use energy to access special features and bonuses.
4
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#multiple-pools)
Multiple Pools
Choose from 2 different energy station pools 2
_Image placeholder: Gaming features and energy stations_
###
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#gaming-benefits)
Gaming Benefits
* **Enhanced Rewards**: Earn additional tokens through gaming mechanics
* **NFT Rewards**: Collect NFTs through trading activities 3
* **Feed & Energy System**: Power up your trading with gaming elements 3
* **Competitive Elements**: Participate in trading contests and leaderboards
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#how-to-start-gaming)
How to Start Gaming
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
1
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#connect-wallet-2)
Connect Wallet
Link your wallet to the gaming platform.
2
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#provide-liquidity)
Provide Liquidity
Add liquidity to earn LP tokens.
3
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#stake-in-energy-stations)
Stake in Energy Stations
Deposit LP tokens to generate energy.
4
####
[hashtag](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#participate-in-activities)
Participate in Activities
Use energy for gaming features and rewards.
* * *
Start trading at [somnia.exchangearrow-up-right](https://somnia.exchange/)
| Gaming at [game.somnia.exchangearrow-up-right](https://game.somnia.exchange/)
[PreviousSomnia Domains (.somi)chevron-left](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-domains-.somi)
[NextTokoschevron-right](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/tokos)
Last updated 4 months ago
* [How to Trade Tokens](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#how-to-trade-tokens)
* [Token Swapping](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#token-swapping)
* [Available Trading Pairs](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#available-trading-pairs)
* [How to Add Liquidity Pools](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#how-to-add-liquidity-pools)
* [Liquidity Rewards](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#liquidity-rewards)
* [Portfolio Management](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#portfolio-management)
* [Play Game Features](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#play-game-features)
* [Gaming Benefits](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#gaming-benefits)
* [How to Start Gaming](https://docs.somnia.network/developer/deployment-and-production/ecosystem/ecosystem-showcase/somnia-exchange#how-to-start-gaming)
---
# Using Data APIs (Ormi) | Somnia Docs
The Somnia mission is to enable the building of mass-consumer real-time applications. As a Developer, you need to understand how to interact with onchain data to build UIs. This guide will teach you how to build a Token Balance dApp that fetches and displays ERC20 token balances from the Somnia Network using Next.js and the Ormi Data APIs.
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#prerequisites)
Prerequisites
------------------------------------------------------------------------------------------------------------------------------------------------
To complete this guide, you will need:
* Basic understanding of React and TypeScript
* An Ormi API key. Get one at [arrow-up-right](https://ormi.xyz/)
[https://subgraph.somnia.network/dashboard/apiarrow-up-right](https://subgraph.somnia.network/dashboard/api)
.
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#what-is-ormi-data-api)
What is Ormi Data API?
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
[Ormiarrow-up-right](https://docs.ormilabs.com/dedicated-env/somnia/data-apis/overview)
provides a unified crypto data infrastructure for live and historical blockchain data. The Data APIs allow developers to query blockchain data without running their own nodes, making it easy to build data-rich applications on the Somnia Network.
####
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#api-base-url)
API Base URL
The Ormi Data API for Somnia Network uses the following base URL:
####
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#api-endpoints)
API Endpoints
The API follows a RESTful structure. For fetching ERC20 token balances, the endpoint structure is:
Where:
* `somnia` - The network identifier
* `v1` - API version
* `{walletAddress}` - The wallet address you want to query
* `balance/erc20` - Specifies that you want ERC-20 token balances
####
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#authentication)
Authentication
The Ormi API requires authentication using a Bearer token. Every request must include an Authorization header:
**Important**: Never expose your API key in client-side code. Always make API calls from a server-side route to keep your key secure.
####
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#example-api-request)
Example API Request
Here's an example of how to make a direct API call using curl:
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#set-up-the-project)
Set up the Project
----------------------------------------------------------------------------------------------------------------------------------------------------------
Create a new Next.js application with TypeScript and Tailwind CSS:
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#create-the-type-definitions)
Create the Type Definitions
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
First, we need to define TypeScript interfaces for the API response. Update `app/page.tsx`:
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#build-the-user-interface)
Build the User Interface
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
Now, let's create the main component with an input field and button. Update your `app/page.tsx`:
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#create-a-.env-file)
Create a `.env` file
------------------------------------------------------------------------------------------------------------------------------------------------------------
The `.env` is for keeping secrets such as the Ormi API KEY. Add the API KEY:
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#create-the-api-route)
Create the API Route
--------------------------------------------------------------------------------------------------------------------------------------------------------------
To avoid CORS issues and keep your API key secure, we'll create an API route. Create a directory and a new file `app/api/balance/route.ts`:
Important: Replace `YOUR_API_KEY_HERE` with your actual Ormi API key.
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#implement-the-fetch-function)
Implement the Fetch Function
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Add the fetch function to handle form submission. Update your `app/page.tsx`:
Don't forget to add the onSubmit handler to your form:
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#display-the-results)
Display the Results
------------------------------------------------------------------------------------------------------------------------------------------------------------
Add error handling and a table to display the token balances. Add this code after your form in `app/page.tsx`:
###
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#test-your-dapp)
Test Your dApp
Start the development server:
Open [http://localhost:3000arrow-up-right](http://localhost:3000/)
in your browser. Enter a wallet address that has tokens on Somnia Network.
Example test address: `0xC4890Bc98273424a18626772F266C35bf57FA56A`
Look at the browser for the response and the displayed token balances. You can click on any contract address to view it in the Shannon Explorer.
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#complete-code)
Complete Code
------------------------------------------------------------------------------------------------------------------------------------------------
chevron-rightpage.tsx[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#page.tsx)
chevron-rightroute.ts[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#route.ts)
[hashtag](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#congratulations)
Congratulations
----------------------------------------------------------------------------------------------------------------------------------------------------
You have built your first API enabled dApp on the Somnia Network!
Now that you have a working Token Balance dApp, you can extend it by using other Ormi API [endpointsarrow-up-right](https://subgraphs.somnia.network/)
.
[PreviousBuilding Subgraph UIs (Apollo Client)chevron-left](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/building-subgraph-uis-apollo-client)
[NextListening to Blockchain Events (WebSocket)chevron-right](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/listening-to-blockchain-events-websocket)
Last updated 5 months ago
* [Prerequisites](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#prerequisites)
* [What is Ormi Data API?](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#what-is-ormi-data-api)
* [Set up the Project](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#set-up-the-project)
* [Create the Type Definitions](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#create-the-type-definitions)
* [Build the User Interface](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#build-the-user-interface)
* [Create a .env file](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#create-a-.env-file)
* [Create the API Route](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#create-the-api-route)
* [Implement the Fetch Function](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#implement-the-fetch-function)
* [Display the Results](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#display-the-results)
* [Test Your dApp](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#test-your-dapp)
* [Complete Code](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#complete-code)
* [Congratulations](https://docs.somnia.network/developer/building-dapps/data-indexing-and-querying/using-data-apis-ormi#congratulations)
Copy
https://api.subgraph.somnia.network/public_api/data_api
Copy
/somnia/v1/address/{walletAddress}/balance/erc20
Copy
Authorization: Bearer YOUR_API_KEY
Copy
curl -X GET "https://api.subgraph.somnia.network/public_api/data_api/somnia/v1/address/0xYOUR_WALLET_ADDRESS/balance/erc20" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json"
Copy
npx create-next-app@latest somnia-balance-demo --typescript --tailwind --app
cd somnia-balance-demo
Copy
'use client'
import { useState, FormEvent } from 'react'
// Type definitions for the API response
interface TokenBalance {
balance: string
contract: {
address: string
decimals: number
erc_type: string
logoUri: string | null
name: string
symbol: string
}
raw_balance: string
}
interface BalanceResponse {
erc20TokenBalances: TokenBalance[]
resultCount: number
}
Copy
export default function Home() {
const [walletAddress, setWalletAddress] = useState('')
const [loading, setLoading] = useState(false)
const [data, setData] = useState(null)
const [error, setError] = useState('')
return (