Seeker Connect

作者 solana-mobile3a5ca04834ec無授權條款8 個星標收錄於 2026年10月8日更新於 2026年10月8日儲存庫今天更新

Connect a web dapp to the Seeker device's built-in wallet with Seeker Connect. Use when adding wallet connection, sign-in with Solana, message signing, or transaction signing to a website that runs in the browser on a Seeker phone, registering the "Seeker Connect" Wallet Standard wallet, setting the Nostr relay for relayDomain, using the seeker-connect-button element, or debugging SeekerConnectError codes like association-failed.

AI 產生的概覽

指導開發者將 Seeker Connect 接入網頁 dapp,以使用 Seeker 裝置內建的 Solana 錢包。

功能
說明如何安裝並註冊 Seeker Connect 的 Wallet Standard 錢包、設定 Nostr 中繼網域,並在瀏覽器 dapp 中使用連線、登入、訊息簽章與交易簽章。內容涵蓋工作階段與授權快取行為、依能力判斷的功能偵測、association-failed 等錯誤碼,以及選用的品牌化連線按鈕。產出的是整合指引與程式碼片段,而非檔案或指令碼。
適用情境
適用於為在 Seeker 手機瀏覽器中執行的網站加入錢包連線、Solana 登入、訊息簽章或交易簽章時。也適合排查 SeekerConnectError 錯誤碼或為 relayDomain 設定中繼時使用。
執行需求
需要一個網頁 dapp 專案,並安裝 npm 套件 @solana-mobile/seeker-connect-wallet-standard、@solana/wallet-standard-features、@wallet-standard/app 與 @wallet-standard/features(0.1.2 或更新版本),可選安裝 @solana-mobile/seeker-connect-ui 與 @solana-mobile/seeker-connect-web。需要在 Seeker 裝置上使用瀏覽器、能連線至 Solana Mobile 的 Nostr 中繼網路,並接受 Solana Mobile 的中繼條款。不附指令碼,僅為說明文件。

Seeker Connect for web dapps

Seeker Connect links a web page open in the browser on the device to a Solana Mobile certified device's own wallet — Seeker is the first such device. It is a dapp-only SDK built on top of the official Mobile Wallet Adapter libraries: a UX/DX layer, not a protocol reimplementation. Where generic MWA centers on a "pick a wallet" chooser, Seeker Connect launches the device wallet directly through the MWA spec's Endpoint-specific URI (Android App Link) mechanism, carries the session over a Nostr relay, and ships Seeker-branded progress and error UI — all exposed to the app as an ordinary Wallet Standard wallet named "Seeker Connect".

Targets: Web is available today. React Native and Android (Kotlin) are planned. Until they ship, wallet connection inside an Expo or React Native app uses the solana-mobile-wallet skill instead.

The mental model — read this before writing code

  • There is no long-lived connection. Every wallet interaction (connect, sign message, sign transaction) opens its own short-lived MWA session, runs one request, and tears down. Each interaction launches the wallet app and shows a branded progress overlay — the one exception is a silent connect, which only reads the cache and never opens a session. This is by design — do not build reconnect loops or keep-alive logic around it.
  • "Connected" means "holds a cached authorization." The first connect() stores a wallet-issued authToken plus accounts and capabilities (localStorage by default). Later operations replay that token silently — no repeated consent prompt.
  • Disconnect only forgets the token locally. It deliberately never calls the wallet's deauthorize, because that would launch the wallet app just to disconnect.
  • It only completes on the device. On desktop, connect fails with association-failed — typically within seconds, when nothing answers the wallet launch; the association timeout only applies to a wallet that launches but never connects. Register the wallet unconditionally; it simply won't get past association elsewhere.
  • The SDK ships no default relay. Use Solana Mobile's, relay.solanamobile.com: it is free, including in production. Using it requires agreeing to Solana Mobile's terms, so tell the developer to accept them — Step 2.

Step 1: install

New project: the react-kit-shadcn template from the Solana Mobile CLI ships with Seeker Connect already wired up on Solana Mobile's relay. The next steps create prints tell the developer to accept the relay terms on the quickstart page:

bash
npx solana-mobile@latest create my-app --template react-kit-shadcn

Existing app: install the Wallet Standard entry point plus the Wallet Standard helpers the snippets below import from:

