Use cases
Explore
Docs
Pricing

Organigram Passkey Wallet

The ultimate web3 experience: unlock wallets and sign transactions with recoverable passkeys in a breeze.

Organigram Passkey Wallet is a lightweight Ethereum wallet powered by WebAuthn passkeys. It gives users the day-to-day experience they expect from modern passwordless apps while preserving the core property that matters in web3: fully and safely owning a standard Ethereum address.

No cumbersome smart account deployment. No per-network account ceremony. No custodial key service. No protocol lock-in. Just a standard EOA, encrypted client-side, unlocked with rotative passkeys, and exposed via Wagmi and Rainbowkit.


pnpm add @organigram/passkey-wallet

Web3 identity, solved

Most web3 onboarding still asks users to understand browser extensions, seed phrases, hardware devices, account abstraction, gas sponsorship, recovery contracts, network-specific deployments, signer permissions... before they have even used the product. Organigram Passkey Wallet takes a simpler path.

The user creates a wallet in the browser. The wallet is a normal Ethereum EOA derived from a recovery phrase. That recovery phrase is encrypted locally with a key derived from a WebAuthn passkey PRF output, then stored only as an encrypted vault envelope. When the user returns, their passkey unlocks the vault and the EOA signs messages or transactions like any other wallet.

The result is a wallet experience that feels native to the web:

  • users unlock and sign with passkeys;
  • multiple backup passkeys can be added to the same address;
  • compromised or obsolete passkeys can be rotated;
  • seed phrases can be exported for disaster prevention;
  • encrypted backup vaults can be saved remotely or locally
  • one address across every EVM network;
  • standard EIP-1193 semantics for dapps;
  • no onchain deployment required;
  • no offchain signing relay or hosted signer to trust.

Highlights

Unlock and sign with passkeys

Users unlock the wallet with a WebAuthn passkey. Once unlocked, the wallet signs messages, typed data, and transactions with the underlying EOA.

A fully-fledged Ethereum wallet

The wallet exposes the same interface dapps already expect from injected wallets, WalletConnect wallets, and RainbowKit connectors. It supports account requests, chain switching, message signing, typed data signing, and transaction submission like any other standard wallet.

Multiple backup keys, one address

Users can add additional passkeys for the same address. A backup phone, laptop, password manager, or security key can unlock the same encrypted wallet vault.

No smart-account deployment burden

The address is a standard EOA, so it exists everywhere EVM accounts exist. There is no need to deploy account contracts across networks before the user can start.

Seed phrase export

Passkeys optimize daily use, while the capacity to export seed phrases preserves portability and gives users a disaster prevention path for the case where every passkey is lost.

Recoverable vaults

The app exports an encrypted vault that can be saved to and restored from anywhere. Use remote servers, save locally or in a password manager without fear to leak sensitive information.

Wallet UI

This package builds a production-ready app used to serve the wallet locally or on custom domains.


pnpm dev
pnpm build:artifact

build:artifact builds the TypeScript library to app-dist, and packages a deterministic artifacts/passkey-wallet-app-v<version>.tar.gz with a .sha256 checksum and manifest.

Local development

pnpm dev serves the wallet at https://localhost:3002, behind an HTTPS proxy in front of Vite (port 3003). The first run creates a local certificate with mkcert.

Any other hostname can be passed as an argument (pnpm dev my.host.test): the certificate is regenerated to include it.

Run your own wallet

You do not have to trust the servers of wallet.organigram.ai, nor the code they serve, to use your Organigram accounts. The local-organigram profile serves your own copy of the wallet, built from this source code, on your machine:


pnpm dev local-organigram

(or pnpm passkey-wallet dev local-organigram from the repository root), then open https://local.wallet.organigram.ai:3002.

local.wallet.organigram.ai is a subdomain of wallet.organigram.ai, so this copy unlocks the same passkeys, hence the same accounts: the profile sets the passkey RP ID to wallet.organigram.ai. Its browser storage is separate, so restore your encrypted vault there once (see Recoverable vaults): your passkey then unlocks it as usual. Your keys never leave your browser, and signing in no longer depends on wallet.organigram.ai being online or unaltered.

The name already resolves to 127.0.0.1 through public DNS, so no setup is needed. For a stronger guarantee, also resolve it in your hosts file: a tampered DNS record could otherwise send it to a server able to request the PRF output of your passkeys (macOS and Linux):


grep -q 'local.wallet.organigram.ai' /etc/hosts || echo '127.0.0.1 local.wallet.organigram.ai' | sudo tee -a /etc/hosts

To use it from any app, turn on Settings → Your own wallet on wallet.organigram.ai. Requests then go to your copy, and once it has answered, apps open it directly: wallet.organigram.ai is no longer loaded at all. If your copy stops answering, for instance because it is not running, the request goes back to wallet.organigram.ai after a few seconds.

Apps that use this package accept the local copy of the official wallet without any setting, on their pages and on their wallet endpoints (sign-in challenge, vault backups, RPC).

A wallet deployed elsewhere can offer the same to its users with its own local copy, served under a subdomain of its host so that it unlocks the same passkeys:

  • the deployment declares it with localWalletOrigin in passkey-wallet.config.json (or PASSKEY_WALLET_LOCAL_WALLET_ORIGIN at build time), for example https://local.wallet.example.com:3002;
  • users run it with PASSKEY_WALLET_PASSKEY_RP_ID=wallet.example.com pnpm dev local.wallet.example.com;
  • apps accept it with NEXT_PUBLIC_ORGANIGRAM_PASSKEY_WALLET_LOCAL_URL, next to NEXT_PUBLIC_ORGANIGRAM_PASSKEY_WALLET_URL. The value is ignored unless it is an HTTPS subdomain of the wallet host.

Each production build of https://wallet.organigram.ai is published as a release of this repository, with the SHA-256 of every served file. To check that the live wallet serves one of these releases unchanged, run from a clone of this repository:


node scripts/verify-runtime-dist.mjs --url https://wallet.organigram.ai

With Node.js 24 and pnpm 10.28, rebuilding a release commit reproduces its files:


pnpm install --frozen-lockfile
pnpm run build
node scripts/verify-runtime-dist.mjs --dist app-dist --manifest <release manifest>

Architecture


Browser
WebAuthn passkey with PRF
|
v
HKDF-derived vault key
|
v
AES-GCM encrypted wallet vault envelope
|
v
Recovery phrase -> EOA -> EIP-1193 provider
Server
WebAuthn challenges
Credential public keys
Credential counters
Encrypted vault envelopes

In practice, the flow is:

  1. Generate a recovery phrase in the browser.
  2. Derive a standard Ethereum EOA from that phrase.
  3. Create a WebAuthn passkey with residentKey: "required", userVerification: "required", and the prf extension.
  4. Derive a vault encryption key from the PRF output and a random salt.
  5. Encrypt the wallet vault with AES-GCM.
  6. Store only the credential metadata and encrypted vault envelope server-side.
  7. On unlock, verify WebAuthn, recover the PRF output, decrypt the envelope, and expose the EOA through EIP-1193.

The private wallet material is generated and decrypted in the browser. The server stores only WebAuthn verification material and encrypted vault envelopes. The server never needs the seed phrase, the private key, or the decrypted user vault.

Quick Start

Create and unlock vaults in the UI with wallet helpers:


import {
createGeneratedRecoveryPhrase,
registerBrowserPasskeyVault,
unlockBrowserPasskeyVault
} from '@organigram/passkey-wallet/browser-wallet'
const registration = await registerBrowserPasskeyVault({
recoveryPhrase: createGeneratedRecoveryPhrase(),
name: 'Account 1'
})
const unlocked = await unlockBrowserPasskeyVault({
records: [registration.record]
})
const signature = await unlocked.wallet.account.signMessage({
message: 'gm'
})

The passkey ceremony and vault decryption happen in the wallet origin. Dapps should integrate through the remote connector.

RainbowKit Integration


import { connectorsForWallets } from '@rainbow-me/rainbowkit'
import { createConfig, http } from 'wagmi'
import { mainnet, sepolia } from 'wagmi/chains'
import {
createOrganigramRemotePasskeyWallet
} from '@organigram/passkey-wallet/remote-rainbowkit'
const passkeyWallet = createOrganigramRemotePasskeyWallet({
walletOrigin: 'https://wallet.organigram.ai'
})
export const wagmiConfig = createConfig({
chains: [mainnet, sepolia],
connectors: connectorsForWallets(
[
{
groupName: 'Recommended',
wallets: [() => passkeyWallet]
}
],
{
appName: 'Your App',
projectId: 'walletconnect-project-id'
}
),
transports: {
[mainnet.id]: http(),
[sepolia.id]: http()
}
})

From the dapp's perspective, this behaves like a wallet. Calling personal_sign, eth_signTypedData_v4, or eth_sendTransaction goes through the wallet origin, where the user approves the request.

Server Responsibilities

The wallet app is static. A server is optional and should only receive encrypted backup packages or dapp sign-in challenges. It should not create WebAuthn credentials, unlock vaults, or receive decrypted wallet material.

Backup Keys And Rotation

A passkey wallet should never depend on a single authenticator.

Organigram Passkey Wallet supports adding another passkey to the same wallet by re-encrypting the same vault payload for a new credential. The address does not change. The user can keep using the same EOA while adding a laptop passkey, a phone passkey, a password manager passkey, or a hardware security key.

Rotation is the same idea in reverse: remove obsolete credentials only when another recovery method remains. The package gives you the wallet and envelope building blocks; your product should decide the UX policy for deletion, confirmation, and minimum backup requirements.

Recovery

Passkeys make daily wallet use simple, but users still need an escape hatch.

Organigram Passkey Wallet keeps the wallet portable by deriving the EOA from a standard recovery phrase. If every passkey is lost, the user can recover the account with the seed phrase, or use it in any other wallet.

The recovery phrase should always be exported before the user loses all passkeys. The package provides a helper to export a seed phrase after a successful WebAuthn ceremony, but cannot do so after all access to the vault is lost.

Security Model

Organigram Passkey Wallet is non-custodial by design.

The browser creates the wallet. The browser encrypts the vault. The browser decrypts the vault after a successful WebAuthn ceremony. The server stores encrypted envelopes and verifies that WebAuthn responses are valid for the expected challenge, origin, RP ID, and credential public key.

This model assumes:

  • WebAuthn PRF support is required for the passkey vault flow;
  • the host app serves the wallet UI from a trustworthy origin;
  • the server correctly expires and consumes challenges;
  • the server verifies credential counters and user verification;
  • recovery phrase export is treated as a sensitive operation;
  • backup passkey deletion never leaves the user without a recovery method;
  • a compromised browser session can read unlocked wallet material while a request is being processed.

