Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 45 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# @1001-digital/wagmi-in-app-wallet

A [wagmi](https://wagmi.sh) connector that turns a BIP39 mnemonic into a fully functional in-browser wallet. Keys are derived locally and stored in `localStorage` — no external signers or extensions required.
A wagmi connector for a client-encrypted, host-synchronized EVM wallet.
Mnemonic and private-key material stay in a browser worker while the host stores
only a versioned AES-GCM vault.

## Install

Expand All @@ -13,48 +15,52 @@ pnpm add @1001-digital/wagmi-in-app-wallet
## Usage

```ts
import { inAppWallet, prepareInAppWallet } from '@1001-digital/wagmi-in-app-wallet'
import { createConfig, http } from '@wagmi/core'
import { mainnet } from 'viem/chains'

const config = createConfig({
chains: [mainnet],
connectors: [inAppWallet()],
transports: { [mainnet.id]: http() },
})

// Derive and store the private key from a mnemonic
await prepareInAppWallet('your twelve word mnemonic ...')

// Then connect
await config.connectors[0].connect()
```
import {
EncryptedWalletKeyring,
inAppWallet,
type WalletVaultStore,
} from '@1001-digital/wagmi-in-app-wallet'

const store: WalletVaultStore = {
load: () => api.get('/me/vaults/evm-in-app-wallet'),
put: (document, expectedRevision) =>
api.put('/me/vaults/evm-in-app-wallet', {
document,
expectedRevision,
}),
}

### Custom storage key
const keyring = new EncryptedWalletKeyring({ store })
await keyring.load()

```ts
inAppWallet({ storageKey: 'my-app:wallet-pk' })
const connector = inAppWallet({
keyring,
requestUnlock: async () => {
await openUnlockDialog(keyring)
},
})
```

## API

### `inAppWallet(parameters?)`

Creates a wagmi connector. Accepts an optional `InAppWalletParameters` object:

- `storageKey` — localStorage key for the private key (default: `evm:in-app-wallet-pk`)

### `prepareInAppWallet(mnemonic)`

Derives a private key from a BIP39 mnemonic, stores it in localStorage, and returns the wallet address. Call this before connecting.

### `InAppWalletParameters`

```ts
type InAppWalletParameters = {
storageKey?: string
}
```
Use `keyring.create({ passphrase, scope })` for a new 12-word wallet or
`keyring.restore({ mnemonic, passphrase, scope })` for recovery. A passphrase is
always retained as the portable recovery wrapper; supported WebAuthn passkeys can
be added as additional PRF-based wrappers.

Calling `disconnect()` or `keyring.lock()` destroys the in-memory signer. It does
not delete the synchronized encrypted vault.

## Security model

- The host API receives ciphertext, public address, salts, and wrapping metadata.
It never receives a mnemonic, private key, passphrase, or WebAuthn PRF output.
- Passphrase keys use Argon2id. Vault and wrapped-key ciphertext use AES-256-GCM
with domain-separated authenticated data.
- Browser reload, tab close, explicit lock, and sign-out require another unlock.
- A recovery phrase remains the user escape path if the host API or passkey
provider becomes unavailable.
- JavaScript running in the page can still influence requests while the wallet is
unlocked. Host applications must use a restrictive CSP and treat third-party
scripts as part of the wallet trust boundary.

## License

Expand Down
15 changes: 12 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@1001-digital/wagmi-in-app-wallet",
"version": "0.1.0",
"version": "1.0.0",
"type": "module",
"exports": {
".": "./src/index.ts"
Expand All @@ -12,9 +12,18 @@
"@wagmi/core": ">=3.0.0",
"viem": ">=2.0.0"
},
"dependencies": {
"@noble/hashes": "^2.2.0"
},
"devDependencies": {
"@wagmi/core": "^3.4.0",
"@wagmi/core": "3.4.6",
"@types/node": "^24.0.0",
"typescript": "^5.8.0",
"viem": "^2.45.0"
"viem": "2.48.4",
"vitest": "^3.2.4"
},
"scripts": {
"test": "vitest run",
"typecheck": "tsc --noEmit"
}
}
Loading