bash
npm install @solana-mobile/seeker-connect-wallet-standard @solana/wallet-standard-features @wallet-standard/app @wallet-standard/features

Use 0.1.2 or later. Earlier releases sit on Mobile Wallet Adapter 2.x, which can leave a wallet call pending forever when the session drops; 0.1.2 moves to MWA 3.0, which rejects it with session-closed instead.

The Wallet Standard package pulls in the other Seeker Connect packages (core, ui, web) as dependencies. They are not re-exported, though — so also install any of them the app imports from directly (@solana-mobile/seeker-connect-ui for the optional button below, @solana-mobile/seeker-connect-web for the imperative path in the references); strict package managers like pnpm refuse imports of undeclared transitive dependencies.

PackageRole
@solana-mobile/seeker-connect-coreShared contracts: config, error taxonomy, SeekerLink port
@solana-mobile/seeker-connect-uiLit elements: progress overlay, error dialog, branded button
@solana-mobile/seeker-connect-wallet-standardThe entry point — the Wallet Standard wallet
@solana-mobile/seeker-connect-webMWA-over-Nostr transport, plus an imperative SDK

Step 2: set the relay

Seeker Connect carries MWA's extended remote communication protocol over a Nostr relay, named by relayDomain. Payloads are end-to-end encrypted between dapp and wallet; the relay carries ciphertext only.

Use relay.solanamobile.com. Solana Mobile runs it for Seeker Connect, and it is free to use, including in production. Pass the bare host; the SDK adds the wss:// scheme itself.

Tell the developer to accept the relay terms. Using Solana Mobile's relay requires agreeing to Solana Mobile's terms. Whenever you wire it in, say so, and point the developer at the quickstart page where they accept them: https://docs.solanamobile.com/solana-mobile-stack/seeker-connect-quickstart

Step 3: register once at startup

Call registerSeekerConnect before the UI renders, so wallet discovery sees it from the first render. It must run in the browser — the snippet below reads window, so under SSR (Next.js and similar) put the call in a client-only module or guard it with typeof window !== 'undefined'; in a plain client-rendered app, module scope of the entry file is fine:

ts
import { registerSeekerConnect } from '@solana-mobile/seeker-connect-wallet-standard';
registerSeekerConnect({  identity: {    name: 'My Dapp',    uri: window.location.origin,    icon: '/icon.png', // resolved relative to uri; shown in the wallet's consent UI  },  relayDomain: 'relay.solanamobile.com', // requires accepting Solana Mobile's terms — Step 2});

Configuration:

OptionRequiredNotes
associationTimeoutMsnoHow long a launch may take before association-failed. Default 30s
chainnoChain requested at authorization. Default solana:mainnet
firstConnectWalletBaseUrinoLeave unset unless Solana Mobile publishes a value to paste. Unset, first connects use the generic solana-wallet: scheme; set, the page navigates to that host — see references/troubleshooting.md [blocked]
identityyesname, uri, optional icon. Shown in the wallet's consent UI
relayDomainyesNostr relay that carries the session traffic. Use relay.solanamobile.com — Step 2

registerSeekerConnect also accepts seekerLink, authorizationCache, and presenter overrides — see references/imperative-api.md [blocked].

Step 4: use it like any other wallet

After registration, "Seeker Connect" appears in the Wallet Standard registry, so wallet-adapter, ConnectorKit, @solana/react-hooks, or a raw @wallet-standard/app listing all pick it up with no further wiring. Existing connect buttons and signing code keep working.

Features exposed: standard:connect, standard:disconnect, standard:events, solana:signMessage, solana:signIn, and — depending on the wallet's reported capabilities after the first connect — solana:signTransaction and/or solana:signAndSendTransaction.

Working with the wallet directly:

ts
import { SeekerConnectWalletName } from '@solana-mobile/seeker-connect-wallet-standard';import { getWallets } from '@wallet-standard/app';import { StandardConnect } from '@wallet-standard/features';
const seeker = getWallets()  .get()  .find((wallet) => wallet.name === SeekerConnectWalletName);const { accounts } = await seeker.features[StandardConnect].connect();

Restore the session on page load with a silent connect — it reads the cache and never launches the wallet, resolving with zero accounts when there is nothing cached:

ts
const { accounts } = await seeker.features[StandardConnect].connect({ silent: true });

Feature-detect the signing routes. Until the first connect, both transaction features are assumed; after it they are re-derived from the wallet's actual capabilities, announced via a standard:events change event. Check before calling:

ts
import { SolanaSignAndSendTransaction } from '@solana/wallet-standard-features';
const feature = seeker.features[SolanaSignAndSendTransaction];if (feature) {  const [{ signature }] = await feature.signAndSendTransaction({    account,    chain: seeker.chains[0],    transaction,  });}

chain is part of the Wallet Standard input, but this wallet ignores it — the network is the one passed to registerSeekerConnect, replayed from the cached authorization on every call. Nothing cross-checks the two, so a per-call solana:devnet against a mainnet registration submits on mainnet without complaint. Pass seeker.chains[0] so the two can never disagree, and change networks at registration.

Transactions cross the feature boundary as raw serialized bytes (Uint8Array), legacy and v0 both supported — serialize with whichever client library the app already uses. Sign-in follows the SIWS spec via solana:signIn; domain defaults to window.location.host.

When sign-in authenticates a user, the server is the authority, not the client: issue a single-use, short-lived nonce server-side, and verify the returned message and signature — including the expected domain and validity window — on the server before creating a session. The seeker-genesis-token skill walks through that server flow step by step.

Handle errors by code

Wallet outcomes reject with a SeekerConnectError carrying a code:

CodeMeaning
association-failedNo wallet completed the launch: timeout, relay unreachable or misconfigured, or not on a Seeker
authorization-declinedThe user declined authorization. The cached token is wiped; the next connect prompts fresh consent
cancelledThe user dismissed the progress overlay. A normal outcome — never surface it as an error
request-declinedThe wallet declined to sign or submit
session-closedThe session ended before the interaction completed. After signAndSendTransaction the transaction may still have landed, and the error carries no signature — never retry automatically — see references/troubleshooting.md [blocked]
wallet-errorAny other wallet-reported error

Three failures are plain Errors instead, and they are misuse rather than wallet outcomes: signAndSendTransaction on a wallet that reported no support for it, any signing call before a successful connect, and a sign-in the wallet answered without a result.

That distinction decides who shows the error. The branded dialog fires for SeekerConnectError and nothing else — every code above except cancelled, which is a normal outcome and stays silent. The three plain Errors never reach the presenter, so if the app shows nothing they fail invisibly. Branch on the type first, then on .code:

ts
import { SeekerConnectError, SeekerConnectErrorCode } from '@solana-mobile/seeker-connect-wallet-standard';
try {  await seeker.features[StandardConnect].connect();} catch (error) {  if (error instanceof SeekerConnectError) {    if (error.code === SeekerConnectErrorCode.cancelled) {      return; // user closed the overlay; nothing to report    }    // The SDK has shown its dialog. Log, and leave the UI in the disconnected state.    console.error(error);    return;  }  // No dialog was shown for this one. Surface it yourself.  showToast('Could not connect to the wallet.'); // your app's own error UI  console.error(error);}

Optional: the branded connect button

@solana-mobile/seeker-connect-ui ships a <seeker-connect-button> custom element in Shadow DOM (no host CSS needed). The SDK defines its elements on first use; call defineSeekerConnectElements() at startup to define them eagerly:

ts
import { defineSeekerConnectElements } from '@solana-mobile/seeker-connect-ui';
defineSeekerConnectElements();
html
<seeker-connect-button theme="dark" variant="sign-in"></seeker-connect-button>

Attributes: disabled, theme="light" | "dark" (names the host page's theme), and variant="connect" (default) or variant="sign-in" for the label. Wire click to the connect or sign-in call yourself — the button is presentation only.

Reference material

  • references/imperative-api.md [blocked] — the non-Wallet-Standard path via createNostrSeekerLink().transact, and the seekerLink / authorizationCache / presenter overrides
  • references/troubleshooting.md [blocked] — association failures, desktop testing, capability-derived features, cache behavior

Related skills

  • seeker-domains — display .skr names instead of raw addresses
  • seeker-genesis-token — verify Seeker device ownership after connecting
  • solana-mobile — the CLI, templates, and toolchain behind solana-mobile create
  • solana-mobile-wallet — wallet connection in React Native apps (Mobile Wallet Adapter directly)

Links

來源與署名

來源:solana-mobile/solana-mobile-skills位於skills/seeker-connect提交3a5ca04

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架