Passkey scope

Passkeys are created for the exact hostname serving the wallet (for example wallet.organigram.ai). Any page served under the passkey RP ID can request the PRF output that decrypts the vault, so no other application should be hosted under it. Dapps never need the wallet RP ID: they reach the wallet through its popup and postMessage.

A wallet served from a subdomain can reuse the passkeys of its parent wallet host by setting passkeyRpId in passkey-wallet.config.json (or PASSKEY_WALLET_PASSKEY_RP_ID) to that host. The value must be the current hostname or one of its parent domains. pnpm dev local-organigram does so for wallet.organigram.ai (see Run your own wallet).

The wallet sends a Content-Security-Policy that forbids third-party scripts and any framing of the wallet origin.

Browser Compatibility

The passkey vault depends on the WebAuthn prf extension. Browsers or password managers that do not return PRF output cannot unlock the encrypted vault.

Apps should present a clear fallback when PRF is unavailable:

  • retry with another passkey provider;
  • use a platform authenticator that supports PRF;
  • connect an external crypto wallet instead.

What This Is Not

Organigram Passkey Wallet is not a smart account framework. It does not require 4337 infrastructure, paymasters, bundlers, module registries, or per-chain deployments.

It is not a custodial wallet. The server does not receive the recovery phrase or private key in clear text.

It is not a hosted wallet service. The package is open source; the host app owns persistence, authentication, and backup policy.

It is not a replacement for hardware wallets in high-assurance custody flows. It is designed for the safest mainstream web UX possible while staying portable and EVM-compatible.

License

MIT

Connect an app

Apps reach the wallet through createOrganigramRemotePasskeyWallet(), a RainbowKit wallet whose wagmi connector opens the wallet in a popup. Each connection, signature, transaction or encryption is a request that the user approves in the popup. Apps exchange requests and answers with the popup through postMessage, and only accept answers from the wallet origin and its local copy. Signatures are checked against the connected address and chain before they reach the app. Private keys never leave the wallet origin.

CreateOrganigramRemotePasskeyWalletInput

Input of createOrganigramRemotePasskeyWallet().

Definition

export type CreateOrganigramRemotePasskeyWalletInput = {
walletOrigin?: string;
requestConnect?: RemoteRequestConnect;
requestPersonalSign?: RemoteRequestPersonalSign;
requestSignTypedData?: RemoteRequestSignTypedData;
requestSendTransaction?: RemoteRequestSendTransaction;
};

HydrateOrganigramRemoteWalletConnectionConnector

A wagmi connector, identified by its id.

Definition

export type HydrateOrganigramRemoteWalletConnectionConnector = {
id: string;
};

HydrateOrganigramRemoteWalletConnectionInput

An address already known to the app, for example from its session.

Definition

export type HydrateOrganigramRemoteWalletConnectionInput = {
address: `0x${string}`;
chainId?: number;
};

HydrateOrganigramRemoteWalletConnectionOptions

Input of hydrateOrganigramRemoteWalletConnection().

Definition

export type HydrateOrganigramRemoteWalletConnectionOptions<TConnector extends HydrateOrganigramRemoteWalletConnectionConnector> = HydrateOrganigramRemoteWalletConnectionInput & {
connectors: readonly TConnector[];
connectAsync: (input: {
connector: TConnector;
}) => Promise<unknown>;
defaultChainId?: number;
};

OrganigramRemotePasskeyWalletConnection

A connection to the wallet remembered in the local storage of the app.

It only holds the public address and chain, never a key: every signature still asks the user in the wallet popup.

Definition

export type OrganigramRemotePasskeyWalletConnection = {
version: 1;
address: `0x${string}`;
chainId: number;
connectedAt: string;
};

beginOrganigramWalletApprovalSequence

Adds presentation-only context to the next wallet approvals. This lets an app tell the wallet that one user action requires several signatures, without broadening any approval or altering its signed payload.

Signature

beginOrganigramWalletApprovalSequence: (input: {
count: number;
label?: string;
}) => (() => void)

clearOrganigramRemotePasskeyWalletConnection

Forget the remembered connection to the wallet.

Signature

clearOrganigramRemotePasskeyWalletConnection: () => void

createOrganigramRemotePasskeyWallet

Create the RainbowKit wallet of the passkey wallet, which opens the wallet in a popup.

The connector exposes an EIP-1193 provider. Account and signing requests open the wallet popup, and every signature is checked against the connected address and chain. Other JSON-RPC requests go to the default RPC URL of the active chain.


const connectors = connectorsForWallets(
[{ groupName: 'Organigram', wallets: [() => createOrganigramRemotePasskeyWallet()] }],
{ appName: 'My app', projectId }
)

Signature

createOrganigramRemotePasskeyWallet: (input?: CreateOrganigramRemotePasskeyWalletInput) => Wallet

Parameters

NameTypeDescription
inputCreateOrganigramRemotePasskeyWalletInputThe origin of the wallet, and replacements of its requests.

getOrganigramRemotePasskeyWalletConnection

Read the remembered connection to the wallet.

Signature

getOrganigramRemotePasskeyWalletConnection: () => OrganigramRemotePasskeyWalletConnection | null

Returns

TypeDescription
OrganigramRemotePasskeyWalletConnection | nullThe connection, or null if none is valid.

hydrateOrganigramRemoteWalletConnection

Connect wagmi to the wallet for an address the app already knows, without opening the wallet popup.

Remembers the connection, then connects the wallet connector, which restores it. If connecting fails, the connection is forgotten.

Signature

hydrateOrganigramRemoteWalletConnection: <TConnector extends HydrateOrganigramRemoteWalletConnectionConnector>(input: HydrateOrganigramRemoteWalletConnectionOptions<TConnector>) => Promise<void>

rememberOrganigramRemotePasskeyWalletConnection

Remember a connection to the wallet in local storage.

Signature

rememberOrganigramRemotePasskeyWalletConnection: (input: {
address: `0x${string}`;
chainId: number;
}) => void

Parameters

NameTypeDescription
input{ address: `0x${string}`; chainId: number; }The connected address and chain.

organigramRemotePasskeyWalletId

Identifier of the wagmi connector of the wallet, organigramPasskeyWalletId.

Definition

const organigramRemotePasskeyWalletId = "organigram-passkeys"

PasskeyWalletConnectPopupInput

Input of requestPasskeyWalletConnect().

Definition

export type PasskeyWalletConnectPopupInput = PasskeyWalletRemotePopupInput;

PasskeyWalletConnectRequest

A request to connect an app to the wallet, which answers with the selected address.

Definition

export type PasskeyWalletConnectRequest = PasskeyWalletRemoteRequestBase & {
type: typeof passkeyWalletConnectType;
};

PasskeyWalletConnectResult

The answer to a connect request, with the address the user selected.

Definition

export type PasskeyWalletConnectResult = PasskeyWalletRemoteResultBase & {
type: typeof passkeyWalletConnectResultType;
};

PasskeyWalletDecryptPayloadRequest

A request to decrypt a text encrypted for the origin of the app.

Definition

export type PasskeyWalletDecryptPayloadRequest = PasskeyWalletRemoteRequestBase & {
type: typeof passkeyWalletDecryptPayloadType;
address: `0x${string}`;
encryptedPayload: WalletEncryptedPayload;
};

PasskeyWalletDecryptPayloadResult

The answer to a decrypt payload request.

Definition

export type PasskeyWalletDecryptPayloadResult = PasskeyWalletRemoteResultBase & {
type: typeof passkeyWalletDecryptPayloadResultType;
plaintext: string;
};

PasskeyWalletEncryptPayloadRequest

A request to encrypt a text with the wallet encryption key, for the origin of the app.

Definition

export type PasskeyWalletEncryptPayloadRequest = PasskeyWalletRemoteRequestBase & {
type: typeof passkeyWalletEncryptPayloadType;
address: `0x${string}`;
plaintext: string;
};

PasskeyWalletEncryptPayloadResult

The answer to an encrypt payload request.

Definition

export type PasskeyWalletEncryptPayloadResult = PasskeyWalletRemoteResultBase & {
type: typeof passkeyWalletEncryptPayloadResultType;
encryptedPayload: WalletEncryptedPayload;
};

PasskeyWalletRemoteErrorResult

The answer to a request the wallet could not serve.

Definition

export type PasskeyWalletRemoteErrorResult = Omit<PasskeyWalletRemoteResultBase, 'address'> & {
type: typeof passkeyWalletRemoteErrorResultType;
address?: undefined;
error: string;
};

PasskeyWalletRemotePopupInput

Common input of the requests to the wallet popup.

Definition

export type PasskeyWalletRemotePopupInput = {
walletOrigin?: string;
appOrigin?: string;
chainId: number;
requestId?: string;
requestedAt?: string;
approvalStep?: number;
approvalCount?: number;
approvalLabel?: string;
};

PasskeyWalletRemoteRequest

A request an app can send to the wallet.

Definition

export type PasskeyWalletRemoteRequest = PasskeyWalletConnectRequest | PasskeyWalletSignMessageRequest | PasskeyWalletSignTypedDataRequest | PasskeyWalletSendTransactionRequest | PasskeyWalletEncryptPayloadRequest | PasskeyWalletDecryptPayloadRequest;

PasskeyWalletRemoteRequestPayloadMessage

The message by which an app posts a request to the wallet popup.

Definition

export type PasskeyWalletRemoteRequestPayloadMessage = {
type: typeof passkeyWalletRemoteRequestPayloadType;
version: 1;
request: PasskeyWalletRemoteRequest;
};

PasskeyWalletRemoteRequestType

Types of the requests an app can send to the wallet.

Definition

export type PasskeyWalletRemoteRequestType = typeof passkeyWalletConnectType | typeof passkeyWalletSignMessageType | typeof passkeyWalletSignTypedDataType | typeof passkeyWalletSendTransactionType | typeof passkeyWalletEncryptPayloadType | typeof passkeyWalletDecryptPayloadType;

PasskeyWalletRemoteResult

An answer of the wallet to a request.

Definition

export type PasskeyWalletRemoteResult = PasskeyWalletConnectResult | PasskeyWalletSignMessageResult | PasskeyWalletSignTypedDataResult | PasskeyWalletSendTransactionResult | PasskeyWalletEncryptPayloadResult | PasskeyWalletDecryptPayloadResult | PasskeyWalletRemoteErrorResult;

PasskeyWalletSendTransactionRequest

A request to sign and send a transaction.

Definition

