Skip to Content
On this page

This guide shows the workflows you will use after mounting WalletConnectProvider.

Use openModal() for the standard connection flow. The examples below run in client components under WalletConnectProvider. Handle rejected wallet actions, including user cancellation, in your app UI.

Connect With the Modal#

"use client" import { useWalletConnect } from "@dogeos/dogeos-sdk" export function WalletButton() { const { isConnected, isConnecting, error, openModal, disconnect } = useWalletConnect() return ( <div> <button disabled={isConnecting} onClick={isConnected ? disconnect : openModal}> {isConnecting ? "Connecting..." : isConnected ? "Disconnect" : "Connect wallet"} </button> {error ? <p role="alert">{error}</p> : null} </div> ) }

openModal() opens the account view when a wallet is already connected, and opens the wallet home view otherwise.

Show Connection Status#

import { useWalletConnect } from "@dogeos/dogeos-sdk" export function WalletStatusBadge() { const { connectionStatus, walletStatus, isWalletReady } = useWalletConnect() return ( <span> {connectionStatus} / {isWalletReady ? "wallet ready" : walletStatus} </span> ) }

connectionStatus is "disconnected", "connecting", or "connected". walletStatus describes the embedded wallet lifecycle: "disabled", "initializing", "ready", "unavailable", or "error".

Inside MyDoge, a connected native wallet does not require isWalletReady. See Native-First Connection. Display the current account and chain rather than treating a cached address as a live connection.

Display the Connected Account#

import { useAccount } from "@dogeos/dogeos-sdk" export function ConnectedAccount() { const { address, chainType, chainId, currentWallet } = useAccount() if (!address) { return <p>Connect a wallet to continue.</p> } return ( <div> <p> {address.slice(0, 6)}...{address.slice(-4)} </p> <p> {currentWallet?.info?.name ?? "Wallet"} on {chainType}:{chainId} </p> </div> ) }

Use currentProvider to read the connected wallet’s balance.

Switch to DogeOS Chikyū Testnet#

Use switchChain() from useAccount(). Pass the same Chain shape you used in provider configuration.

import { ChainTypeEnum, useAccount, type Chain } from "@dogeos/dogeos-sdk" const dogeOSChikyuTestnet = { id: 6281971, name: "DogeOS Chikyū Testnet", nativeCurrency: { name: "DOGE", symbol: "DOGE", decimals: 18 }, rpcUrls: { default: { http: ["https://rpc.testnet.dogeos.com/"] } }, blockExplorers: { default: { name: "DogeOS L2Scan", url: "https://dogeos-testnet.l2scan.co", }, }, testnet: true, } satisfies Chain export function SwitchNetworkButton() { const { switchChain } = useAccount() async function onSwitch() { await switchChain({ chainType: ChainTypeEnum.EVM, chainInfo: dogeOSChikyuTestnet, }) } return <button onClick={onSwitch}>Switch to DogeOS Chikyū</button> }

If the wallet does not know the target EVM chain, the SDK asks the wallet adapter to add it.

Sign a Message#

import { useAccount } from "@dogeos/dogeos-sdk" export function SignMessageButton() { const { address, signMessage } = useAccount() async function onSign() { if (!address || !signMessage) return const signature = await signMessage({ message: "Welcome to DogeOS", }) console.log(signature) } return ( <button disabled={!address || !signMessage} onClick={onSign}> Sign message </button> ) }

The SDK requires a non-empty message. For EVM, signMessage({ message }) signs that message unchanged. The optional separate nonce argument is not part of the EVM signed message and does not add replay protection. For authentication, include the server-issued nonce in the message itself as shown below. The example above demonstrates signing only.

Sign In With Wallet#

signInWithWallet() builds and signs a challenge using the active provider. In SDK 4.0.4, EVM accounts use a standard Sign-In with Ethereum (SIWE) message bound to the connected account and selected chain, including DogeOS Testnet where supported. SIWE does not require Ethereum mainnet. Both signing methods return a signature, not an authenticated application session.

For production authentication, have your server issue and retain a fresh, expiring, single-use challenge bound to the account, chain, domain and URI. When it returns the complete message, sign its exact bytes with signMessage({ message }); do not parse and rebuild it with signInWithWallet() or append another nonce.

import type { useAccount } from "@dogeos/dogeos-sdk" type SignMessage = NonNullable<ReturnType<typeof useAccount>["signMessage"]> // The caller validates the server challenge and current account/chain first. // This helper signs only; it does not verify a login or create a session. export async function signExactChallenge(signMessage: SignMessage, message: string) { const signature = await signMessage({ message }) return { message, signature } }

Submit the returned proof to your server and await verification of the signature, stored message, nonce, expiry, domain/URI, account and chain before marking the user signed in. Consume the nonce so the proof cannot be reused. Recheck the account, provider and chain after asynchronous steps and discard stale challenges. If the account changes during verification, reconcile any resulting session through your app’s logout/account-switch flow before showing an authenticated state.

