Aubergine

For developers

Developer mode is not a second product and not a paid extra. It is a switch inside the same extension. Here is what it unlocks and how to build against it.

Project status

This wallet runs on the test network (no real money) and is not published in the browser stores yet. It has not been audited.

What developer mode unlocks

The complete list — there is no further tier hidden behind this one.

  • Network selection extended by futurenet (a custom endpoint is planned but not selectable yet); testnet and the main network are available to beginners too. Mainnet and futurenet ask for an optional browser permission the first time you pick them
  • Transaction view: plain language plus expandable XDR/Base64
  • Signing Soroban contract calls — the contract and function are named, the arguments are not decoded yet
  • dApp connector with a per-origin allowlist
  • Asset approvals for any issuer, with a freely editable limit (beginner mode gets a short curated list instead)
  • Signer overview for multisig accounts (display only in phase 1)
  • Manually overridable network fee
  • Numeric breakdown of reserved account balances

Turning it on

Settings → "Developer mode", confirmed behind a warning. A persistent badge in the header then shows the active mode. The wallet never switches by itself, and it never switches because a web page asked it to. Only on activation is the content script registered at all — before that, the wallet simply does not exist as far as web pages are concerned. Access to web pages is an optional permission: the browser asks for it at the moment you flip the switch, and it is not requested at install time. Decline, and everything except the dApp connector keeps working.

Two modes, one product

dApp connector

The injected provider lives at window.stellarWallet and deliberately follows the established Freighter-style signature so existing Stellar dApps work without changes. We are not introducing a new standard — interoperability matters more here than elegance.

isConnected(): Promise<boolean>
Is the wallet present, is developer mode on, and is the wallet currently unlocked? It says nothing about whether your origin is allowed — getPublicKey() is what settles that.
getPublicKey(): Promise<string>
Public key of the active account. Requires the user to grant access.
signTransaction(xdr, opts?): Promise<string>
Presents the transaction for confirmation and returns the signed XDR. Without confirmation you get USER_REJECTED.
getNetwork(): Promise<{ network, networkPassphrase }>
The currently selected network as an object, not a string: network is the lower-case id (testnet, futurenet, mainnet, custom), networkPassphrase is the value you should build against.

Code example

A minimal integration. Errors come back as typed codes rather than raw exception strings, so your dApp can localise them.

connect.ts
// Connect, request a signature, handle failures properly.
const wallet = window.stellarWallet;

if (!wallet) {
  // Wallet not installed, or developer mode is off.
  showInstallHint();
  return;
}

try {
  const publicKey = await wallet.getPublicKey();

  // getNetwork() returns an object, not a string.
  const { network, networkPassphrase } = await wallet.getNetwork();
  // network is e.g. 'testnet', networkPassphrase the matching passphrase.

  const xdr = buildPaymentXdr({ from: publicKey, to, amount, networkPassphrase });

  // The wallet shows a plain-language summary and waits for the
  // user to confirm explicitly. If the passphrase you pass differs
  // from the active network, the dialog warns about it.
  const signedXdr = await wallet.signTransaction(xdr, { networkPassphrase });

  await submitToHorizon(signedXdr);
} catch (error) {
  switch (error.code) {
    case 'USER_REJECTED':
      // No error dialog needed - this was a decision.
      break;
    case 'WALLET_LOCKED':
      showUnlockHint();
      break;
    case 'NETWORK_ERROR':
      showRetry();
      break;
    default:
      report(error);
  }
}
Code example: connect, request a signature, handle error codes.

Message protocol

There is exactly one permitted channel between the interface and the background process: a typed message API whose payloads are schema-validated. No shared objects, no direct access. The method families:

wallet

  • wallet.create
  • wallet.importMnemonic
  • wallet.unlock
  • wallet.lock
  • wallet.status

account

  • account.list
  • account.add
  • account.select
  • account.balances
  • account.history

tx

  • tx.build
  • tx.describe
  • tx.sign
  • tx.submit

dapp

developer mode only
  • dapp.requestConnect
  • dapp.signXdr

settings

  • settings.get
  • settings.set

Error codes

  • WALLET_LOCKED — wallet is locked, password required
  • BAD_PASSWORD — wrong password
  • USER_REJECTED — the user declined
  • NETWORK_ERROR — endpoint unreachable
  • TX_FAILED:<result_code> — the network rejected the transaction

Technical foundation

Extension
WXT — one codebase for Chrome MV3 and Firefox MV3
Interface
React 19 with TypeScript 5 in strict mode
Styling
Tailwind CSS v4 with shared design tokens (no CSS-in-JS, because of the MV3 CSP)
Chain SDK
@stellar/stellar-sdk 16.x — Horizon client and Soroban RPC in one
State
Zustand for UI and settings, TanStack Query for network-derived data
Cryptography
WebCrypto AES-256-GCM, key derivation via Argon2id (hash-wasm), PBKDF2-SHA-512 fallback
Tests
Vitest for domain logic (crypto, SEP-0005 derivation, transaction description); a Playwright end-to-end run is planned but not set up yet
Website
Astro 5 and Tailwind v4, static, no tracking

Contributing

The source is public. Contributions are especially welcome around plain-language transaction descriptions, translations, and tests for the cryptography layer. Architectural decisions and invariants are recorded in the repository’s architecture document — please read it first, it is strict on purpose.

Placeholder Repository URL — to be filled in by Rene

This detail must be completed before publication.