export type PasskeyWalletSendTransactionRequest = PasskeyWalletRemoteRequestBase & {
type: typeof passkeyWalletSendTransactionType;
address: `0x${string}`;
transaction: PasskeyWalletTransaction;
};

PasskeyWalletSendTransactionResult

The answer to a send transaction request.

Definition

export type PasskeyWalletSendTransactionResult = PasskeyWalletRemoteResultBase & {
type: typeof passkeyWalletSendTransactionResultType;
transactionHash: `0x${string}`;
};

PasskeyWalletSignMessagePopupInput

Input of requestPasskeyWalletSignMessage().

Definition

export type PasskeyWalletSignMessagePopupInput = PasskeyWalletRemotePopupInput & {
address: `0x${string}`;
message: string;
};

PasskeyWalletSignMessageRequest

A request to sign a message with EIP-191 personal_sign.

Definition

export type PasskeyWalletSignMessageRequest = PasskeyWalletRemoteRequestBase & {
type: typeof passkeyWalletSignMessageType;
address: `0x${string}`;
message: string;
};

PasskeyWalletSignMessageResult

The answer to a sign message request.

Definition

export type PasskeyWalletSignMessageResult = PasskeyWalletRemoteResultBase & {
type: typeof passkeyWalletSignMessageResultType;
message: string;
signature: `0x${string}`;
};

PasskeyWalletSignTypedDataPopupInput

Input of a request to sign typed data.

Definition

export type PasskeyWalletSignTypedDataPopupInput = PasskeyWalletRemotePopupInput & {
address: `0x${string}`;
typedData: PasskeyWalletTypedData;
};

PasskeyWalletSignTypedDataRequest

A request to sign EIP-712 typed data.

Definition

export type PasskeyWalletSignTypedDataRequest = PasskeyWalletRemoteRequestBase & {
type: typeof passkeyWalletSignTypedDataType;
address: `0x${string}`;
typedData: PasskeyWalletTypedData;
};

PasskeyWalletSignTypedDataResult

The answer to a sign typed data request.

Definition

export type PasskeyWalletSignTypedDataResult = PasskeyWalletRemoteResultBase & {
type: typeof passkeyWalletSignTypedDataResultType;
typedData: PasskeyWalletTypedData;
signature: `0x${string}`;
};

PasskeyWalletTransaction

A transaction to send, with quantities in hex.

Definition

export type PasskeyWalletTransaction = {
from: `0x${string}`;
to: `0x${string}`;
data?: `0x${string}`;
value?: `0x${string}`;
gas?: `0x${string}`;
gasPrice?: `0x${string}`;
maxFeePerGas?: `0x${string}`;
maxPriorityFeePerGas?: `0x${string}`;
nonce?: `0x${string}`;
};

PasskeyWalletTypedData

EIP-712 typed data.

Definition

export type PasskeyWalletTypedData = {
domain: Record<string, unknown>;
types: Record<string, ReadonlyArray<{
name: string;
type: string;
}>>;
primaryType: string;
message: Record<string, unknown>;
};

assertPasskeyWalletSignMessageResult

Check that a signed message comes from the expected address and chain.

Recovers the signer from the signature, so that a forged answer is rejected. This only works for EOA signatures, which the wallet produces.

Signature

assertPasskeyWalletSignMessageResult: (input: {
result: PasskeyWalletSignMessageResult;
address: `0x${string}`;
chainId: number;
message: string;
}) => Promise<void>

Parameters

NameTypeDescription
input{ result: PasskeyWalletSignMessageResult; address: `0x${string}`; chainId: number; message: string; }The answer, and the address, chain and message of the request.

assertPasskeyWalletSignTypedDataResult

Check that signed typed data comes from the expected address and chain.

Recovers the signer from the signature, so that a forged answer is rejected.

Signature

assertPasskeyWalletSignTypedDataResult: (input: {
result: PasskeyWalletSignTypedDataResult;
address: `0x${string}`;
chainId: number;
typedData: PasskeyWalletTypedData;
}) => Promise<void>

Parameters

NameTypeDescription
input{ result: PasskeyWalletSignTypedDataResult; address: `0x${string}`; chainId: number; typedData: PasskeyWalletTypedData; }The answer, and the address, chain and typed data of the request.

buildPasskeyWalletRemotePopupUrl

Build the URL of the wallet popup for a request.

Every request other than connect is then delivered by postMessage. The wallet reads the requesting origin from the browser-authenticated MessageEvent instead of trusting the appOrigin written in the URL, and no message, transaction or secret lands in a URL.

Signature

buildPasskeyWalletRemotePopupUrl: (input: PasskeyWalletRemotePopupInput & {
type: PasskeyWalletRemoteRequestType;
address?: `0x${string}`;
}) => URL

Parameters

NameTypeDescription
inputPasskeyWalletRemotePopupInput & { type: PasskeyWalletRemoteRequestType; address?: `0x${string}`; }The request, with its type and address.

isPasskeyWalletRemoteRequestPayloadMessage

Check that a posted message is a valid request to the wallet.

The domain of the request must be the host of its appOrigin. The wallet must still compare appOrigin with the origin of the MessageEvent, which the browser authenticates.

Signature

isPasskeyWalletRemoteRequestPayloadMessage: (value: unknown, expectedRequestId?: string) => value is PasskeyWalletRemoteRequestPayloadMessage

Parameters

NameTypeDescription
valueunknownThe data of the message.
expectedRequestIdstringThe identifier of the expected request, if known.

isPasskeyWalletTransaction

Check that a value is a transaction with valid addresses and hex quantities.

Signature

isPasskeyWalletTransaction: (value: unknown) => value is PasskeyWalletTransaction

Parameters

NameTypeDescription
valueunknownThe value to check.

parsePasskeyWalletRemoteRequest

Read and check a request from the URL parameters of the wallet popup.

Rejects encrypt and decrypt requests, which must be delivered by postMessage, and requests whose domain is not the host of their appOrigin.

Signature

parsePasskeyWalletRemoteRequest: (params: URLSearchParams) => PasskeyWalletRemoteRequest

Parameters

NameTypeDescription
paramsURLSearchParamsThe URL parameters.

parsePasskeyWalletTypedData

Read typed data.

Signature

parsePasskeyWalletTypedData: (value: unknown) => PasskeyWalletTypedData

Parameters

NameTypeDescription
valueunknownThe typed data, or its JSON.

requestPasskeyWalletConnect

Ask the wallet, in a popup, to connect the app.

Signature

requestPasskeyWalletConnect: (input: PasskeyWalletConnectPopupInput & {
timeoutMs?: number;
}) => Promise<PasskeyWalletConnectResult>

Parameters

NameTypeDescription
inputPasskeyWalletConnectPopupInput & { timeoutMs?: number; }The request, and a timeout, 5 minutes by default.

Returns

TypeDescription
Promise<PasskeyWalletConnectResult>The answer, with the address the user selected.

requestPasskeyWalletDecryptPayload

Ask the wallet, in a popup, to decrypt a text encrypted for the origin of the app.

Signature

requestPasskeyWalletDecryptPayload: (input: PasskeyWalletRemotePopupInput & {
address: `0x${string}`;
encryptedPayload: WalletEncryptedPayload;
timeoutMs?: number;
}) => Promise<PasskeyWalletDecryptPayloadResult>

Parameters

NameTypeDescription
inputPasskeyWalletRemotePopupInput & { address: `0x${string}`; encryptedPayload: WalletEncryptedPayload; timeoutMs?: number; }The request, the address, the encrypted payload, and a timeout, 5 minutes by default.

Returns

TypeDescription
Promise<PasskeyWalletDecryptPayloadResult>The answer, with the decrypted text.

requestPasskeyWalletEncryptPayload

Ask the wallet, in a popup, to encrypt a text for the origin of the app.

Signature

requestPasskeyWalletEncryptPayload: (input: PasskeyWalletRemotePopupInput & {
address: `0x${string}`;
plaintext: string;
timeoutMs?: number;
}) => Promise<PasskeyWalletEncryptPayloadResult>

Parameters

NameTypeDescription
inputPasskeyWalletRemotePopupInput & { address: `0x${string}`; plaintext: string; timeoutMs?: number; }The request, the address, the text, and a timeout, 5 minutes by default.

Returns

TypeDescription
Promise<PasskeyWalletEncryptPayloadResult>The answer, with the encrypted payload.

requestPasskeyWalletSignMessage

Ask the wallet, in a popup, to sign a message.

The answer is checked with assertPasskeyWalletSignMessageResult().

Signature

requestPasskeyWalletSignMessage: (input: PasskeyWalletSignMessagePopupInput & {
timeoutMs?: number;
}) => Promise<PasskeyWalletSignMessageResult>

Parameters

NameTypeDescription
inputPasskeyWalletSignMessagePopupInput & { timeoutMs?: number; }The request, the address, the message, and a timeout, 5 minutes by default.

Returns

TypeDescription
Promise<PasskeyWalletSignMessageResult>The answer, with the signature.

stringifyPasskeyWalletTypedData

Serialize typed data in JSON, writing bigints as decimal strings.

Signature

stringifyPasskeyWalletTypedData: (typedData: PasskeyWalletTypedData) => string

Parameters

NameTypeDescription
typedDataPasskeyWalletTypedDataThe typed data.

passkeyWalletConnectResultType

Type of the answer to a connect request.

Definition

const passkeyWalletConnectResultType = "organigram:wallet:connect-result"

passkeyWalletConnectType

Type of a request to connect an app to the wallet.

Definition

const passkeyWalletConnectType = "organigram:wallet:connect"

passkeyWalletDecryptPayloadResultType

Type of the answer to a decrypt payload request.

Definition

const passkeyWalletDecryptPayloadResultType = "organigram:wallet:decrypt-payload-result"

passkeyWalletDecryptPayloadType

Type of a request to decrypt a text with the wallet encryption key.

Definition

const passkeyWalletDecryptPayloadType = "organigram:wallet:decrypt-payload"

passkeyWalletEncryptPayloadResultType

Type of the answer to an encrypt payload request.

Definition

const passkeyWalletEncryptPayloadResultType = "organigram:wallet:encrypt-payload-result"

passkeyWalletEncryptPayloadType

Type of a request to encrypt a text with the wallet encryption key.

Definition

const passkeyWalletEncryptPayloadType = "organigram:wallet:encrypt-payload"

passkeyWalletRemoteErrorResultType

Type of the answer to a request the wallet could not serve.

Definition

const passkeyWalletRemoteErrorResultType = "organigram:wallet:remote-error"

passkeyWalletRemoteRequestPayloadType

