Somnia 백서

Somnia

원문

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

ess(this).balance); } ``` <details> <summary>Example.sol</summary> ```solidity // 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); } } ``` </details> ## Frontend tip ```typescript await walletClient.sendTransaction({ to: '0xTipJarAddress', value: parseEther('0.05'), }); ``` ## Sponsor SOMI Transactions with Account Abstraction Using a smart account + relayer (e.g. via Privy, Thirdweb), the dApp can cover gas fees: ```typescript await sendTransaction({ to: contractAddress, data: mintFunctionEncoded, value: 0n, // user sends no SOMI }); ``` The Smart Contract function can execute mint or other logic as usual. The paymaster or relayer pays SOMI. ## 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. <br> # Wallet Integration and Auth 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](/developer/building-dapps/wallet-integration-and-auth/authenticating-with-privy) * [Use **RainbowKit** to offer a beautiful, customizable wallet connection UI](/developer/building-dapps/wallet-integration-and-auth/authenticating-with-rainbowkit) * [Leverage **ConnectKit** for flexible and extensible wallet integrations](/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. # Authenticating with MetaMask 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.\ \ [MetaMask](https://docs.metamask.io) is a wallet library that developers can use to build login functionality for applications on the Somnia Network. {% hint style="success" %} Somnia Mainnet is LIVE. To deploy on Somnia Mainnet, you will need SOMI Tokens. Please refer to the [guide](/get-started/getting-started-for-mainnet) on Moving from Testnet to Mainnet. {% endhint %} 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. ## Start a NextJS Project Run the command below to start a NextJS project: ```bash npx create-next-app metamask-example ``` Select Typescript, TailWind CSS, and Page Router in the build options. <br> Change the directory into the project folder. Delete the code inside of the `<main>` tags and replace them with the following: ```html <p>Hello, World!</p> ``` ## Install Viem ```bash npm i viem ``` [Viem](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. ## Import Methods The next step is to set up the React State methods and the ViemJS methods that we will require: ```typescript import { useState } from "react"; import { createPublicClient, http, createWalletClient, } from "viem"; ``` 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`](https://dream-rpc.somnia.network). In the future developers can use RPC providers to avoid rate limiting. ## Import Somnia Import Somnia Testnet ```typescript import { somniaTestnet } from "viem/chains"; ``` ## 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: ```typescript const [address, setAddress] = useState<string>(""); const [connected, setConnected] = useState(false); ``` Now that the States are declared, we can declare a function to handle the MetaMask authentication process on Somnia Network. ## Connect MetaMask Function Add the function below inside the export statement: ```typescript 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!" ); } }; ``` ## Update the UI MetaMask connection is set up, and the final step is to test the connection via the User Interface. Update the `<p>Hello, World!</p>` in the return statement to the following: ```typescript {!connected ? ( <button onClick={connectToMetaMask} className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" > Connect Wallet </button> ) : ( <div> <p>Connected as: {address}</p> </div> )} ``` Open your terminal and run the following command to start the app: ```bash npm run dev ``` 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](/developer/development-frameworks/using-the-viem-library).\ \ Congratulations, you have successfully connected from MetaMask to Somnia Network. 🎉 <br> # Authenticating with ConnectKit In this guide, we'll integrate [ConnectKit](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. ## 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](/get-started/connect-your-wallet-to-mainnet). 3. Familiarity with React and Next.js is assumed. ## Create the Next.js Project Open your terminal and run the following commands to set up a new Next.js project: ```bash 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: ```bash npm install wagmi viem @tanstack/react-query connectkit ``` ## 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: ```typescript '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 ( <WagmiConfig config={config}> <QueryClientProvider client={queryClient}> <ConnectKitProvider>{children}</ConnectKitProvider> </QueryClientProvider> </WagmiConfig> ); } ``` In the app directory, locate the **`layout.tsx`** file and update it as follows: ```typescript import ClientProvider from './components/ClientProvider'; export default function RootLayout({ children }) { return ( <html lang="en"> <head> <title>Somnia DApp</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> </head> <body> <ClientProvider>{children}</ClientProvider> </body> </html> ); } ``` ## 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: ```typescript 'use client'; import { useAccount } from 'wagmi'; import { ConnectKitButton } from 'connectkit'; export default function Home() { const { address, isConnected } = useAccount(); return ( <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]"> <main className="flex flex-col gap-8 row-start-2 items-center sm:items-start"> Hello, world! {/* Connect Button */} <div className="mt-4"> <ConnectKitButton /> </div> {/* Show Wallet Address */} {isConnected && ( <p className="mt-4 text-lg text-blue-600">Connected as: {address}</p> )} </main> </div> ); } ``` To run the application, start the Development Server by running the following command: ```bash npm run dev ``` 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. ## 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 Discord](https://discord.gg/somnia). <br> # Authenticating with Privy [Privy](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 [here](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. <figure><img src="https://2122549367-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkYErT9t3BJtpPfejLO6I%2Fuploads%2Fg9NXZ2uShPKKpmPnqFGs%2FSomnia-ProviderApp.png?alt=media&amp;token=eecad0bb-b8f7-4eb0-a221-e64ed795c3a0" alt=""><figcaption></figcaption></figure> ## Prerequisites * This guide is not an introduction to JavaScript Programming; you are expected to understand JavaScript. * To complete this guide, sign up for [Privy](https://dashboard.privy.io/) and get an AppID and get the Somnia Provider AppID. * Familiarity with React and Next.js is assumed. ## Installation ### Create the Next.js Project <a href="#create-the-next.js-project" id="create-the-next.js-project"></a> Open your terminal and run the following commands to set up a new Next.js project: ```bash npx create-next-app@latest somnia-privy cd somnia-privy ``` Install the necessary packages ```bash npm install @privy-io/react-auth viem ``` ## Set Up PrivyProvider Go to <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. <figure><img src="https://2122549367-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkYErT9t3BJtpPfejLO6I%2Fuploads%2FqyA5RwjgOn7hgjHDMWgb%2FPrivy-Dashboard.png?alt=media&amp;token=3a209129-c947-4df7-b666-e6c6a55d92ec" alt=""><figcaption></figcaption></figure> Open the newly created app and in the left side navigation menu navigate to: **`User Management >>>> Global Wallet >>>> Integrations`**<br> Click the toggle to turn ON the Somnia Provider App. <figure><img src="https://2122549367-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkYErT9t3BJtpPfejLO6I%2Fuploads%2FBa3BbCNQlCJOuANLaHWl%2FEkran%20Resmi%202026-05-15%2001.18.07.png?alt=media&amp;token=45741c49-b83a-4a9a-b3e0-b4ff97b0e04e" alt=""><figcaption></figcaption></figure> Wrap your application **`layout.ts`** file with PrivyProvider and supply your PrivateKey from Privy and the Somnia Provider App ID to the **`loginMethods`**: ```typescript 'use client'; import { PrivyProvider } from '@privy-io/react-auth'; import { somniaTestnet } from 'viem/chains'; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( <html lang='en'> <body className={`${geistSans.variable} ${geistMono.variable} antialiased`} > <PrivyProvider appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID!} config={{ loginMethods: { primary: ['email', 'google', 'privy:cm8d9yzp2013kkr612h8ymoq8'], }, defaultChain: somniaTestnet, supportedChains: [somniaTestnet], embeddedWallets: { createOnLogin: 'users-without-wallets', }, }} > {children} </PrivyProvider> </body> </html> ); } ``` Add your environment variable in .env.local: ```bash NEXT_PUBLIC_PRIVY_APP_ID=your-privy-app-id ``` ## Privy Hooks These hooks make it easy to authenticate users, manage wallets, and interact with the Somnia Network using Privy Global Wallet ```typescript import { useCrossAppAccounts, usePrivy } from '@privy-io/react-auth'; ``` ## Authenticate Use the provided hooks to authenticate users and access their wallets. <details> <summary>page.tsx</summary> <pre class="language-typescript"><code class="lang-typescript">export default function Home() { const { loginWithCrossAppAccount } = useCrossAppAccounts(); <strong> const { ready, authenticated, user, logout } = usePrivy(); </strong> const disableLogin = !ready || (ready &#x26;&#x26; authenticated); const [loginError, setLoginError] = useState&#x3C;string | null>(null); const [walletAddress, setWalletAddress] = useState&#x3C;string | null>(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 ? ( &#x3C;p>Loading...&#x3C;/p> ) : authenticated ? ( {walletAddress ? ( &#x3C;p>Connected as: {walletAddress}&#x3C;/p> ) : ( &#x3C;p className='text-gray-600'>No wallet address found.&#x3C;/p> )} &#x3C;button onClick={logout} className='bg-red-600 text-white px-4 py-2 rounded' > Logout &#x3C;/button> &#x3C;/div> ) : ( &#x3C;> &#x3C;button onClick={startCrossAppLogin} className='bg-purple-600 text-white px-4 py-2 rounded' > Login with Global Wallet &#x3C;/button> {loginError &#x26;&#x26; &#x3C;p className='text-red-500 text-sm'>{loginError}&#x3C;/p>} &#x3C;/> &#x3C;/div> )} } </code></pre> </details> ## Send Transactions Once authenticated, use the **`useSendTransaction`** hook from `useCrossAppAccount` method to interact with Somnia Testnet: ```typescript const { sendTransaction } = useCrossAppAccounts(); ...... const sendSTT = async () => { if (!walletAddress) return; const txn = { to: '0xb6e4fa6ff2873480590c68D9Aa991e5BB14Dbf03', value: 1000000000000000, chainId: 50312, }; try { const tx = await sendTransaction(txn, { address: walletAddress }); console.log('TX Sent:', tx); } catch (err) { console.error('TXN Failed:', err); } }; ...... <button onClick={sendSTT}> Send 0.001 STT</button> ``` ## Complete Code <details> <summary>Complete <strong><code>page.tsx</code></strong> code</summary> ```typescript 'use client'; import { usePrivy, useCrossAppAccounts, } from '@privy-io/react-auth'; import { useEffect, useState } from 'react'; import { createPublicClient, http, formatEther } from 'viem'; import { somniaTestnet } from 'viem/chains'; export default function Home() { const { ready, authenticated, user, logout } = usePrivy(); const { loginWithCrossAppAccount, sendTransaction } = useCrossAppAccounts(); const [loginError, setLoginError] = useState<string | null>(null); const [hydrated, setHydrated] = useState(false); const [walletAddress, setWalletAddress] = useState<string | null>(null); const [balance, setBalance] = useState<string>(''); const providerAppId = 'cm8d9yzp2013kkr612h8ymoq8'; const client = createPublicClient({ chain: somniaTestnet, transport: http(), }); const startCrossAppLogin = async () => { try { setLoginError(null); const result = await loginWithCrossAppAccount({ appId: providerAppId, }); 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.'); } }; useEffect(() => { if (authenticated) { const globalWallet = user?.linkedAccounts?.find( (account) => account.type === 'cross_app' && account.providerApp?.id === providerAppId ); console.log(globalWallet); const wallet = globalWallet?.smartWallets?.[0]; console.log(wallet); if (wallet?.address) { setWalletAddress(wallet.address); setHydrated(true); fetchBalance(wallet.address); } else if (user?.wallet?.address) { setWalletAddress(user.wallet.address); setHydrated(true); fetchBalance(user.wallet.address); } else { setHydrated(true); } } }, [authenticated, user]); const fetchBalance = async (address: string) => { try { const result = await client.getBalance({ address: address as `0x${string}`, }); const formatted = parseFl
프로젝트로 돌아가기