# 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 ```