Type of the message by which an app posts a request to the wallet popup.

Definition

const passkeyWalletRemoteRequestPayloadType = "organigram:wallet:request-payload"

passkeyWalletSendTransactionResultType

Type of the answer to a send transaction request.

Definition

const passkeyWalletSendTransactionResultType = "organigram:wallet:send-transaction-result"

passkeyWalletSendTransactionType

Type of a request to send a transaction.

Definition

const passkeyWalletSendTransactionType = "organigram:wallet:send-transaction"

passkeyWalletSignMessageResultType

Type of the answer to a sign message request.

Definition

const passkeyWalletSignMessageResultType = "organigram:wallet:sign-message-result"

passkeyWalletSignMessageType

Type of a request to sign a message.

Definition

const passkeyWalletSignMessageType = "organigram:wallet:sign-message"

passkeyWalletSignTypedDataResultType

Type of the answer to a sign typed data request.

Definition

const passkeyWalletSignTypedDataResultType = "organigram:wallet:sign-typed-data-result"

passkeyWalletSignTypedDataType

Type of a request to sign EIP-712 typed data.

Definition

const passkeyWalletSignTypedDataType = "organigram:wallet:sign-typed-data"

getAcceptedWalletOrigins

The origins whose messages an app expecting walletOrigin accepts.

Signature

getAcceptedWalletOrigins: (walletOrigin: string) => string[]

getLocalWalletOrigin

The local copy of walletOrigin, if it has one.

Signature

getLocalWalletOrigin: (walletOrigin: string) => string | null

isLocalWalletOriginOf

Only an HTTPS subdomain of the wallet host can unlock its passkeys.

Signature

isLocalWalletOriginOf: (localWalletOrigin: string, walletOrigin: string) => boolean

postToWalletPopup

Sends a message to the wallet popup, whichever accepted origin it shows: the browser only delivers it to the one the popup is on.

Signature

postToWalletPopup: (popup: Window, message: unknown, walletOrigin: string) => void

rememberWalletOrigin

Remembers which of the accepted origins answered for walletOrigin.

Signature

rememberWalletOrigin: (walletOrigin: string, answeringOrigin: string) => void

toPreferredWalletUrl

Opens requests on the local copy once it has answered on this device, so that the official wallet is no longer loaded at all.

Signature

toPreferredWalletUrl: (url: URL) => URL

watchLocalWalletPopup

Brings the popup back to the wallet when its local copy does not answer, for instance because it is not running. Returns the cleanup.

Signature

watchLocalWalletPopup: (input: {
popup: Window;
popupUrl: URL;
walletOrigin: string;
requestId: string;
timeoutMs?: number;
}) => (() => void)

localWalletOptOutParam

Asks a wallet to serve a request itself.

Definition

const localWalletOptOutParam = "localWallet"

officialLocalWalletOrigin

Origin of the local copy of the official wallet.

A wallet can have a local copy: the same wallet run on the user's own machine, served under a subdomain of the wallet host, so that it unlocks the same passkeys. An app that trusts the wallet then also trusts that copy. The official wallet's copy is pnpm dev local-organigram; another deployment declares its own through NEXT_PUBLIC_ORGANIGRAM_PASSKEY_WALLET_LOCAL_URL.

Definition

const officialLocalWalletOrigin = "https://local.wallet.organigram.ai:3002"

officialWalletOrigin

Origin of the official Organigram passkey wallet.

Definition

const officialWalletOrigin = "https://wallet.organigram.ai"

passkeyWalletLocalHandoffType

Posted by a wallet before it hands a request to its local copy.

Definition

const passkeyWalletLocalHandoffType = "organigram:wallet:local-handoff"

passkeyWalletReadyType

Posted by the local copy's request page as soon as it loads.

Definition

const passkeyWalletReadyType = "organigram:wallet:ready"

Sign in

Users sign in to an app with Sign-In with Ethereum. The app issues a message with a nonce from its server, the wallet signs it in a popup, and the server verifies the signature, the domain and the nonce before opening a session. With NextAuth, signInWithPasskeyWallet() runs the whole flow in the browser, and authorizePasskeyWalletCredentials() verifies the result in a credentials provider.

PasskeyWalletPopupInput

Input of a sign-in request through the wallet popup.

Definition

export type PasskeyWalletPopupInput = {
walletOrigin?: string;
appOrigin?: string;
nonce: string;
chainId: number;
requestId?: string;
requestedAt?: string;
popup?: Window | null;
};

PasskeyWalletStackSessionChangedMessage

A message posted by the wallet to the app that opened it when the user revokes the session of that app.

Definition

export type PasskeyWalletStackSessionChangedMessage = {
type: typeof passkeyWalletStackSessionChangedType;
version: 1;
event: 'revoked';
appOrigin: string;
address: `0x${string}` | null;
};

SignInWithPasskeyWalletInput

Input of signInWithPasskeyWallet().

Definition

export type SignInWithPasskeyWalletInput = {
chainId?: number;
walletOrigin?: string;
getCsrfToken: () => Promise<string | null | undefined>;
requestWalletSignIn?: (input: WalletSignInInput) => Promise<PasskeyWalletSignInResult>;
submitCredentials?: SubmitCredentials;
};

buildPasskeyWalletSignInPopupUrl

Build the URL of the wallet popup for a sign-in request, with the challenge URL /api/auth/wallet/challenge of the app.

Signature

buildPasskeyWalletSignInPopupUrl: (input: PasskeyWalletPopupInput) => URL

Parameters

NameTypeDescription
inputPasskeyWalletPopupInputThe sign-in request.

createPasskeyWalletStackSessionChangedMessage

Create a session change message.

Signature

createPasskeyWalletStackSessionChangedMessage: (input: {
event: "revoked";
appOrigin: string;
address?: `0x${string}` | null;
}) => PasskeyWalletStackSessionChangedMessage

Parameters

NameTypeDescription
input{ event: "revoked"; appOrigin: string; address?: `0x${string}` | null; }The event, the origin of the app and the address of the session.

notifyCurrentTabNextAuthSessionChanged

Tell NextAuth in the current tab that the session changed, so that it reloads it.

Signature

notifyCurrentTabNextAuthSessionChanged: () => void

notifyPasskeyWalletStackSessionChanged

Post a session change message to the window that opened the wallet.

Signature

notifyPasskeyWalletStackSessionChanged: (input: {
event: "revoked";
appOrigin: string;
address?: `0x${string}` | null;
targetOrigin?: string;
}) => void

Parameters

NameTypeDescription
input{ event: "revoked"; appOrigin: string; address?: `0x${string}` | null; targetOrigin?: string; }The event, the origin of the app, the address of the session, and the origin to post to, the app origin by default.

parsePasskeyWalletStackSessionChangedMessage

Read a session change message.

Also check the origin of the MessageEvent carrying it: only trust messages from the wallet.

Signature

parsePasskeyWalletStackSessionChangedMessage: (value: unknown) => PasskeyWalletStackSessionChangedMessage | null

Parameters

NameTypeDescription
valueunknownThe data of the message.

Returns

TypeDescription
PasskeyWalletStackSessionChangedMessage | nullThe message, or null if it is invalid.

requestPasskeyWalletSignIn

Ask the wallet, in a popup, to sign a Sign-In with Ethereum message for the app.

Only accepts answers from the wallet origin and its local copy, for this request. The result is not verified: the server must verify it with verifyPasskeyWalletSiwe().

Signature

requestPasskeyWalletSignIn: (input: PasskeyWalletPopupInput & {
timeoutMs?: number;
}) => Promise<PasskeyWalletSignInResult>

Parameters

NameTypeDescription
inputPasskeyWalletPopupInput & { timeoutMs?: number; }The sign-in request, and a timeout, 5 minutes by default.

Returns

TypeDescription
Promise<PasskeyWalletSignInResult>The signed message.

signInWithPasskeyWallet

Sign in to a NextAuth app with the wallet.

Opens the wallet popup, uses the CSRF token as the nonce of the Sign-In with Ethereum message, and submits the signed message to the NextAuth credentials provider, which verifies it with authorizePasskeyWalletCredentials().


import { getCsrfToken } from 'next-auth/react'
await signInWithPasskeyWallet({ getCsrfToken })

Signature

signInWithPasskeyWallet: (input: SignInWithPasskeyWalletInput) => Promise<PasskeyWalletSignInResult>

Parameters

NameTypeDescription
inputSignInWithPasskeyWalletInputThe CSRF token getter, the chain and the wallet origin.

Returns

TypeDescription
Promise<PasskeyWalletSignInResult>The signed message.

passkeyWalletStackSessionChangedType

Type of the message telling an app that its session changed in the wallet.

Definition

const passkeyWalletStackSessionChangedType = "organigram:wallet:stack-session-changed"

submitPasskeyWalletCredentials

Submit a signed message to the NextAuth credentials callback of the app, at /api/auth/callback/credentials.

Definition

const submitPasskeyWalletCredentials: SubmitCredentials

AuthorizePasskeyWalletCredentialsInput

Input of authorizePasskeyWalletCredentials().

Definition

export type AuthorizePasskeyWalletCredentialsInput = {
credentials: PasskeyWalletNextAuthCredentials | undefined;
domain: string;
nonce: string | null | undefined;
chain?: Chain;
transportUrl: string;
transportOptions?: Parameters<typeof import('viem').http>[1];
};

PasskeyWalletNextAuthCredentials

Credentials submitted to the NextAuth credentials provider.

Definition

export type PasskeyWalletNextAuthCredentials = {
message?: unknown;
signature?: unknown;
csrfToken?: unknown;
};

authorizePasskeyWalletCredentials

Verify the credentials submitted to a NextAuth credentials provider by signInWithPasskeyWallet().

Rejects credentials without message, signature or nonce, then verifies them with verifyPasskeyWalletSiwe(). The nonce is the CSRF token of the session, which signInWithPasskeyWallet() puts in the message.


CredentialsProvider({
credentials: { message: {}, signature: {} },
authorize: async credentials =>
await authorizePasskeyWalletCredentials({
credentials,
domain: 'app.example.com',
nonce: csrfToken,
transportUrl: rpcUrl
})
})

Signature

authorizePasskeyWalletCredentials: (input: AuthorizePasskeyWalletCredentialsInput) => Promise<PasskeyWalletVerifiedSiwe | null>

Parameters

NameTypeDescription
inputAuthorizePasskeyWalletCredentialsInputThe credentials, the domain and nonce of the app, and an RPC endpoint.

Returns

TypeDescription
Promise<PasskeyWalletVerifiedSiwe | null>The verified sign-in, or null.

