Somnia 백서

Somnia

원문

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

install thirdweb ethers viem dotenv ``` This installs: `thirdweb` → The Thirdweb React SDK. `ethers` → To interact with blockchain transactions. `dotenv` → To securely store API keys. ## Create the Thirdweb Client The Thirdweb client allows the app to communicate with the blockchain. Create a **`client.ts`** file and add: ``` import { createThirdwebClient } from "thirdweb"; export const client = createThirdwebClient({ clientId: process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID as string, // Replace with your actual Client ID }); ``` {% hint style="info" %} Get your Client ID: Register at thirdweb.com/dashboard. 💡 {% endhint %} ## Add Environment Variables Store API keys in a .env.local file: ``` NEXT_PUBLIC_THIRDWEB_CLIENT_ID=your-client-id-here NEXT_PUBLIC_SOMNIA_RPC_URL=https://dream-rpc.somnia.network/ ``` Restart Next.js after modifying .env.local ## Build the Account Abstraction App To ensure Thirdweb Components are available throughout the app, wrap the children's components inside ThirdwebProvider. Modify **`layout.ts`**: ```typescript import { ThirdwebProvider } from 'thirdweb/react'; <body> <ThirdwebProvider> {children} </ThirdwebProvider> </body> ``` ## 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. ```typescript import { useActiveAccount } from "thirdweb/react"; const smartAccount = useActiveAccount(); <ConnectButton client={client} appMetadata={{ name: "Example App", url: "https://example.com", }} /> ``` 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. <div align="left"><figure><img src="https://2122549367-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkYErT9t3BJtpPfejLO6I%2Fuploads%2FbxG90nEcMeuXf9kutKQr%2Fconnect-button.png?alt=media&amp;token=09e7cc83-a09d-443d-85f6-a2b616189cb8" alt=""><figcaption><p>click Connect</p></figcaption></figure></div> <div align="right"><figure><img src="https://2122549367-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkYErT9t3BJtpPfejLO6I%2Fuploads%2FVCF2GxRUflnhi1WGcd9T%2Fconnected-button.png?alt=media&amp;token=b53519f9-a194-466e-8041-d7f4187e2518" alt=""><figcaption><p>Connected Wallet</p></figcaption></figure></div> To show the connected address, we add the following UI component: ```typescript {smartAccount ? ( <div className="mt-6 p-4 bg-white rounded-lg shadow"> <p className="text-lg font-semibold text-gray-700"> Connected as: {smartAccount.address} </p> {message && <p className="mt-2 text-green-600">{message}</p>} </div> ) : ( <p className="text-lg text-red-600 text-center"> Please connect your wallet. </p> )} ``` ## 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. ``` import { useSendTransaction } from "thirdweb/react"; import { ethers } from "ethers"; const { mutate: sendTransaction, isPending } = useSendTransaction(); const sendTokens = async () => { if (!smartAccount) { setMessage("No smart account connected."); return; } console.log("Sending 0.01 STT from:", smartAccount.address); sendTransaction( { to: "0xb6e4fa6ff2873480590c68D9Aa991e5BB14Dbf03", value: ethers.parseUnits("0.01", 18), chain: somniaTestnet, client, }, { onSuccess: (receipt) => { console.log("Transaction Success:", receipt); setMessage(`Sent 0.01 STT! TX: ${receipt.transactionHash}`); }, onError: (error) => { console.error("Transaction Failed:", error); setMessage("Transaction failed! Check console."); }, } ); }; ``` ## 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. ```typescript const [message, setMessage] = useState<string>(''); <button onClick={sendTokens} disabled={isPending} className={`mt-4 px-6 py-2 rounded-lg ${ isPending ? 'bg-gray-400 cursor-not-allowed' : 'bg-blue-600 hover:bg-blue-700 text-white' }`} > {isPending ? 'Sending...' : 'Send 0.01 STT'} </button> {message && <p className='mt-2 text-green-600'>{message}</p>} ``` <details> <summary>Complete Code</summary> ```typescript 'use client'; import { useState } from 'react'; import { ConnectButton, useActiveAccount, useSendTransaction, } from 'thirdweb/react'; import { ethers } from 'ethers'; import { client } from './client'; import { somniaTestnet } from 'viem/chain'; export default function Home() { const [message, setMessage] = useState<string>(''); const smartAccount = useActiveAccount(); // Get connected account const { mutate: sendTransaction, isPending } = useSendTransaction(); const sendTokens = async () => { if (!smartAccount) { setMessage('No smart account connected.'); return; } console.log('🚀 Sending 0.01 STT from:', smartAccount.address); sendTransaction( { to: '0xb6e4fa6ff2873480590c68D9Aa991e5BB14Dbf03', // Replace value: ethers.parseUnits('0.01', 18), chain: somniaTestnet, client, }, { onSuccess: (receipt) => { console.log('Transaction Success:', receipt); setMessage(`Sent 0.01 STT! TX: ${receipt.transactionHash}`); }, onError: (error) => { console.error('Transaction Failed:', error); setMessage('Transaction failed! Check console.'); }, } ); }; return ( <main className='p-4 pb-10 min-h-[100vh] flex items-center justify-center container max-w-screen-lg mx-auto'> <div className='py-20'> <div className='flex justify-center mb-10'> <ConnectButton client={client} appMetadata={{ name: 'Example App', url: 'https://example.com', }} /> </div> {smartAccount ? ( <div className='mt-6 p-4 bg-white rounded-lg shadow'> <p className='text-lg font-semibold text-gray-700'> Connected as: {smartAccount.address} </p> <button onClick={sendTokens} disabled={isPending} className={`mt-4 px-6 py-2 rounded-lg ${ isPending ? 'bg-gray-400 cursor-not-allowed' : 'bg-blue-600 hover:bg-blue-700 text-white' }`} > {isPending ? 'Sending...' : 'Send 0.01 STT'} </button> {message && <p className='mt-2 text-green-600'>{message}</p>} </div> ) : ( <p className='text-lg text-white-600 text-center'> Please connect your wallet. </p> )} </div> </main> ); } ``` </details> The full implementation includes wallet connection, balance retrieval, and token transfer. <figure><img src="https://2122549367-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkYErT9t3BJtpPfejLO6I%2Fuploads%2FwKc8sH2NricEjaR4sN5z%2FWallet.png?alt=media&amp;token=e00eb509-954e-45ab-90ea-48583cde0a63" alt=""><figcaption><p>Smart Contract Wallet</p></figcaption></figure> ## 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. <br> # Data Indexing and Querying 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**](/developer/building-dapps/data-indexing-and-querying/ormi-subgraph) * [How to build with **Protofire’s subgraph infrastructure**](/developer/building-dapps/data-indexing-and-querying/protofire-subgraph) * [Frontend integrations for building responsive UIs from your subgraph data](/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. # Ormi Subgraph 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 [Ormi](https://ormilabs.com), a powerful gateway that simplifies subgraph deployment through a hosted Graph Node and IPFS infrastructure. {% embed url="<https://www.youtube.com/watch?v=yYKbvJAEX80>" %} ## 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. ## Install Graph CLI globally ```bash npm install -g @graphprotocol/graph-cli ``` ## 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: ```bash graph init --contract-name MyToken --from-contract 0xYourTokenAddress --network somnia-testnet mytoken ``` **`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 ## Define the Subgraph Schema <a href="#define-the-subgraph-schema" id="define-the-subgraph-schema"></a> For the example`MyToken` Contract, which is an ERC20 Token, Edit `schema.graphql` to index all the Transfer events emitted from the Smart Contract. ```typescript type Transfer @entity(immutable: true) { id: Bytes! from: Bytes! to: Bytes! value: BigInt! blockNumber: BigInt! blockTimestamp: BigInt! transactionHash: Bytes! } ``` ## Build the Subgraph After customizing your schema and mapping logic, build the subgraph by running the command: ``` graph codegen && graph build ``` This will generate the necessary `artifacts` for deployment. ## Deploy Using Ormi Open the Somnia Ormi website <https://subgraph.somnia.network/> and create an account. <figure><img src="https://2122549367-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkYErT9t3BJtpPfejLO6I%2Fuploads%2FAm7Rs07ZpEQcTVVmaI4q%2FOrmi-Login.png?alt=media&amp;token=7e677985-72ea-446a-8697-8dc96ac2371c" alt=""><figcaption></figcaption></figure> On the left navigation menu, click the "key" icon to access your `privateKey.` <figure><img src="https://2122549367-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkYErT9t3BJtpPfejLO6I%2Fuploads%2FOxEJLWk2vP58mYRizSZh%2FOrmi-pKey.png?alt=media&amp;token=2cf1a5d2-fbe7-4c0a-b27c-89d8950c909d" alt=""><figcaption></figcaption></figure> Deploy your subgraph to Ormi’s hosted infrastructure with the following command: ```bash graph deploy mytoken --node https://api.subgraph.somnia.network/deploy --ipfs https://api.subgraph.somnia.network/ipfs --deploy-key yourORMIPrivateKey ``` 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. <figure><img src="https://2122549367-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkYErT9t3BJtpPfejLO6I%2Fuploads%2FIaqgFZvz9xebeCboNoPL%2FAds%C4%B1z%20tasar%C4%B1m%20(24).png?alt=media&amp;token=3823da8f-a314-4d17-b836-41d410597ec1" alt=""><figcaption></figcaption></figure> Open the deployed subgraph in the explorer to interact with it: <figure><img src="https://2122549367-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkYErT9t3BJtpPfejLO6I%2Fuploads%2FMzzQXDCRdUcvrpiLQkW4%2Formi-explorer.png?alt=media&amp;token=8b9f0dcf-7424-49c1-a3d1-510d6a89e324" alt=""><figcaption></figcaption></figure> ## 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.<br> For more information, visit the Ormi [docs](https://docs.ormilabs.com/dedicated-env/somnia/subgraphs/overview). # Protofire Subgraph The blockchain is an ever-growing database of transactions and Smart Contract events. Developers use subgraphs, an indexing solution provided by the Graph protocol, to retrieve and analyze this data efficiently. A graph in this context represents the structure of blockchain data, including token transfers, contract events, and user interactions. A subgraph is a customized indexing service that listens to blockchain transactions and structures them in a way that can be easily queried using GraphQL. {% embed url="<https://www.youtube.com/watch?v=Dbk7KHSyt_I>" %} ## Prerequisites * This guide is not an introduction to Solidity Programming; you are expected to understand Basic Solidity Programming. * GraphQL is installed and set up on your local machine. ```bash npm install -g @graphprotocol/graph-cli ``` ## Deploy a Simple ERC20 Token on Somnia We will deploy a basic ERC20 token on the Somnia network using Hardhat. Ensure you have Hardhat, OpenZeppelin, and dotenv installed: ```bash npm install --save-dev hardhat @nomicfoundation/hardhat-ignition-ethers @openzeppelin/contracts dotenv ethers ``` ## Create an ERC20 Token Contract Create a new Solidity file: `contracts/MyToken.sol` and update it ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.25; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract MyToken is ERC20 { constructor(uint256 initialSupply) ERC20("MyToken", "MTK") { _mint(msg.sender, initialSupply * 10**decimals()); } function mint(address to, uint256 amount) external { _mint(to, amount); } function burn(uint256 amount) external { _burn(msg.sender, amount); } } ``` ## Create a Deployment Script Create a new file in `ignition/modules/MyTokenModule.ts` ```typescript import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; export default buildModule("MyTokenModule", (m) => { const initialSupply = m.getParameter("initialSupply", 1000000n * 10n ** 18n); const myToken = m.contract("MyToken", [initialSupply]); return { myToken }; }); ``` ## Deploy the Smart 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 Faucet.<br> ```javascript module.exports = { // ... networks: { somniaTestnet: { url: "https://dream-rpc.somnia.network", accounts: ["0xPRIVATE_KEY"], // put dev menomonic or PK here, }, }, // ... }; ``` Open a new terminal and deploy the smart contract to the Somnia Network. Run the command: ```bash npx hardhat ignition deploy ./ignition/modules/MyTokenModule.ts --network somniaTestnet ``` This will deploy the ERC20 contract to the Somnia network and return the deployed contract address. ## Simulate On-Chain Activity Once deployed, we will create a script to generate multiple transactions on the blockchain. Create a new file `scripts/interact.js` <details> <summary><code>interact.js</code></summary> ```javascript require("dotenv").config(); const { ethers } = require("hardhat"); async function main() { // Connect to Somnia RPC const provider = new ethers.JsonRpcProvider(process.env.SOMNIA_RPC_URL); // Load wallets from .env const deployer = new ethers.Wallet(process.env.PRIVATE_KEY_1, provider); const user1 = new ethers.Wallet(process.env.PRIVATE_KEY_2, provider); const user2 = new ethers.Wallet(process.env.PRIVATE_KEY_3, provider); const user3 = new ethers.Wallet(process.env.PRIVATE_KEY_4, provider); const user4 = new ethers.Wallet(process.env.PRIVATE_KEY_5, provider); const contractAddress = "0xBF9516ADc5263d277E2505d4e141F7159B103d33"; // Replace with your deployed contract address const abi = [ "function transfer(address to, uint256 amount) external returns (bool)", "function mint(address to, uint256 amount) external", "function burn(uint256 amount) external", ]; // Attach to the deployed ERC20 contract const token = new ethers.Contract(contractAddress, abi, provider); console.log("🏁 Starting Token Transactions Simulation on Somnia..."); // Simulate Transfers const transfers = [ { from: deployer, to: user1.address, amount: "1000" }, { from: deployer, to: user2.address, amount: "1000" }, { from: user1, to: user2.address, amount: "50" }, { from: user2, to: user3.address, amount: "30" }, { from: user3, to: user4.address, amount: "10" }, { from: user4, to: deployer.address, amount: "5" }, { from: deployer, to: user2.address, amount: "100" }, { from: user1, to: user3.address, amount: "70" }, { from: user2, to: user4.address, amount: "40" }, ]; for (const tx of transfers) { const { from, to, amount } = tx; const txResponse = await token.connect(from).transfer(to, ethers.parseUnits(amount, 18)); await txResponse.wait(); console.log(`✅ ${from.address} sent ${amount} MTK to ${to}`); } // Simulate Minting const mintAmount1 = ethers.parseUnits("500", 18); const mintTx1 = await token.connect(deployer).mint(user1.address, mintAmount1); await mintTx1.wait(); console.log(`✅ Minted ${ethers.formatUnits(mintAmount1, 18)} MTK to User1!`); const mintAmount2 = ethers.parseUnits("300", 18); const mintTx2 = await token.connect(deployer).mint(user2.address, mintAmount2); await mintTx2.wait(); console.log(`✅ Minted ${ethers.formatUnits(mintAmount2, 18)} MTK to User2!`); // Simulate Burning const burnAmount1 = ethers.parseUnits("50", 18); const burnTx1 = await token.connect(user1).burn(burnAmount1); await burnTx1.wait(); console.log(`🔥 User1 burned ${ethers.formatUnits(burnAmount1, 18)} MTK!`); const burnAmount2 = ethers.parseUnits("100", 18); const burnTx2 = await token.connect(user2).burn(burnAmount2); await burnTx2.wait(); console.log(`🔥 User2 burned ${ethers.formatUnits(burnAmount2, 18)} MTK!`); console.log("🏁 Simulation Complete on Somnia!"); } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); }); ``` </details> Create an `.env` file to hold sensitive informations such as the private keys ```properties SOMNIA_RPC_URL=https://dream-rpc.somnia.network PRIVATE_KEY_1=0x... PRIVATE_KEY_2=0x... PRIVATE_KEY_3=0x... PRIVATE_KEY_4=0x... PRIVATE_KEY_5=0x... ``` #### Run the Script ```bash node scripts/interact.js ``` This will generate several on-chain transactions for our subgraph to index. ## Deploy a Subgraph on Somnia Go to <https://somnia.chain.love/> and connect your Wallet. <figure><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXcOA43Zg4tecvciwgPQxb-dlcBwhPkTGY0POkIQ8JaUdKcaheg-AwetWTR1yFdYSGw_A2c_fm-xuQba6mVDitI_rD7wuazgEsjBjUjxT8-0ELLl9CW0JFlzNuPLHlZYXKy7VP16_A?key=ZDOrGANolV8eE-n5bJ-_qrWy" alt=""><figcaption></figcaption></figure> First, you need to create a private key for deploying subgraphs. To do so, please go to [Somnia Protofire Service](https://somnia.chain.love) and create an Account. You are now able to create
프로젝트로 돌아가기