If your server instead supplies SignInParams, pass those fields to signInWithWallet() and verify the exact SDK-generated message on the server. Omitting the parameters generates a demonstration challenge with a local random nonce; it does not implement server authentication.

Sign on your intended supported EVM chain, including DogeOS Testnet. Ethereum mainnet is not required. The SDK demo repository  demonstrates the distinction between signing and a server-confirmed session.

Call the Current Provider#

currentProvider is the provider for the active chain type. For EVM wallets, call request() with an EIP-1193 request. The embedded wallet supports public RPC reads on the selected chain, including balances, transaction counts, and gas estimates.

Read an EVM Balance#

import { useAccount } from "@dogeos/dogeos-sdk" export function ReadEvmBalanceButton() { const { address, chainType, currentProvider } = useAccount() async function onReadBalance() { if (!address || !currentProvider || chainType !== "evm") return const balanceHex = await currentProvider.request({ method: "eth_getBalance", params: [address, "latest"], }) console.log(BigInt(balanceHex)) } return ( <button disabled={!address || !currentProvider || chainType !== "evm"} onClick={onReadBalance}> Read EVM balance </button> ) }

eth_getBalance returns a hexadecimal quantity in the chain’s smallest unit. On Chikyū, divide by 10^18 to display DOGE, using a bigint-aware formatter to preserve precision. Query again after the account or chain changes.

Other common EVM reads use the same request() method:

MethodParametersResult
eth_getTransactionCount[address, "pending"]Transaction count as a hexadecimal quantity
eth_estimateGas[{ from: address, to: recipient, value: "0x0" }]Estimated gas units as a hexadecimal quantity
eth_blockNumber[]Latest block number as a hexadecimal quantity

These reads do not sign or broadcast a transaction. Handle request failures in your app and offer a retry.

Read a Dogecoin Balance#

For a connected Dogecoin embedded wallet, call getBalance():

import { useAccount } from "@dogeos/dogeos-sdk" export function ReadDogecoinBalanceButton() { const { address, chainType, currentProvider } = useAccount() const canReadBalance = Boolean(address) && chainType === "dogecoin" && currentProvider != null && typeof currentProvider.getBalance === "function" async function onReadBalance() { if (!canReadBalance || !currentProvider) return const balance = await currentProvider.getBalance() console.log(balance) } return ( <button disabled={!canReadBalance} onClick={onReadBalance}> Read Dogecoin balance </button> ) }

The result contains confirmed, unconfirmed, and total amounts in Dogecoin’s smallest unit (10^8 units per DOGE). Use the provider for the active Dogecoin connection. External wallets can expose different method names; check the wallet’s API when adding one to your app.

Call the Solana Provider#

For Solana wallets, the SDK preserves the connected Solana provider on currentProvider. Injected wallets often expose publicKey, while WalletConnect Solana providers expose account methods such as solana_getAccounts or solana_requestAccounts.

import { useAccount } from "@dogeos/dogeos-sdk" export function SolanaAccount() { const { chainType, currentProvider } = useAccount() async function onReadAccount() { if (!currentProvider || chainType !== "solana") return if ("publicKey" in currentProvider && currentProvider.publicKey) { console.log(String(currentProvider.publicKey)) return } if ("request" in currentProvider && typeof currentProvider.request === "function") { const accounts = await currentProvider.request({ method: "solana_getAccounts" }) console.log(accounts) } } return <button onClick={onReadAccount}>Read Solana account</button> }

Access Current Wallet Connectors#

useConnectors() returns connector providers for the current wallet and the active provider. This is useful when you need to inspect whether the connected wallet exposes EVM, Dogecoin, Solana, or Aptos provider entries.

import { useConnectors } from "@dogeos/dogeos-sdk" export function ProviderFamilies() { const { connectors, currentProvider } = useConnectors() if (!connectors) { return <p>No wallet connected.</p> } return ( <ul> <li>EVM: {connectors.evm ? "available" : "unavailable"}</li> <li>Dogecoin: {connectors.dogecoin ? "available" : "unavailable"}</li> <li>Current provider: {currentProvider ? "ready" : "missing"}</li> </ul> ) }

Programmatic Connect#

useWalletConnect().connect() is available for advanced custom flows, but it takes a runtime WalletConnector that includes live providers:

import { ChainTypeEnum } from "@dogeos/dogeos-sdk" await connect({ wallet, chainType: ChainTypeEnum.EVM, })

Do not pass an entry returned by getConnectors() to connect(). getConnectors() returns WalletConfig[] for provider configuration; connect() requires the initialized wallet object used by the SDK modal and adapter layer.

If you are not already managing those runtime connectors, call openModal() instead.