PasskeyWalletChallenge

A Sign-In with Ethereum message issued by an app for the wallet to sign.

Definition

export type PasskeyWalletChallenge = {
domain: string;
nonce: string;
issuedAt: string;
expiresAt: string;
message: string;
};

PasskeyWalletVerifiedSiwe

A verified Sign-In with Ethereum message.

Definition

export type PasskeyWalletVerifiedSiwe = {
address: `0x${string}`;
chainId: number;
domain: string;
uri: string;
nonce: string;
encryptionPublicKeyResource: WalletEncryptionPublicKeySiweResource | null;
};

PasskeyWalletVerifySiweInput

Input of verifyPasskeyWalletSiwe().

Definition

export type PasskeyWalletVerifySiweInput = {
message: string;
signature: Hex;
domain: string;
nonce: string;
chain?: Chain;
transportUrl: string;
transportOptions?: Parameters<typeof http>[1];
};

buildPasskeyWalletPopupUrl

Build the URL of the wallet popup for a sign-in request.

Signature

buildPasskeyWalletPopupUrl: (input: {
walletOrigin: string;
domain: string;
challengeUrl: string;
requestId?: string;
requestedAt?: string;
returnUrl?: string;
}) => URL

Parameters

NameTypeDescription
input{ walletOrigin: string; domain: string; challengeUrl: string; requestId?: string; requestedAt?: string; returnUrl?: string; }The origin of the wallet, the domain of the app, its challenge URL, and optionally the request identifier, date and return URL.

createPasskeyWalletChallenge

Create a Sign-In with Ethereum message for an address, valid for 5 minutes.

Store the nonce on the server, for example in the session, and check it with verifyPasskeyWalletSiwe().

Signature

createPasskeyWalletChallenge: (input: {
request: Request;
address: `0x${string}`;
chainId: number;
encryptionPublicKeyResource?: string;
nonce?: string;
now?: Date;
statement?: string;
uri?: string;
}) => PasskeyWalletChallenge

Parameters

NameTypeDescription
input{ request: Request; address: `0x${string}`; chainId: number; encryptionPublicKeyResource?: string; nonce?: string; now?: Date; statement?: string; uri?: string; }The request, the address and chain, and optionally the public key resource from createWalletEncryptionPublicKeySiweResource(), a nonce, the current date, a statement and the URI of the app, the origin of the request by default.

createPasskeyWalletManifest

Create the manifest of an app, with the challenge URL /api/auth/wallet/challenge on the origin of the request.

Signature

createPasskeyWalletManifest: (request: Request) => PasskeyWalletManifest

Parameters

NameTypeDescription
requestRequestThe request for the manifest.

getPasskeyWalletCorsAllowedOrigins

List the wallet origins allowed to call the sign-in endpoints of an app.

Allows the official wallet and its local copy, the configured wallet and its local copy, other configured origins, and, when the app runs on localhost, the wallet served on port 3002 of the same host.

Signature

getPasskeyWalletCorsAllowedOrigins: (input: {
request: Request;
walletUrl?: string;
allowedOrigins?: string;
}) => string[]

Parameters

NameTypeDescription
input{ request: Request; walletUrl?: string; allowedOrigins?: string; }The request, the URL of the configured wallet, and other allowed origins, separated by commas.

getPasskeyWalletCorsOrigin

Get the origin of a request if it is an allowed wallet origin.

Signature

getPasskeyWalletCorsOrigin: (input: {
request: Request;
walletUrl?: string;
allowedOrigins?: string;
}) => string | null

Parameters

NameTypeDescription
input{ request: Request; walletUrl?: string; allowedOrigins?: string; }The request, the URL of the configured wallet, and other allowed origins, separated by commas.

Returns

TypeDescription
string | nullThe origin, or null.

getPasskeyWalletRequestOrigin

Get the public origin of a request, from the X-Forwarded-Host and X-Forwarded-Proto headers when present.

Clients can set these headers. Only rely on the result behind a proxy that overwrites them.

Signature

getPasskeyWalletRequestOrigin: (request: Request) => string

Parameters

NameTypeDescription
requestRequestThe request.

isWalletSignInOriginAllowed

Check whether an origin is one of a list of allowed origins.

Signature

isWalletSignInOriginAllowed: (origin: string, allowedOrigins: readonly string[]) => boolean

Parameters

NameTypeDescription
originstringThe origin to check.
allowedOriginsreadonly string[]The allowed origins.

verifyPasskeyWalletSiwe

Verify a Sign-In with Ethereum message signed by the wallet.

Checks the signature, the domain, the nonce and the validity dates of the message with viem. Never pass an empty domain or nonce: viem then skips their check. Check that the nonce is the one the app issued for this session, and use it only once.


const verified = await verifyPasskeyWalletSiwe({
message,
signature,
domain: 'app.example.com',
nonce: session.nonce,
transportUrl: rpcUrl
})

Signature

verifyPasskeyWalletSiwe: (input: PasskeyWalletVerifySiweInput) => Promise<PasskeyWalletVerifiedSiwe | null>

Parameters

NameTypeDescription
inputPasskeyWalletVerifySiweInputThe message, signature, expected domain and nonce, and an RPC endpoint.

Returns

TypeDescription
Promise<PasskeyWalletVerifiedSiwe | null>The verified sign-in, or null if the message is invalid.

withPasskeyWalletCorsHeaders

Add CORS headers with credentials to a response, if the request comes from an allowed wallet origin.

Signature

withPasskeyWalletCorsHeaders: <Response extends {
headers: Headers;
}>(request: Request, response: Response, options?: {
allowedMethods?: string;
walletUrl?: string;
allowedOrigins?: string;
}) => Response

Parameters

NameTypeDescription
requestRequestThe request.
responseResponseThe response, returned with its headers changed.
options{ allowedMethods?: string; walletUrl?: string; allowedOrigins?: string; }Allowed methods, GET, OPTIONS by default, the URL of the configured wallet, and other allowed origins.

PasskeyWalletErrorResult

The answer of the wallet to a request it could not serve.

Definition

export type PasskeyWalletErrorResult = {
type: typeof passkeyWalletErrorType;
version: 1;
requestId: string;
code: 'user_rejected' | 'challenge_unavailable' | 'domain_mismatch' | 'wallet_locked' | 'unsupported_origin' | 'internal_error';
message: string;
};

PasskeyWalletManifest

How an app accepts sign-ins with the wallet.

Definition

export type PasskeyWalletManifest = {
version: 1;
domain: string;
signInChallengeUrl: string;
signInCompletionUrl?: string;
};

PasskeyWalletSignInRequest

A request to sign in to an app with the wallet, read from the URL of the wallet popup.

Definition

export type PasskeyWalletSignInRequest = {
type: typeof passkeyWalletSignInType;
version: 1;
requestId: string;
domain: string;
challengeUrl: string;
returnUrl?: string;
requestedAt: string;
};

PasskeyWalletSignInResult

The answer of the wallet to a sign-in request: a signed Sign-In with Ethereum message.

Definition

export type PasskeyWalletSignInResult = {
type: typeof passkeyWalletSignInResultType;
version: 1;
requestId: string;
domain: string;
address: `0x${string}`;
message: string;
signature: `0x${string}`;
completedAt: string;
};

assertSignInDomainMatches

Check that a Sign-In with Ethereum message is for the expected domain, before signing it.

Signature

assertSignInDomainMatches: (message: string, expectedDomain: string) => void

Parameters

NameTypeDescription
messagestringThe message.
expectedDomainstringThe domain of the request.

parsePasskeyWalletSignInRequest

Read and check a sign-in request from the URL parameters of the wallet popup.

The challenge URL must use HTTPS and belong to the requested domain or one of its subdomains, and the return URL must use HTTPS, so that the wallet only signs messages fetched from the app asking for them.

Signature

parsePasskeyWalletSignInRequest: (params: URLSearchParams) => PasskeyWalletSignInRequest

Parameters

NameTypeDescription
paramsURLSearchParamsThe URL parameters.

resolveSiweDomain

Read the domain of a Sign-In with Ethereum message from its first line.

Signature

resolveSiweDomain: (message: string) => string

Parameters

NameTypeDescription
messagestringThe message.

validatePasskeyWalletManifest

Check the manifest of an app.

The domain of the manifest must be the host serving it, and its URLs must use HTTPS and belong to that domain or its subdomains.

Signature

validatePasskeyWalletManifest: (manifest: unknown, servingHost: string) => PasskeyWalletManifest

Parameters

NameTypeDescription
manifestunknownThe manifest, parsed from JSON.
servingHoststringThe host the manifest was fetched from.

passkeyWalletErrorType

Type of the message answering a request the wallet could not serve.

Definition

const passkeyWalletErrorType = "organigram:wallet:error"

passkeyWalletSignInResultType

Type of the message answering a sign-in request.

Definition

const passkeyWalletSignInResultType = "organigram:wallet:sign-in-result"

passkeyWalletSignInType

Type of a sign-in request to the wallet.

Definition

const passkeyWalletSignInType = "organigram:wallet:sign-in"

Passkey vaults

The wallet is a standard Ethereum account derived from a BIP-39 recovery phrase. The recovery phrase is encrypted in a vault with AES-256-GCM, under a key derived from the PRF output of a WebAuthn passkey, so that only that passkey can decrypt it. A wallet can have several passkeys, each with its own vault. These functions run in the wallet app: an unlocked wallet holds the recovery phrase and keys in memory, so keep it there only as long as needed, and never store, send or log it.

BrowserPasskeyWalletVaultPayload

The secrets encrypted in a passkey vault.

Holds the recovery phrase in clear: never store, send or log it outside an encrypted envelope.

Definition

export type BrowserPasskeyWalletVaultPayload = {
version: 1;
recoveryPhrase: string;
walletEncryptionKey?: WalletEncryptionKeyPairPayload;
};

PasskeyPrfCredential

A passkey to evaluate with its PRF salt.

Definition

export type PasskeyPrfCredential = {
credentialId: string;
transports?: string[];
prfSalt: string;
};

createBrowserPasskeyWalletVaultPayload

Create the payload of a new vault, with a new wallet encryption key pair.

Signature

createBrowserPasskeyWalletVaultPayload: (recoveryPhrase: string) => Promise<BrowserPasskeyWalletVaultPayload>

Parameters

NameTypeDescription
recoveryPhrasestringThe recovery phrase of the wallet.

createGeneratedRecoveryPhrase

Generate a new 12-word English BIP-39 recovery phrase.

Signature

createGeneratedRecoveryPhrase: () => string

