# Contractless Browser Wallet

This is the WebExtension application for the Contractless Web3 wallet. It uses
the repository's `core` package for wallet generation, encryption,
transaction inspection, signing, message proofs, and wallet registration.

The private key is encrypted in extension-local storage. Decrypted key
material exists only in the background service worker while the wallet is
unlocked and is cleared when the wallet locks or the worker is suspended.

## Requirements

Install:

- Rust and Cargo
- `wasm-pack`
- Node.js 20 or later
- pnpm

Install the JavaScript dependencies from this directory:

```text
cargo install wasm-pack
pnpm install
```

## Build

Build both browser packages:

```text
pnpm run build
```

This compiles separate testnet and mainnet versions of the Rust core to
WebAssembly and creates:

```text
dist/chrome/
dist/firefox/
```

Build only one browser when developing:

```text
pnpm run build:chrome
pnpm run build:firefox
```

Chrome and Firefox use the same TypeScript, HTML, CSS, SDK provider, and two
network-specific WebAssembly cores. Each output receives its own manifest because Chrome uses a
Manifest V3 background service worker while Firefox uses a Manifest V3
background event script.

### Load in Chrome

Open `chrome://extensions`, enable **Developer mode**, select **Load
unpacked**, and choose `dist/chrome`.

### Load in Firefox

Open `about:debugging`, select **This Firefox**, select **Load Temporary
Add-on**, and choose `dist/firefox/manifest.json`.

The Firefox build requires Firefox 149 or later. Firefox 149 removed the user
gesture restriction from `action.openPopup`, allowing website connection and
transaction requests to open the wallet approval interface automatically in
the same way as the Chrome build.

Run Mozilla's validator after building Firefox:

```text
pnpm run lint:firefox
```

Create the Firefox submission archive:

```text
pnpm run package:firefox
```

The archive is written to `web-ext-artifacts`. Firefox requires Mozilla signing
before the packaged extension can be installed permanently in standard
Firefox releases.

Safari requires conversion through Apple's Safari Web Extension tooling and
Xcode.

## Networks

The wallet presents a Testnet/Mainnet selector before wallet creation and
unlock. Addresses, active wallets, WebAssembly transaction codecs, API URLs,
and website sessions are isolated by the selected network. Switching networks
locks the wallet and expires all application access keys.

Testnet uses `.cltc` addresses and defaults to
`https://api.contractless.dev`. Mainnet uses `.clc` addresses and has no API
configured by default. A mainnet wallet may still be created, imported, and
backed up while that endpoint is empty; network lookups, registration, and
transaction broadcasts remain unavailable until a mainnet API is configured.

## Provider

Websites receive `window.contractless` with a Promise-based `request` method.
Applications should normally use the downloadable client in
`../sdk/contractless.js` instead of calling the provider directly:

```js
import { ContractlessWallet } from "./contractless.js";

const wallet = new ContractlessWallet({
  appId: "example-application"
});

const connection = await wallet.connect({
  message: "Sign in to Example Application"
});
```

Transaction and message requests are routed through the background worker and
wallet approval screen. Websites never receive wallet private keys or
unrestricted raw-signing access. Single-signer transactions are signed and
broadcast by the wallet. The transaction ID and signed bytes are returned only
after the configured API accepts the broadcast.

Supported provider methods are:

- `contractless_detect`
- `contractless_connect`
- `contractless_accounts`
- `contractless_balances`
- `contractless_disconnect`
- `contractless_signMessage`
- `contractless_sendTransaction`

Connection, message signing, and transaction submission wait for an explicit
approval or rejection. A successful connection creates a random 256-bit
access key bound to the browser-derived origin, stable application ID, active
wallet, and current wallet unlock session.

The website stores its copy in browser `sessionStorage`. The extension stores
the matching authorization in extension session storage. The key is reusable
until the wallet locks, changes active wallets, or the application
disconnects. It is not a permanent connected-site permission.

Every authorized request includes the claimed origin, application ID, and
access key. The extension independently derives the real origin from the
browser message sender and rejects mismatches.

See `../sdk/README.md` for complete website integration examples.

## Wallet registration

The **Register wallet** button creates the command-38 registration signature
and the fixed API handshake proof locally. It sends the address, public key,
and signatures to:

```text
POST /api/v1/addresses/register
```

The private key and wallet encryption key never leave the extension. The API
requires the authenticated address and public key to match the registration
being submitted before forwarding it to a Contractless node.

The API operator must include the installed extension origin in
`API_CORS_ORIGINS`, for example:

```text
API_CORS_ORIGINS=chrome-extension://extension-id
```

Keep any existing allowed website origins in the comma-separated list.
Changing the API endpoint in the wallet prompts the user for permission to
contact that HTTPS origin.

Firefox extension pages use a `moz-extension://` origin. The production API
must allow the installed Firefox extension to make the same wallet requests as
the Chrome build.

## Firefox Submission Source

Mozilla reviewers must receive the readable source because Vite bundles the
TypeScript and `wasm-pack` generates the WebAssembly package. Submit the built
Firefox archive as the add-on and a separate source archive containing at
least:

```text
core/
extension/src/
extension/package.json
extension/pnpm-lock.yaml
extension/pnpm-workspace.yaml
extension/tsconfig.json
extension/vite.config.ts
README.md
```

Do not include `extension/node_modules`, `extension/dist`, or `core/target` in
the source archive. Reviewers can reproduce the Firefox build by entering the
`extension` directory and running:

```text
pnpm install --frozen-lockfile
pnpm run build:firefox
```
