Developers

Keythings Wallet Integration Guide

Everything you need to connect a dApp to the Keythings Wallet extension: capability-based authorization, RPC workflows, transaction publishing, and production hardening tips sourced directly from the keythings-extension-wallet repository.

Environment Checklist

The wallet ships as a Chrome Manifest V3 extension. For integration testing you only need the published build and a localhost dApp:

  • Install Keythings Wallet from the Chrome Web Store and pin the extension.
  • Enable developer options inside the extension and make sure window.keeta appears on allowed origins.
  • Serve your dApp on http://localhost:3000; restart the dev server after each wallet upgrade since the allowlist only trusts that origin.

Detect the Injected Provider

src/inpage/inpage-provider.ts injects window.keeta into approved origins. Always feature-detect and guide users when the wallet is missing or locked.

import type { KeetaProvider } from 'keythings-extension-wallet/types'

export function getKeetaProvider(): KeetaProvider {
  const provider = window.keeta
  if (!provider?.isKeeta) {
    throw new Error('Keythings Wallet provider not detected. Ask the user to install or enable the extension.')
  }
  return provider
}

The provider emits accountsChanged, chainChanged, and disconnect. Attach listeners during app bootstrap to keep UI state synchronized with wallet activity.

Simplified Capability System

Keythings Wallet uses a simplified two-tier capability system for dApp connections. The wallet automatically requests the appropriate permissions based on your dApp's needs.

Default Connection: Read + Transact

By default, when a dApp connects to Keythings Wallet, it receives both read and transact capabilities:

  • Read Access: View wallet data, account balances, transaction history, and network information
  • Transact Access: Send transactions, sign operations, and interact with smart contracts

The connection approval dialog will show: "Allow localhost:3000 to send transactions and view your wallet data"

Read-Only Connection

For dApps that only need to view wallet information (like portfolio trackers or analytics tools), you can request read-only access:

const provider = getKeetaProvider()

// Request read-only access (no transaction capabilities)
const granted = await provider.requestCapabilities(['read'])
console.table(granted)

// Default connection (read + transact) - no need to specify
const defaultGranted = await provider.requestCapabilities(['read', 'transact'])

The connection approval dialog will show: "Allow localhost:3000 to view your wallet data"

Capability Management

Capabilities expire and need to be refreshed. The wallet handles this automatically, but you can also refresh manually:

// Refresh capabilities before they expire
await provider.refreshCapabilities(['read', 'transact'])

// Check if capabilities are still valid
const isConnected = await provider.isConnected()
if (!isConnected) {
  // Re-request capabilities
  await provider.requestCapabilities(['read', 'transact'])
}

Account Connection Workflow

  1. Call requestAccounts() to prompt the user.
  2. Persist the approved account and active network in application state.
  3. Subscribe to provider events and react immediately to account, network, or lock changes.
const provider = getKeetaProvider()

const accounts = await provider.requestAccounts()
if (accounts.length === 0) {
  throw new Error('User rejected the connection request or no accounts are available.')
}

provider.on?.('accountsChanged', (next) => {
  console.info('Accounts changed', next)
})

provider.on?.('chainChanged', (chainId) => {
  console.info('Network switched', chainId)
})

provider.on?.('disconnect', () => {
  console.warn('Wallet disconnected — clear session state and prompt to reconnect.')
})

If you only need cached accounts, call getAccounts() once you have confirmed that the read capability token is still valid.

Wallet RPC Reference

All RPC requests flow through wallet-provider-handler.ts. The table below mirrors the supported methods. Useprovider.request({ method, params }) for lower-level access.

These JSON-RPC methods are the same wallet provider APIs documented in full detail in the API Reference. Use this table as a quick lookup while you are following the integration flow.

MethodSummary
keeta_requestAccountsPrompts the user to approve the dApp and returns authorized addresses.
keeta_getAccountsReturns cached accounts without opening a prompt (requires prior approval).
keeta_requestCapabilitiesRequests capabilities: `['read']` for read-only, `['read', 'transact']` for full access (default).
keeta_refreshCapabilitiesRenews expiring capability tokens without prompting the user again.
keeta_getNetworkReturns the active Keeta network (name + chainId).
keeta_switchNetworkRequests a network switch. User confirmation is required.
keeta_getBalanceReads the balance of a Keeta address (requires `read` capability).
keeta_getAllBalancesReturns all tracked token balances for the active account (requires `read` capability).
keeta_sendTransactionPublishes a transaction built by the dApp (requires `transact` capability).
keeta_signMessageSigns an arbitrary message (requires `transact` capability).
keeta_getKtaPriceReturns the latest cached KTA → USD price snapshot.

Additional helpers such as keeta_getTokenMetadata, keeta_getAccountState, and keeta_getCapabilityTokensare exposed through provider.request and obey the same capability checks.

History: For transaction history and ledger queries, use the Keeta JS SDK directly (for example UserClient.history or Client.history) or an indexer service. The wallet no longer exposes a history RPC.

Transactions & Builders

For multi-operation flows (storage accounts, token mints, permission changes), request a user client. The wallet instantiates the Keeta SDK inside the service worker and exposes hardened builder primitives.

const provider = getKeetaProvider()
await provider.requestCapabilities(['read', 'transact'])

const userClient = await provider.getUserClient()
const builder = userClient.initBuilder()

builder.send(
  { publicKeyString: 'kta_recipient_address' },
  '1000000000',
  { publicKeyString: 'kta_token_address' }
)

if (typeof builder.computeBlocks === 'function') {
  await builder.computeBlocks()
}

const receipt = await userClient.publishBuilder(builder)
console.log('Transaction hash', receipt?.blocks?.[0]?.hash ?? receipt)

When moving funds on behalf of a storage account, set the storage account context inside the builder and ensure the user has granted SEND_ON_BEHALF. Review the dApp’s src/app/lib/storage-account-manager.ts for the authorized pattern.

Auxiliary Data & Services

  • KTA price: keeta_getKtaPrice returns the cached CoinGecko snapshot generated by the background handler.
  • Token metadata: keeta_getTokenMetadata reads decimals, ticker, and metadata fields from on-chain state.
  • Network details: keeta_getNetwork mirrors the wallet’s active chain and should drive your dApp routing.

Localhost Allowlist

During development the wallet injects the provider into these origins:

http://localhost:3000/*
http://localhost:3001/*
http://localhost:5173/*
http://127.0.0.1:3000/*
http://127.0.0.1:3001/*
http://127.0.0.1:5173/*

Production dApps must be served over HTTPS. The wallet will prompt the user to approve new origins before injection.

Production Hardening Checklist

  • Error surfaces: Wallet errors return code, message, and optional data. Map known KETA_ERROR_CODES to actionable UI.
  • Rate limiting: Throttle polling (balances, price, activity) to avoid frequent service-worker cold starts.
  • Capability hygiene: Track expiresAt and refresh tokens before expiry. Surface banners when permissions are lost.
  • User feedback: Mirror wallet prompts with a contextual modal (“Waiting for Keythings Wallet …”).
  • Automated testing: Integrate bun run test:e2e, bun tests/validation/validate-manifest.js, and bun run security:all in CI pipelines.

Next Steps & References

  • Read src/background/wallet-provider-handler.ts for end-to-end authorization flow details.
  • Review the Security guide to align dApp messaging with wallet risk assessments.