decryptVaultPayloadWithPrfOutput

Decrypt a vault envelope with the PRF output of its passkey.

Does not zero the PRF output, so that the caller may try several envelopes with it. Zero it once done.

Signature

decryptVaultPayloadWithPrfOutput: (input: {
envelope: PasskeyVaultEnvelopeData;
prfOutput: Uint8Array;
}) => Promise<BrowserPasskeyWalletVaultPayload>

Parameters

NameTypeDescription
input{ envelope: PasskeyVaultEnvelopeData; prfOutput: Uint8Array; }The envelope and the PRF output.

derivePasskeyWalletAccount

Switch an unlocked wallet to another of its accounts.

Signature

derivePasskeyWalletAccount: (input: {
wallet: UnlockedPasskeyWallet;
account: StoredVaultAccount;
}) => UnlockedPasskeyWallet

Parameters

NameTypeDescription
input{ wallet: UnlockedPasskeyWallet; account: StoredVaultAccount; }The unlocked wallet and the account to select.

deriveStoredVaultAccount

Derive the account of an address index from a recovery phrase.

Signature

deriveStoredVaultAccount: (input: {
recoveryPhrase: string;
addressIndex: number;
name: string;
}) => StoredVaultAccount

Parameters

NameTypeDescription
input{ recoveryPhrase: string; addressIndex: number; name: string; }The recovery phrase, the address index, and the name of the account, Account <index + 1> by default.

getDerivationPath

Get the Ethereum BIP-44 derivation path of an address index.

Signature

getDerivationPath: (addressIndex: number) => string

Parameters

NameTypeDescription
addressIndexnumberThe address index.

hexEncodeMessage

Encode a UTF-8 message in hex, as personal_sign requests carry it.

Signature

hexEncodeMessage: (message: string) => `0x${string}`

Parameters

NameTypeDescription
messagestringThe message.

parseBrowserPasskeyWalletVaultPayload

Read a decrypted vault payload.

Signature

parseBrowserPasskeyWalletVaultPayload: (plaintext: string) => BrowserPasskeyWalletVaultPayload

Parameters

NameTypeDescription
plaintextstringThe decrypted payload, in JSON.

registerAdditionalBrowserPasskeyVault

Create another passkey able to unlock an unlocked wallet, for example as a backup.

Signature

registerAdditionalBrowserPasskeyVault: (input: {
wallet: UnlockedPasskeyWallet;
name: string;
accounts: StoredVaultAccount[];
rpId?: string;
rpName?: string;
}) => Promise<{
record: StoredVaultRecord;
wallet: UnlockedPasskeyWallet;
}>

Parameters

NameTypeDescription
input{ wallet: UnlockedPasskeyWallet; name: string; accounts: StoredVaultAccount[]; rpId?: string; rpName?: string; }The unlocked wallet, the name of the passkey, the accounts it unlocks, and the WebAuthn relying party.

Returns

TypeDescription
Promise<{ record: StoredVaultRecord; wallet: UnlockedPasskeyWallet; }>The new vault record and the wallet, now attached to the new passkey.

registerBrowserPasskeyVault

Create a passkey and a vault encrypting a recovery phrase with it.

Asks the browser to create a discoverable passkey with user verification and the PRF extension. The recovery phrase is encrypted with a key derived from the PRF output, which is then zeroed. Store the returned record, which holds no secret in clear, and keep the unlocked wallet in memory only.


const { record, wallet } = await registerBrowserPasskeyVault({
recoveryPhrase: createGeneratedRecoveryPhrase(),
name: 'My wallet'
})

Signature

registerBrowserPasskeyVault: (input: {
recoveryPhrase: string;
name: string;
rpId?: string;
rpName?: string;
}) => Promise<{
record: StoredVaultRecord;
wallet: UnlockedPasskeyWallet;
}>

Parameters

NameTypeDescription
input{ recoveryPhrase: string; name: string; rpId?: string; rpName?: string; }The recovery phrase, the name of the wallet, the WebAuthn relying party identifier, the current host name by default, and the relying party name.

Returns

TypeDescription
Promise<{ record: StoredVaultRecord; wallet: UnlockedPasskeyWallet; }>The vault record and the unlocked wallet, on its first account.

requestPasskeyPrfOutput

Runs one user-verified assertion restricted to the given passkeys, each evaluated with its own PRF salt, and returns the PRF output of the passkey the person selected. The caller owns the returned PRF output and must zero it once its key is derived.

Signature

requestPasskeyPrfOutput: (input: {
credentials: PasskeyPrfCredential[];
rpId?: string;
}) => Promise<{
credentialId: string;
prfOutput: Uint8Array;
}>

signPersonalMessage

Sign a message with EIP-191 personal_sign.

Signature

signPersonalMessage: (input: {
wallet: UnlockedPasskeyWallet;
message: string;
}) => Promise<`0x${string}`>

Parameters

NameTypeDescription
input{ wallet: UnlockedPasskeyWallet; message: string; }The unlocked wallet and the message.

signTypedDataMessage

Sign EIP-712 typed data.

Signature

signTypedDataMessage: (input: {
wallet: UnlockedPasskeyWallet;
typedData: PasskeyWalletTypedData;
}) => Promise<`0x${string}`>

Parameters

NameTypeDescription
input{ wallet: UnlockedPasskeyWallet; typedData: PasskeyWalletTypedData; }The unlocked wallet and the typed data.

unlockBrowserPasskeyVault

Unlock a wallet with one of the passkeys of its vaults.

Asks the user to pick one of the passkeys of the records, with user verification. The vaults of that passkey are tried in turn, and the first whose recovery phrase derives the address of its record is unlocked. The PRF output is zeroed.


const { wallet } = await unlockBrowserPasskeyVault({ records })
const signature = await signPersonalMessage({ wallet, message: 'Hello' })

Signature

unlockBrowserPasskeyVault: (input: {
records: StoredVaultRecord[];
rpId?: string;
}) => Promise<{
record: StoredVaultRecord;
wallet: UnlockedPasskeyWallet;
}>

Parameters

NameTypeDescription
input{ records: StoredVaultRecord[]; rpId?: string; }The vault records to choose from, and the WebAuthn relying party identifier, the current host name by default.

Returns

TypeDescription
Promise<{ record: StoredVaultRecord; wallet: UnlockedPasskeyWallet; }>The record unlocked and the wallet, on the first account of the record.

updateStaticPasskeyVaultEncryptionKey

Unlock a vault and re-encrypt it with another wallet encryption key pair.

Asks the user for a passkey, and keeps the PRF salt of the vault. Store the returned record in place of the old one.

Signature

updateStaticPasskeyVaultEncryptionKey: (input: {
records: StoredVaultRecord[];
walletEncryptionKey: WalletEncryptionKeyPairPayload;
rpId?: string;
}) => Promise<{
record: StoredVaultRecord;
wallet: UnlockedPasskeyWallet;
}>

Parameters

NameTypeDescription
input{ records: StoredVaultRecord[]; walletEncryptionKey: WalletEncryptionKeyPairPayload; rpId?: string; }The vault records to choose from, the new key pair and the WebAuthn relying party identifier.

Returns

TypeDescription
Promise<{ record: StoredVaultRecord; wallet: UnlockedPasskeyWallet; }>The re-encrypted record and the unlocked wallet.

PasskeyVaultEnvelopeData

A secret encrypted with a key derived from a passkey, such as the recovery phrase of a wallet.

The envelope holds no secret: it can be stored anywhere, as only the passkey that created it can decrypt it.

Definition

export type PasskeyVaultEnvelopeData = {
algorithm: typeof passkeyVaultAlgorithm;
keyVersion: typeof passkeyVaultKeyVersion;
ciphertext: string;
salt: string;
prfSalt: string;
nonce: string;
};

base64UrlToBytes

Decode unpadded base64url.

Accepts canonical unpadded base64url only. Lenient decoders silently skip unknown characters, so a tampered value could otherwise decode to bytes other than the ones it appears to encode.

Signature

base64UrlToBytes: (value: string) => Uint8Array

Parameters

NameTypeDescription
valuestringThe value to decode.

bytesToBase64Url

Encode bytes in unpadded base64url.

Signature

bytesToBase64Url: (bytes: Uint8Array) => string

Parameters

NameTypeDescription
bytesUint8ArrayThe bytes to encode.

decryptPasskeyVaultSecret

Decrypt the secret of a vault envelope.

Signature

decryptPasskeyVaultSecret: (input: DecryptPasskeyVaultSecretInput) => Promise<string>

Parameters

NameTypeDescription
input~DecryptPasskeyVaultSecretInputThe envelope and the key derived for it.

derivePasskeyVaultKey

Derive the AES-GCM key of a vault from the PRF output of a passkey.

The key cannot be exported. A copy of the PRF output made for the import is zeroed afterwards, but not the original: zero it once you no longer need it.

Signature

derivePasskeyVaultKey: (input: DerivePasskeyVaultKeyInput) => Promise<CryptoKey>

Parameters

NameTypeDescription
input~DerivePasskeyVaultKeyInputThe 32-byte PRF output, the 32-byte salt of the vault, and an optional HKDF info that separates keys derived from the same PRF output for different purposes.

encryptPasskeyVaultSecret

Encrypt a secret with a vault key into a vault envelope.

Signature

encryptPasskeyVaultSecret: (input: EncryptPasskeyVaultSecretInput) => Promise<PasskeyVaultEnvelopeData>

Parameters

NameTypeDescription
input~EncryptPasskeyVaultSecretInputThe secret, the key from derivePasskeyVaultKey(), the salt it was derived with, the 32-byte PRF salt, and an optional 12-byte nonce, random by default. Never reuse a nonce with the same key.

getCrypto

Get the Web Crypto API of the environment.

Signature

getCrypto: () => Crypto

hasBase64UrlByteLength

Check that a value is canonical base64url whose decoded length passes a test.

Signature

hasBase64UrlByteLength: (value: unknown, isValidLength: (byteLength: number) => boolean) => boolean

Parameters

NameTypeDescription
valueunknownThe value to check.
isValidLength(byteLength: number) => booleanTest of the decoded length, in bytes.

isPasskeyPrfSupported

Check whether a newly created passkey enabled the PRF extension, which the wallet needs to derive its keys.

Signature

isPasskeyPrfSupported: (input: {
credential: PrfSupportCredential | null;
}) => Promise<boolean>

Parameters

NameTypeDescription
input{ credential: ~PrfSupportCredential | null; }The credential returned by navigator.credentials.create().

isPasskeyVaultEnvelope

