Developer documentation

Integrate Nora Wallet
without provider collisions.

Discover Nora Wallet with EIP-6963, connect accounts, react to provider events, switch EVM networks, request signatures, and submit transactions.

Overview

A standards-first EVM provider

Nora Wallet exposes an EIP-1193-compatible provider and announces it through EIP-6963. Your dApp should discover the provider explicitly instead of assuming that window.ethereum belongs to a particular wallet.

User approval remains authoritative.

Connecting, signing, sending transactions, and changing networks may open a Nora Wallet approval window. Unlocking a locked wallet only resumes the review; it does not approve the request.

EIP-6963 discovery
EIP-1193 events
Origin-scoped permissions
EOA and ERC-6551 TBA connections
01

Discover Nora Wallet

Use EIP-6963 as the primary path. It lets multiple installed wallets coexist without overwriting one another. Dispatch the request event after registering your listener so wallets can announce themselves immediately.

TypeScript
type EIP1193Provider = {
  isTerraWallet?: boolean
  request(args: { method: string; params?: unknown[] }): Promise<unknown>
  on(event: string, listener: (...args: unknown[]) => void): void
  removeListener(event: string, listener: (...args: unknown[]) => void): void
}

type EIP6963Detail = {
  info: { uuid: string; name: string; icon: string; rdns: string }
  provider: EIP1193Provider
}

export function findTerraWallet(timeoutMs = 1_000) {
  return new Promise<EIP1193Provider>((resolve, reject) => {
    const timeout = window.setTimeout(() => {
      window.removeEventListener("eip6963:announceProvider", onProvider)
      reject(new Error("Nora Wallet was not found"))
    }, timeoutMs)

    function onProvider(event: Event) {
      const { info, provider } = (event as CustomEvent<EIP6963Detail>).detail
      if (provider.isTerraWallet || info.name === "Nora Wallet") {
        window.clearTimeout(timeout)
        window.removeEventListener("eip6963:announceProvider", onProvider)
        resolve(provider)
      }
    }

    window.addEventListener("eip6963:announceProvider", onProvider)
    window.dispatchEvent(new Event("eip6963:requestProvider"))
  })
}

Optional compatibility fallback

Nora Wallet also exposes window.terraEthereum. Use it only as a fallback for environments where your connector does not yet support EIP-6963.

TypeScript
declare global {
  interface Window {
    terraEthereum?: EIP1193Provider
  }
}

const provider = await findTerraWallet().catch(() => window.terraEthereum)

if (!provider) {
  throw new Error("Install Nora Wallet to continue")
}
Do not identify a provider from window.ethereum alone. Another installed wallet may own that namespace.
02

Connect an account

Call eth_requestAccounts only after a clear user action such as clicking “Connect Nora Wallet.” Nora Wallet records permission per website origin and may let the user choose an EOA or a deployed Token Bound Account.

TypeScript
const accounts = await provider.request({
  method: "eth_requestAccounts",
}) as string[]

const account = accounts[0]
const chainId = await provider.request({ method: "eth_chainId" }) as string

// Use eth_accounts for a silent permission check on later visits.
const permittedAccounts = await provider.request({
  method: "eth_accounts",
}) as string[]

Before rendering a previously connected session, use eth_accounts. An empty array means the current origin does not have an exposed account.

03

Keep application state synchronized

Subscribe to account and network changes. Never keep using an old address after accountsChanged, and treat an empty account list as a disconnected permission state.

TypeScript
const handleAccounts = (accounts: unknown) => {
  const next = Array.isArray(accounts) ? accounts[0] : undefined
  // Clear account-scoped state when next is undefined.
}

const handleChain = (chainId: unknown) => {
  // chainId is a hexadecimal EVM quantity, for example "0x38".
  window.location.reload()
}

provider.on("accountsChanged", handleAccounts)
provider.on("chainChanged", handleChain)
provider.on("disconnect", (error) => console.warn("Wallet disconnected", error))

// Remove listeners when your application unmounts.
provider.removeListener("accountsChanged", handleAccounts)
provider.removeListener("chainChanged", handleChain)
04

Switch or propose an EVM network

Chain IDs must be hexadecimal quantities. Nora Wallet shows the requested network details before applying a sensitive network action. Provide HTTPS RPC and explorer URLs suitable for production.

TypeScript
await provider.request({
  method: "wallet_switchEthereumChain",
  params: [{ chainId: "0x38" }], // BNB Smart Chain
})

// If your application offers an add-network flow, send complete metadata.
await provider.request({
  method: "wallet_addEthereumChain",
  params: [{
    chainId: "0x89",
    chainName: "Polygon Mainnet",
    nativeCurrency: { name: "POL", symbol: "POL", decimals: 18 },
    rpcUrls: ["https://polygon-rpc.com"],
    blockExplorerUrls: ["https://polygonscan.com"],
  }],
})
05

Request a signature

Explain what the signature does before opening the wallet. Include a domain, purpose, and one-time nonce in authentication messages, then verify the recovered signer on your server.

TypeScript
const message = "Sign in to Example dApp
Nonce: 8f23c1"
const signature = await provider.request({
  method: "personal_sign",
  params: [message, account],
}) as string

const typedSignature = await provider.request({
  method: "eth_signTypedData_v4",
  params: [account, JSON.stringify(typedData)],
}) as string
ERC-6551 Token Bound Accounts in Nora Wallet do not support direct personal_sign or typed-data signing. Connect the controlling EOA when a message signature is required.
06

Submit a transaction

Nora Wallet prepares the transaction on the active network and displays a dedicated approval screen. Contract interactions may carry additional warnings. Do not treat a returned hash as final confirmation; follow its receipt.

TypeScript
const transactionHash = await provider.request({
  method: "eth_sendTransaction",
  params: [{
    from: account,
    to: "0x000000000000000000000000000000000000dEaD",
    value: "0x0",
    data: "0x",
  }],
}) as string

A connected Token Bound Account can execute supported transactions through its verified NFT controller. Its account must be deployed, owned by the current controller, and connected on the matching chain.

07

Handle provider errors

Wrap every request in try/catch and branch on the numeric code. Rejection is an expected user decision, not an application crash.

CodeMeaningRecommended response
4001User rejected the requestReturn the user to a safe, retryable state.
4100UnauthorizedRequest account access or ask the user to unlock Nora Wallet.
4200Unsupported methodUse a supported EIP-1193 method or your own RPC client.
4900Provider disconnectedDisable write actions and wait for connectivity.
4901Chain disconnectedAsk the user to select or add a reachable network.
08

Integration security checklist

  • Discover wallets through EIP-6963 and let the user choose explicitly.
  • Request accounts only in response to intentional user interaction.
  • Verify the active account and chain immediately before every write.
  • Never request or transmit a recovery phrase, private key, or wallet password.
  • Display transaction purpose, destination, value, and network in your own UI.
  • Treat RPC data, token metadata, dApp input, and provider icons as untrusted.
  • Handle rejection, lock, timeout, disconnect, and chain changes without retry loops.
Build with clarity

Ready to connect?

Start with provider discovery, add a user-triggered connect button, and test locked, rejected, disconnected, and multi-wallet states before release.

Review discovery

Standards references: EIP-1193 and EIP-6963 .