Check that a value is a passkey vault envelope of the current algorithm and key version, with salts, nonce and ciphertext of valid lengths.

Signature

isPasskeyVaultEnvelope: (value: unknown) => value is PasskeyVaultEnvelopeData

Parameters

NameTypeDescription
valueunknownThe value to check.

randomBytes

Generate cryptographically secure random bytes.

Signature

randomBytes: (length: number) => Uint8Array

Parameters

NameTypeDescription
lengthnumberNumber of bytes.

toArrayBuffer

Copy the bytes of a view into a new ArrayBuffer.

Signature

toArrayBuffer: (bytes: Uint8Array) => ArrayBuffer

Parameters

NameTypeDescription
bytesUint8ArrayThe bytes to copy.

passkeyVaultAlgorithm

Algorithm of passkey vaults: AES-256-GCM with a key derived by HKDF-SHA-256 from the passkey PRF output.

Definition

const passkeyVaultAlgorithm = "AES-GCM-HKDF-SHA-256"

passkeyVaultKeyVersion

Version of the key derivation of passkey vaults.

Definition

const passkeyVaultKeyVersion = 2

textDecoder

Shared UTF-8 decoder.

Definition

const textDecoder: TextDecoder

textEncoder

Shared UTF-8 encoder.

Definition

const textEncoder: TextEncoder

PasskeyProviderEvent

Events emitted by the EIP-1193 provider of the wallet.

Definition

export type PasskeyProviderEvent = 'accountsChanged' | 'chainChanged' | 'disconnect';

PasskeyProviderListener

Listener of a provider event.

Definition

export type PasskeyProviderListener = (...args: unknown[]) => void;

PasskeyRegistrationResult

The address and passkey of a newly registered wallet.

Definition

export type PasskeyRegistrationResult = {
address: `0x${string}`;
credentialId: string;
};

UnlockedPasskeyWallet

A wallet unlocked with a passkey, ready to sign.

It holds the recovery phrase and the private keys in memory. Keep it in memory only for as long as needed, never store, send or log it.

Definition

export type UnlockedPasskeyWallet = {
address: `0x${string}`;
account: HDAccount;
recoveryPhrase: string;
walletEncryptionKey?: WalletEncryptionKeyPairPayload;
credentialId: string;
};

organigramPasskeyWalletIcon

Path of the icon of the passkey wallet, relative to the app serving it.

Definition

const organigramPasskeyWalletIcon = "/png/logo-gradient.png"

organigramPasskeyWalletId

Identifier of the passkey wallet connector.

Definition

const organigramPasskeyWalletId = "organigram-passkeys"

Vault records

A vault record holds a vault envelope with the public details needed to unlock it: the accounts, the passkey and its device. Records hold no secret in clear, so that they can be stored in the browser or exported. These functions read, list and update records.

StoredVaultAccount

An account of a wallet, derived from its recovery phrase.

Definition

export type StoredVaultAccount = {
address: `0x${string}`;
name: string;
addressIndex: number;
derivationPath: string;
};

StoredVaultRecord

A wallet vault encrypted for one passkey, with the public details needed to unlock it.

A wallet has one record per passkey that can unlock it. The record holds no secret in clear: only its passkey can decrypt the envelope.

Definition

export type StoredVaultRecord = {
version: 1;
address: `0x${string}`;
accounts: StoredVaultAccount[];
credentialId: string;
rawCredentialId?: string;
name: string;
transports: string[];
passkeyDevice: {
kind: 'macos' | 'ios' | 'ipados' | 'windows' | 'android' | 'linux' | 'security-key' | 'cross-device' | 'platform' | 'passkey';
label: string;
};
createdAt: string;
lastUsedAt: string | null;
envelope: PasskeyVaultEnvelopeData;
};

VaultRegistry

A list of vault records, as stored or exported.

Definition

export type VaultRegistry = {
version: 1;
vaults: StoredVaultRecord[];
};

addAccountToVaultRecords

Add an account to every vault of the wallet that holds another account.

Signature

addAccountToVaultRecords: (input: {
vaults: StoredVaultRecord[];
sourceAddress: `0x${string}`;
account: StoredVaultAccount;
}) => StoredVaultRecord[]

Parameters

NameTypeDescription
input{ vaults: StoredVaultRecord[]; sourceAddress: `0x${string}`; account: StoredVaultAccount; }The vault records, an account the wallet already holds, and the account to add.

createVaultRegistry

Wrap vault records in a registry.

Signature

createVaultRegistry: (vaults: StoredVaultRecord[]) => VaultRegistry

Parameters

NameTypeDescription
vaultsStoredVaultRecord[]The vault records.

isStoredVaultRecord

Check that a value is a valid vault record.

Signature

isStoredVaultRecord: (value: unknown) => value is StoredVaultRecord

Parameters

NameTypeDescription
valueunknownThe value to check.

markVaultRecordUsed

Set the date of the last unlock of a vault.

Signature

markVaultRecordUsed: (input: {
vaults: StoredVaultRecord[];
credentialId: string;
usedAt?: string;
}) => StoredVaultRecord[]

Parameters

NameTypeDescription
input{ vaults: StoredVaultRecord[]; credentialId: string; usedAt?: string; }The vault records, the identifier of the passkey, and the date, now by default.

parseVaultRegistry

Read a vault registry, dropping invalid records.

Signature

parseVaultRegistry: (value: unknown) => VaultRegistry

Parameters

NameTypeDescription
valueunknownThe registry, parsed from JSON.

removeAccountFromVaultRecords

Remove an account from every vault, and the vaults left without accounts.

This only removes local records. Without a remaining vault or exported recovery phrase, the account can no longer be unlocked.

Signature

removeAccountFromVaultRecords: (input: {
vaults: StoredVaultRecord[];
address: `0x${string}`;
}) => StoredVaultRecord[]

Parameters

NameTypeDescription
input{ vaults: StoredVaultRecord[]; address: `0x${string}`; }The vault records and the address of the account.

removeVaultRecord

Remove the vault record of a passkey.

Signature

removeVaultRecord: (input: {
vaults: StoredVaultRecord[];
credentialId: string;
}) => StoredVaultRecord[]

Parameters

NameTypeDescription
input{ vaults: StoredVaultRecord[]; credentialId: string; }The vault records and the identifier of the passkey.

removeVaultRecords

Remove the vault records of several passkeys.

Signature

removeVaultRecords: (input: {
vaults: StoredVaultRecord[];
credentialIds: Set<string>;
}) => StoredVaultRecord[]

Parameters

NameTypeDescription
input{ vaults: StoredVaultRecord[]; credentialIds: Set<string>; }The vault records and the identifiers of the passkeys.

serializeVaultRegistry

Serialize vault records as a registry in JSON.

Signature

serializeVaultRegistry: (vaults: StoredVaultRecord[], space?: number) => string

Parameters

NameTypeDescription
vaultsStoredVaultRecord[]The vault records.
spacenumberIndentation of the JSON.

upsertVaultRecord

Add a vault record, or replace the record of the same passkey, and put it first.

Signature

upsertVaultRecord: (input: {
vaults: StoredVaultRecord[];
record: StoredVaultRecord;
}) => StoredVaultRecord[]

Parameters

NameTypeDescription
input{ vaults: StoredVaultRecord[]; record: StoredVaultRecord; }The vault records and the record to add.

vaultHasAccount

Check whether a vault holds an account, ignoring the case of the address.

Signature

vaultHasAccount: (vault: StoredVaultRecord, address: string) => boolean

Parameters

NameTypeDescription
vaultStoredVaultRecordThe vault record.
addressstringThe address of the account.

VaultRegistryGroup

Vault records holding the same accounts, usually the passkeys of one wallet.

Definition

export type VaultRegistryGroup = {
id: string;
accounts: StoredVaultAccount[];
passkeys: StoredVaultRecord[];
createdAt: string;
};

WalletGroup

An account with the passkeys able to unlock it.

Definition

export type WalletGroup = {
address: `0x${string}`;
name: string;
addressIndex: number;
derivationPath: string;
passkeys: StoredVaultRecord[];
};

formatAddress

Shorten an address for display, as 0x1234...abcd.

Signature

formatAddress: (address: string) => string

Parameters

NameTypeDescription
addressstringThe address.

formatCredentialId

Shorten a passkey identifier longer than 18 characters for display.

Signature

formatCredentialId: (credentialId: string) => string

Parameters

NameTypeDescription
credentialIdstringThe passkey identifier.

getNextAddressIndex

Get the address index of the next account to derive: one more than the highest index of the records, or 0.

Signature

getNextAddressIndex: (records: StoredVaultRecord[]) => number

Parameters

NameTypeDescription
recordsStoredVaultRecord[]The vault records.

getVaultAccount

Find an account of a vault record, ignoring the case of the address.

Signature

getVaultAccount: (vault: StoredVaultRecord, address: string) => StoredVaultAccount | null

Parameters

NameTypeDescription
vaultStoredVaultRecordThe vault record.
addressstringThe address of the account.

Returns

TypeDescription
StoredVaultAccount | nullThe account, or null.

getVaultRegistryGroupId

Identify the set of accounts of a vault record, by address index and address.

Signature

getVaultRegistryGroupId: (vault: StoredVaultRecord) => string

Parameters

NameTypeDescription
vaultStoredVaultRecordThe vault record.

groupVaultRecordsBySeed

Group vault records holding the same accounts, oldest first.

Signature

groupVaultRecordsBySeed: (vaults: StoredVaultRecord[]) => VaultRegistryGroup[]

Parameters

NameTypeDescription
vaultsStoredVaultRecord[]The vault records.

groupVaultsByWallet

Group vault records by account, ordered by address index.

Signature

groupVaultsByWallet: (vaults: StoredVaultRecord[]) => WalletGroup[]

Parameters

NameTypeDescription
vaultsStoredVaultRecord[]The vault records.

mergeVaults

Merge imported vault records into local ones, newest first.

An imported record replaces the local record of the same passkey.

Signature

mergeVaults: (localVaults: StoredVaultRecord[], importedVaults: StoredVaultRecord[]) => StoredVaultRecord[]

Parameters

NameTypeDescription
localVaultsStoredVaultRecord[]The local records.
importedVaultsStoredVaultRecord[]The imported records.

Backups

Vault records can be backed up on a server. Each backup exposes only its vault envelope and passkey identifier: the rest of the record is encrypted with a key derived from the recovery phrase, so that the server learns nothing beyond the wallet address, and any passkey of the wallet can restore every backup.

RemoteVaultBackupPackage

The backups of a wallet, as exported or restored.

Definition

export type RemoteVaultBackupPackage = {
version: 1;
address: `0x${string}`;
backups: RemoteVaultBackupRecord[];
exportedAt: string;
};

RemoteVaultBackupRecord

A vault record encrypted for backup on a server.

A backup exposes its vault envelope, which only its own passkey can decrypt, with the identifier of that passkey. The rest of the vault record (account addresses, names, devices) is encrypted with a key derived from the recovery phrase, so that any passkey of the wallet can restore every backup while the server learns nothing beyond the wallet address.

Definition

export type RemoteVaultBackupRecord = {
version: 2;
backupId: string;
credentialId: string;
envelope: PasskeyVaultEnvelopeData;
salt: string;
iv: string;
ciphertext: string;
createdAt: string;
};

createRemoteVaultBackupPackage

Wrap the backups of a wallet in a package.

Signature

createRemoteVaultBackupPackage: (input: {
address: `0x${string}`;
backups: RemoteVaultBackupRecord[];
}) => RemoteVaultBackupPackage

Parameters

NameTypeDescription
input{ address: `0x${string}`; backups: RemoteVaultBackupRecord[]; }The address of the wallet and its backups.

createRemoteVaultBackupRecord

Encrypt a vault record for backup.

The backup key is derived with HKDF from a SHA-256 hash of the recovery phrase, a random salt, and the address. The recovery phrase itself is never part of the backup.

Signature

createRemoteVaultBackupRecord: (input: {
address: `0x${string}`;
record: StoredVaultRecord;
recoveryPhrase: string;
}) => Promise<RemoteVaultBackupRecord>

Parameters

NameTypeDescription
input{ address: `0x${string}`; record: StoredVaultRecord; recoveryPhrase: string; }The address of the wallet, the vault record and the recovery phrase of the wallet.

createVaultBackupDigest

Hash backups with SHA-256, over a JSON serialization with sorted keys.

Signature

createVaultBackupDigest: (backups: RemoteVaultBackupRecord[]) => Promise<string>

Parameters

NameTypeDescription
backupsRemoteVaultBackupRecord[]The backups.

Returns

TypeDescription
Promise<string>The digest, in hex.

createVaultBackupSignatureMessage

Build the message the wallet signs to authorize storing backups.

Signature

createVaultBackupSignatureMessage: (input: {
address: `0x${string}`;
vaultCount: number;
digest: string;
}) => string

Parameters

NameTypeDescription
input{ address: `0x${string}`; vaultCount: number; digest: string; }The address of the wallet, the number of backups and their digest from createVaultBackupDigest().

decryptRemoteVaultBackupRecord

Decrypt a vault record from a backup.

The result is not validated: check it with isStoredVaultRecord() before use.

Signature

decryptRemoteVaultBackupRecord: (input: {
address: `0x${string}`;
backup: RemoteVaultBackupRecord;
recoveryPhrase: string;
}) => Promise<StoredVaultRecord>

Parameters

NameTypeDescription
input{ address: `0x${string}`; backup: RemoteVaultBackupRecord; recoveryPhrase: string; }The address of the wallet, the backup and the recovery phrase of the wallet.

normalizeBackupAddress

Check and checksum an address typed by a user to restore backups.

Signature

normalizeBackupAddress: (value: string) => `0x${string}`

Parameters

NameTypeDescription
valuestringThe address.

parseRemoteVaultBackupPackage

Read a backup package.

Signature

parseRemoteVaultBackupPackage: (value: unknown) => RemoteVaultBackupPackage

Parameters

NameTypeDescription
valueunknownThe package, parsed from JSON.

End-to-end encryption

Each wallet has an ECDH P-256 key pair, stored in its vaults, to encrypt files and texts that only it can decrypt. Its public key can be shared with an app in a Sign-In with Ethereum resource. Texts encrypted for an app are bound to the origin of that app, so that another app cannot have them decrypted.

WalletDecryptedFile

A decrypted file.

Definition

export type WalletDecryptedFile = {
name: string;
type: string;
bytes: Uint8Array;
lastModified?: number;
};

WalletEncryptedFilePackage

A file encrypted for the wallet that encrypted it.

Definition

export type WalletEncryptedFilePackage = {
version: 1;
kind: 'organigram:wallet-encrypted-file';
algorithm: 'ECDH-P256+A256GCM';
recipient: {
address: `0x${string}`;
publicKey: JsonWebKey;
keyVersion: number;
};
file: {
name: string;
type: string;
size: number;
lastModified?: number;
};
iv: string;
ciphertext: string;
encryptedAt: string;
};

WalletEncryptedPayload

A text encrypted by a wallet for itself, which only one application origin may have decrypted.

Definition

export type WalletEncryptedPayload = {
version: 1;
kind: 'organigram:wallet-encrypted-payload';
algorithm: 'ECDH-P256+A256GCM';
appOrigin: string;
recipient: {
address: `0x${string}`;
publicKey: JsonWebKey;
keyVersion: number;
};
iv: string;
ciphertext: string;
encryptedAt: string;
};

WalletEncryptionKeyPairPayload

The ECDH P-256 key pair of a wallet for end-to-end encryption, stored in its passkey vaults.

Holds the private key in clear: keep it in the encrypted vault and in memory only, never store, send or log it elsewhere.

Definition

export type WalletEncryptionKeyPairPayload = {
version: 1;
algorithm: 'ECDH-P256';
privateKey: JsonWebKey;
publicKey: JsonWebKey;
keyVersion: number;
};

WalletEncryptionPublicKeySiweResource

The public key of a wallet, read from a Sign-In with Ethereum resource.

Definition

export type WalletEncryptionPublicKeySiweResource = {
kind: typeof encryptionPublicKeyResourceKind;
address: string;
publicKey: JsonWebKey;
keyVersion: number;
};

createWalletEncryptionKeyPair

Generate a new ECDH P-256 key pair for a wallet.

The private key is exportable, so that it can be stored in the passkey vaults of the wallet. Keep it there.

Signature

createWalletEncryptionKeyPair: () => Promise<WalletEncryptionKeyPairPayload>

createWalletEncryptionPublicKeySiweResource

Encode the public key of a wallet as a resource of a Sign-In with Ethereum message.

Signing in with this resource lets the application learn the public key of the wallet, bound to its address by the signature.

Signature

createWalletEncryptionPublicKeySiweResource: (input: {
address: `0x${string}`;
keyPair: WalletEncryptionKeyPairPayload;
}) => string

Parameters

NameTypeDescription
input{ address: `0x${string}`; keyPair: WalletEncryptionKeyPairPayload; }The address of the wallet and its key pair. Only the public key is encoded.

decryptFileWithWalletEncryptionKey

Decrypt a file encrypted with encryptFileWithWalletEncryptionKey().

Signature

decryptFileWithWalletEncryptionKey: (input: {
encryptedPackage: WalletEncryptedFilePackage;
keyPair: WalletEncryptionKeyPairPayload;
}) => Promise<WalletDecryptedFile>

Parameters

NameTypeDescription
input{ encryptedPackage: WalletEncryptedFilePackage; keyPair: WalletEncryptionKeyPairPayload; }The encrypted package and the key pair of the wallet.

decryptPayloadWithWalletEncryptionKey

Decrypt a text encrypted with encryptPayloadWithWalletEncryptionKey().

Signature

decryptPayloadWithWalletEncryptionKey: (input: {
encryptedPayload: WalletEncryptedPayload;
appOrigin: string;
keyPair: WalletEncryptionKeyPairPayload;
}) => Promise<string>

Parameters

NameTypeDescription
input{ encryptedPayload: WalletEncryptedPayload; appOrigin: string; keyPair: WalletEncryptionKeyPairPayload; }The encrypted payload, the origin of the application asking for it, and the key pair of the wallet.

encryptFileWithWalletEncryptionKey

Encrypt a file so that only the wallet holding the key pair can decrypt it.

The AES-GCM key is derived by ECDH between the private and public keys of the same key pair. The name, type, size and date of the file stay in clear and are not authenticated with the ciphertext.

Signature

encryptFileWithWalletEncryptionKey: (input: {
address: `0x${string}`;
file: File;
keyPair: WalletEncryptionKeyPairPayload;
}) => Promise<WalletEncryptedFilePackage>

Parameters

NameTypeDescription
input{ address: `0x${string}`; file: File; keyPair: WalletEncryptionKeyPairPayload; }The address of the wallet, the file and the key pair.

encryptPayloadWithWalletEncryptionKey

Encrypt a text so that only the wallet holding the key pair can decrypt it, for one application origin.

The application origin, the address and the key version are authenticated with the ciphertext, so that the payload cannot be decrypted for another application or relabelled for it.

Signature

encryptPayloadWithWalletEncryptionKey: (input: {
address: `0x${string}`;
appOrigin: string;
plaintext: string;
keyPair: WalletEncryptionKeyPairPayload;
}) => Promise<WalletEncryptedPayload>

Parameters

NameTypeDescription
input{ address: `0x${string}`; appOrigin: string; plaintext: string; keyPair: WalletEncryptionKeyPairPayload; }The address of the wallet, the origin of the application, the text and the key pair.

parseWalletEncryptedFilePackage

Read an encrypted file package.

Signature

parseWalletEncryptedFilePackage: (value: unknown) => WalletEncryptedFilePackage

Parameters

NameTypeDescription
valueunknownThe package, parsed from JSON.

parseWalletEncryptedPayload

Read an encrypted payload.

Signature

parseWalletEncryptedPayload: (value: unknown) => WalletEncryptedPayload

Parameters

NameTypeDescription
valueunknownThe payload, parsed from JSON.

parseWalletEncryptionKeyPair

Read a wallet encryption key pair.

Signature

parseWalletEncryptionKeyPair: (value: unknown) => WalletEncryptionKeyPairPayload

Parameters

NameTypeDescription
valueunknownThe key pair, parsed from JSON.

parseWalletEncryptionPublicKeySiweResource

Read the public key of a wallet from the resources of a Sign-In with Ethereum message.

Only trust the result once the signature of the message is verified.

Signature

parseWalletEncryptionPublicKeySiweResource: (input: {
address: string;
resources: string[] | undefined;
}) => WalletEncryptionPublicKeySiweResource | null

Parameters

NameTypeDescription
input{ address: string; resources: string[] | undefined; }The address that signed the message, and the resources of the message.

Returns

TypeDescription
WalletEncryptionPublicKeySiweResource | nullThe public key, or null if no valid resource of this address is found.

Edit this page

On this page:

Web3 identity, solved
Unlock and sign with passkeys

Follow us:

© 2026 Organigram SAS

All Rights Reserved.