Skip to content
Merged
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
136 changes: 104 additions & 32 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- [listWallets](#listwallets)
- [useWallet](#usewallet)
- [createWallet](#createwallet)
- [getIdToken](#getidtoken)
- [signMessage](#signmessage)
- [signTypedData](#signtypeddata)
- [isValidMessageSignature](#isvalidmessagesignature)
Expand All @@ -41,6 +42,8 @@
- [StorageManager](#storagemanager)
- [CredentialSigner](#credentialsigner)
- [OmsWallet](#omswallet)
- [PendingWalletSelection](#pendingwalletselection)
- [WalletSelectionBehavior](#walletselectionbehavior)
- [WalletCredential](#walletcredential)
- [AccessGrant](#accessgrant)
- [ListAccessParams](#listaccessparams)
Expand Down Expand Up @@ -113,7 +116,7 @@ new OMSClient(params: {
oms.supportedNetworks: readonly Network[]
```

Returns the supported network registry. Each entry has `id`, `name`, `nativeTokenSymbol`, and `explorerUrl`.
Returns the supported network registry. Each entry has `id`, `name`, `nativeTokenSymbol`, `explorerUrl`, and `displayName`.

## WalletClient

Expand Down Expand Up @@ -180,26 +183,26 @@ await oms.wallet.startEmailAuth({ email: 'user@example.com' })
completeEmailAuth(params: {
code: string
walletType?: WalletType
autoActivate?: boolean
walletSelection?: 'automatic' | 'manual'
}): Promise<
| { walletAddress: Address; wallet: OmsWallet; wallets: OmsWallet[]; credential: WalletCredential }
| { wallets: OmsWallet[]; credential: WalletCredential }
| PendingWalletSelection
>
```

Verifies the OTP code and activates a wallet. Must be called after [`startEmailAuth`](#startemailauth).

This method verifies the code with a one-week WaaS session lifetime, loads all wallet pages, then automatically selects an existing wallet matching `walletType`, or creates a new one if none exists. Wallet metadata is persisted to storage. Pass `autoActivate: false` to return `{ wallets, credential }` without selecting or creating a wallet; then call [`useWallet`](#usewallet) or [`createWallet`](#createwallet).
This method verifies the code with a one-week WaaS session lifetime, loads all wallet pages, then automatically selects an existing wallet matching `walletType`, or creates a new one if none exists. Wallet metadata is persisted to storage. Pass `walletSelection: 'manual'` to return a [`PendingWalletSelection`](#pendingwalletselection) bound to the verified auth flow; complete selection through that object.

**Parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `code` | `string` | Yes | The one-time passcode entered by the user. |
| `walletType` | `WalletType` | No | The wallet type to load or create. Defaults to `WalletType.Ethereum`. |
| `autoActivate` | `boolean` | No | Defaults to `true`. Set to `false` to let the app choose an existing wallet or create a new one. |
| `walletSelection` | `'automatic' \| 'manual'` | No | Defaults to `'automatic'`. Set to `'manual'` to let the app choose an existing wallet or create one through the returned pending selection. |

**Returns** `Promise<{ walletAddress: Address; wallet: OmsWallet; wallets: OmsWallet[]; credential: WalletCredential }>` by default, or `Promise<{ wallets: OmsWallet[]; credential: WalletCredential }>` when `autoActivate` is `false`.
**Returns** `Promise<{ walletAddress: Address; wallet: OmsWallet; wallets: OmsWallet[]; credential: WalletCredential }>` by default, or `Promise<PendingWalletSelection>` when `walletSelection` is `'manual'`.

**Throws** if the code is incorrect, expired, or the network request fails.

Expand All @@ -214,6 +217,20 @@ try {
}
```

Manual selection:

```typescript
const selection = await oms.wallet.completeEmailAuth({
code: '123456',
walletType: WalletType.Ethereum,
walletSelection: 'manual',
})

await selection.selectWallet({ walletId: selection.wallets[0].id })
// or:
await selection.createAndSelectWallet({ reference: 'main' })
```

---

### startOidcRedirectAuth
Expand Down Expand Up @@ -252,14 +269,14 @@ completeOidcRedirectAuth(params: {
callbackUrl: string
cleanUrl?: boolean
replaceUrl?: (url: string) => void
autoActivate?: boolean
walletSelection?: 'automatic' | 'manual'
}): Promise<
| { walletAddress: Address; wallet: OmsWallet; wallets: OmsWallet[]; credential: WalletCredential }
| { wallets: OmsWallet[]; credential: WalletCredential }
| PendingWalletSelection
>
```

Completes an OIDC redirect flow by validating the persisted state nonce, exchanging the authorization code with WaaS using a one-week session lifetime, and activating an existing wallet or creating one. Pass `autoActivate: false` to return `{ wallets, credential }` for app-driven wallet selection. `cleanUrl` removes OAuth query parameters after successful completion; outside a browser, pass `replaceUrl`.
Completes an OIDC redirect flow by validating the persisted state nonce, exchanging the authorization code with WaaS using a one-week session lifetime, and activating an existing wallet or creating one. Pass `walletSelection: 'manual'` to return a [`PendingWalletSelection`](#pendingwalletselection) for app-driven wallet selection. `cleanUrl` removes OAuth query parameters after successful completion; outside a browser, pass `replaceUrl`.

```typescript
const { walletAddress, credential } = await oms.wallet.completeOidcRedirectAuth({
Expand All @@ -277,14 +294,14 @@ signInWithOidcRedirect(params: {
provider: string | OidcProviderConfig
redirectUri?: string
walletType?: WalletType
autoActivate?: boolean
walletSelection?: 'automatic' | 'manual'
relayRedirectUri?: string
authorizeParams?: Record<string, string>
cleanUrl?: boolean
currentUrl?: string
assignUrl?: (url: string) => void
replaceUrl?: (url: string) => void
}): Promise<{ walletAddress: Address; wallet: OmsWallet; wallets: OmsWallet[]; credential: WalletCredential } | { wallets: OmsWallet[]; credential: WalletCredential } | void>
}): Promise<{ walletAddress: Address; wallet: OmsWallet; wallets: OmsWallet[]; credential: WalletCredential } | PendingWalletSelection | void>
```

Browser convenience method for regular web apps. If the current URL has OIDC callback params, it completes auth and returns the same result as [`completeOidcRedirectAuth`](#completeoidcredirectauth). Otherwise it starts auth, redirects with `window.location.assign`, and returns `void`. For router-driven apps, prefer [`startOidcRedirectAuth`](#startoidcredirectauth) and [`completeOidcRedirectAuth`](#completeoidcredirectauth).
Expand Down Expand Up @@ -319,7 +336,7 @@ await oms.wallet.signOut()
listWallets(): Promise<OmsWallet[]>
```

Returns all wallets available to the authenticated credential. This can be used after completing auth with `autoActivate: false` to show a wallet picker.
Returns all wallets available to an authenticated active or pending wallet-selection session.

---

Expand All @@ -329,7 +346,7 @@ Returns all wallets available to the authenticated credential. This can be used
useWallet(params: { walletId: string }): Promise<{ walletAddress: Address; wallet: OmsWallet }>
```

Activates an existing wallet by server-side wallet id and persists it as the current wallet session.
Activates an existing wallet by server-side wallet id and persists it as the current wallet session. Requires an active wallet session; pending manual auth flows must use [`PendingWalletSelection.selectWallet`](#pendingwalletselection).

---

Expand All @@ -339,7 +356,29 @@ Activates an existing wallet by server-side wallet id and persists it as the cur
createWallet(params?: { type?: WalletType; reference?: string }): Promise<{ walletAddress: Address; wallet: OmsWallet }>
```

Creates a new wallet, activates it, and persists it as the current wallet session. `type` defaults to `WalletType.Ethereum`.
Creates a new wallet, activates it, and persists it as the current wallet session. Requires an active wallet session. `type` defaults to `WalletType.Ethereum`. Pending manual auth flows must use [`PendingWalletSelection.createAndSelectWallet`](#pendingwalletselection), which uses the auth-requested wallet type automatically.

---

### getIdToken

```typescript
getIdToken(params?: {
ttlSeconds?: number
customClaims?: Record<string, unknown>
}): Promise<string>
```

Requests an ID token for the active wallet session. The SDK uses the active wallet id automatically.

**Parameters**

| Name | Type | Description |
|---|---|---|
| `ttlSeconds` | `number` | Optional token lifetime in seconds. |
| `customClaims` | `Record<string, unknown>` | Optional custom claims to include in the token. |

**Returns** `Promise<string>` — the issued ID token.

---

Expand Down Expand Up @@ -718,6 +757,9 @@ type OmsSdkErrorCode =
| 'OMS_REQUEST_FAILED'
| 'OMS_AUTH_COMMITMENT_CONSUMED'
| 'OMS_SESSION_MISSING'
| 'OMS_WALLET_SELECTION_STALE'
| 'OMS_WALLET_SELECTION_UNAVAILABLE'
| 'OMS_WALLET_SELECTION_IN_FLIGHT'
| 'OMS_TRANSACTION_STATUS_LOOKUP_FAILED'
| 'OMS_VALIDATION_ERROR'
```
Expand All @@ -730,6 +772,7 @@ type OmsSdkErrorCode =
| `OmsRequestError` | Network, fetch, or non-2xx HTTP failures. |
| `OmsResponseError` | Invalid JSON or malformed API responses. |
| `OmsTransactionError` | Transaction was submitted but status polling failed; includes `txnId`. |
| `OmsWalletSelectionError` | Manual wallet selection is stale, invalid, or already processing an action. |
| `OmsValidationError` | SDK-side validation failures before a request is sent. |

Use `isOmsSdkError(err)` or `err instanceof OmsSdkError` to branch on structured error fields.
Expand All @@ -746,34 +789,36 @@ interface Network {
readonly name: string
readonly nativeTokenSymbol: string
readonly explorerUrl: string
readonly displayName: string
}
```

A supported OMS network entry. The SDK exports `Networks`, `supportedNetworks`, `findNetworkById(id)`, and `findNetworkByName(name)`.
`name` is the registry/routing slug for indexer URLs, while `displayName` is the user-facing label.

```typescript
findNetworkById(id: number): Network | undefined
findNetworkByName(name: string): Network | undefined
```

| Key | id | name | nativeTokenSymbol | explorerUrl |
|---|---:|---|---|---|
| `Networks.mainnet` | 1 | `mainnet` | `ETH` | `https://etherscan.io` |
| `Networks.sepolia` | 11155111 | `sepolia` | `ETH` | `https://sepolia.etherscan.io` |
| `Networks.polygon` | 137 | `polygon` | `POL` | `https://polygonscan.com` |
| `Networks.amoy` | 80002 | `amoy` | `POL` | `https://amoy.polygonscan.com` |
| `Networks.arbitrum` | 42161 | `arbitrum` | `ETH` | `https://arbiscan.io` |
| `Networks.arbitrumSepolia` | 421614 | `arbitrum-sepolia` | `ETH` | `https://sepolia.arbiscan.io` |
| `Networks.optimism` | 10 | `optimism` | `ETH` | `https://optimistic.etherscan.io` |
| `Networks.optimismSepolia` | 11155420 | `optimism-sepolia` | `ETH` | `https://sepolia-optimism.etherscan.io` |
| `Networks.base` | 8453 | `base` | `ETH` | `https://basescan.org` |
| `Networks.baseSepolia` | 84532 | `base-sepolia` | `ETH` | `https://sepolia.basescan.org` |
| `Networks.bsc` | 56 | `bsc` | `BNB` | `https://bscscan.com` |
| `Networks.bscTestnet` | 97 | `bsc-testnet` | `BNB` | `https://testnet.bscscan.com` |
| `Networks.arbitrumNova` | 42170 | `arbitrum-nova` | `ETH` | `https://nova.arbiscan.io` |
| `Networks.avalanche` | 43114 | `avalanche` | `AVAX` | `https://subnets.avax.network/c-chain` |
| `Networks.avalancheTestnet` | 43113 | `avalanche-testnet` | `AVAX` | `https://subnets-test.avax.network/c-chain` |
| `Networks.katana` | 747474 | `katana` | `ETH` | `https://katanascan.com` |
| Key | id | name | displayName | nativeTokenSymbol | explorerUrl |
|---|---:|---|---|---|---|
| `Networks.mainnet` | 1 | `mainnet` | `Ethereum` | `ETH` | `https://etherscan.io` |
| `Networks.sepolia` | 11155111 | `sepolia` | `Sepolia` | `ETH` | `https://sepolia.etherscan.io` |
| `Networks.polygon` | 137 | `polygon` | `Polygon` | `POL` | `https://polygonscan.com` |
| `Networks.amoy` | 80002 | `amoy` | `Polygon Amoy` | `POL` | `https://amoy.polygonscan.com` |
| `Networks.arbitrum` | 42161 | `arbitrum` | `Arbitrum` | `ETH` | `https://arbiscan.io` |
| `Networks.arbitrumSepolia` | 421614 | `arbitrum-sepolia` | `Arbitrum Sepolia` | `ETH` | `https://sepolia.arbiscan.io` |
| `Networks.optimism` | 10 | `optimism` | `Optimism` | `ETH` | `https://optimistic.etherscan.io` |
| `Networks.optimismSepolia` | 11155420 | `optimism-sepolia` | `Optimism Sepolia` | `ETH` | `https://sepolia-optimism.etherscan.io` |
| `Networks.base` | 8453 | `base` | `Base` | `ETH` | `https://basescan.org` |
| `Networks.baseSepolia` | 84532 | `base-sepolia` | `Base Sepolia` | `ETH` | `https://sepolia.basescan.org` |
| `Networks.bsc` | 56 | `bsc` | `BSC` | `BNB` | `https://bscscan.com` |
| `Networks.bscTestnet` | 97 | `bsc-testnet` | `BSC Testnet` | `BNB` | `https://testnet.bscscan.com` |
| `Networks.arbitrumNova` | 42170 | `arbitrum-nova` | `Arbitrum Nova` | `ETH` | `https://nova.arbiscan.io` |
| `Networks.avalanche` | 43114 | `avalanche` | `Avalanche` | `AVAX` | `https://subnets.avax.network/c-chain` |
| `Networks.avalancheTestnet` | 43113 | `avalanche-testnet` | `Avalanche Testnet` | `AVAX` | `https://subnets-test.avax.network/c-chain` |
| `Networks.katana` | 747474 | `katana` | `Katana` | `ETH` | `https://katanascan.com` |

### OmsEnvironment

Expand Down Expand Up @@ -886,6 +931,33 @@ Wallet metadata returned by auth and wallet listing APIs.

---

### PendingWalletSelection

```typescript
interface PendingWalletSelection {
walletType: WalletType
wallets: OmsWallet[]
credential: WalletCredential

selectWallet(params: { walletId: string }): Promise<WalletActivationResult>
createAndSelectWallet(params?: { reference?: string }): Promise<WalletActivationResult>
}
```

Returned by manual email or OIDC auth completion. The selection is bound to the verified auth flow and signer that created it. It can be used once to select one of the returned `wallets` or to create and select a new wallet of `walletType`.

---

### WalletSelectionBehavior

```typescript
type WalletSelectionBehavior = 'automatic' | 'manual'
```

Controls whether auth completion immediately activates a wallet or returns a [`PendingWalletSelection`](#pendingwalletselection).

---

### WalletCredential

```typescript
Expand Down
60 changes: 40 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ pnpm dev:example
## Quick Start

```typescript
import { Networks, OMSClient } from '@0xsequence/typescript-sdk'
import { Networks, OMSClient, WalletType } from '@0xsequence/typescript-sdk'
import { parseUnits } from 'viem'

const oms = new OMSClient({
Expand Down Expand Up @@ -121,6 +121,22 @@ Email OTP is a two-step flow:
1. **`startEmailAuth({ email })`** — clears any active session and sends a one-time code to the user's inbox.
2. **`completeEmailAuth({ code })`** — verifies the code, then automatically loads an existing wallet or creates a new one if none exists. Returns `{ walletAddress, wallet, wallets, credential }`.

Use manual wallet selection when the app needs to present wallet choices:

```typescript
const selection = await oms.wallet.completeEmailAuth({
code: '123456',
walletType: WalletType.Ethereum,
walletSelection: 'manual',
})

await selection.selectWallet({ walletId: selection.wallets[0].id })
// or:
await selection.createAndSelectWallet({ reference: 'main' })
```

The returned pending selection is bound to the verified auth flow and signer. Hold that object and complete selection through it instead of saving `{ wallets }` and later calling global wallet activation methods.

### OIDC Redirect Auth

Google redirect auth is configured on the default environment. The redirect auth APIs are provider-neutral, so custom environments can add or replace providers.
Expand Down Expand Up @@ -149,6 +165,8 @@ const { walletAddress, wallet, wallets, credential } = await oms.wallet.complete
})
```

OIDC redirect auth also supports manual wallet selection by passing `walletSelection: 'manual'` to `completeOidcRedirectAuth`.

For simple browser apps, use the one-call convenience method from a sign-in action and from the callback page:

```typescript
Expand All @@ -168,6 +186,8 @@ const walletAddress = oms.wallet.walletAddress
const { expiresAt, loginType, sessionEmail } = oms.wallet.session
```

Use `oms.wallet.getIdToken({ ttlSeconds, customClaims })` to request an ID token for the active wallet session.

Pending email OTP and OIDC redirect state are not exposed through `session`; use the auth method results to drive pending UI.

To end the session, call:
Expand All @@ -178,7 +198,7 @@ await oms.wallet.signOut()

## Networks

The SDK exports `Networks`, `supportedNetworks`, `findNetworkById(id)`, and `findNetworkByName(name)` for the networks currently configured by OMS. Each network has `id`, `name`, `nativeTokenSymbol`, and `explorerUrl`.
The SDK exports `Networks`, `supportedNetworks`, `findNetworkById(id)`, and `findNetworkByName(name)` for the networks currently configured by OMS. Each network has `id`, `name`, `nativeTokenSymbol`, `explorerUrl`, and `displayName`. `name` is the registry/routing slug, while `displayName` is the user-facing label.

The `network` parameter on all transaction and signing methods accepts a `Network` from the SDK registry:

Expand All @@ -191,24 +211,24 @@ console.log(supportedNetworks)
console.log(findNetworkById(80002)) // Networks.amoy
```

| Key | id | name | native token | explorerUrl |
|---|---:|---|---|---|
| `Networks.mainnet` | 1 | `mainnet` | ETH | `https://etherscan.io` |
| `Networks.sepolia` | 11155111 | `sepolia` | ETH | `https://sepolia.etherscan.io` |
| `Networks.polygon` | 137 | `polygon` | POL | `https://polygonscan.com` |
| `Networks.amoy` | 80002 | `amoy` | POL | `https://amoy.polygonscan.com` |
| `Networks.arbitrum` | 42161 | `arbitrum` | ETH | `https://arbiscan.io` |
| `Networks.arbitrumSepolia` | 421614 | `arbitrum-sepolia` | ETH | `https://sepolia.arbiscan.io` |
| `Networks.optimism` | 10 | `optimism` | ETH | `https://optimistic.etherscan.io` |
| `Networks.optimismSepolia` | 11155420 | `optimism-sepolia` | ETH | `https://sepolia-optimism.etherscan.io` |
| `Networks.base` | 8453 | `base` | ETH | `https://basescan.org` |
| `Networks.baseSepolia` | 84532 | `base-sepolia` | ETH | `https://sepolia.basescan.org` |
| `Networks.bsc` | 56 | `bsc` | BNB | `https://bscscan.com` |
| `Networks.bscTestnet` | 97 | `bsc-testnet` | BNB | `https://testnet.bscscan.com` |
| `Networks.arbitrumNova` | 42170 | `arbitrum-nova` | ETH | `https://nova.arbiscan.io` |
| `Networks.avalanche` | 43114 | `avalanche` | AVAX | `https://subnets.avax.network/c-chain` |
| `Networks.avalancheTestnet` | 43113 | `avalanche-testnet` | AVAX | `https://subnets-test.avax.network/c-chain` |
| `Networks.katana` | 747474 | `katana` | ETH | `https://katanascan.com` |
| Key | id | name | display name | native token | explorerUrl |
|---|---:|---|---|---|---|
| `Networks.mainnet` | 1 | `mainnet` | Ethereum | ETH | `https://etherscan.io` |
| `Networks.sepolia` | 11155111 | `sepolia` | Sepolia | ETH | `https://sepolia.etherscan.io` |
| `Networks.polygon` | 137 | `polygon` | Polygon | POL | `https://polygonscan.com` |
| `Networks.amoy` | 80002 | `amoy` | Polygon Amoy | POL | `https://amoy.polygonscan.com` |
| `Networks.arbitrum` | 42161 | `arbitrum` | Arbitrum | ETH | `https://arbiscan.io` |
| `Networks.arbitrumSepolia` | 421614 | `arbitrum-sepolia` | Arbitrum Sepolia | ETH | `https://sepolia.arbiscan.io` |
| `Networks.optimism` | 10 | `optimism` | Optimism | ETH | `https://optimistic.etherscan.io` |
| `Networks.optimismSepolia` | 11155420 | `optimism-sepolia` | Optimism Sepolia | ETH | `https://sepolia-optimism.etherscan.io` |
| `Networks.base` | 8453 | `base` | Base | ETH | `https://basescan.org` |
| `Networks.baseSepolia` | 84532 | `base-sepolia` | Base Sepolia | ETH | `https://sepolia.basescan.org` |
| `Networks.bsc` | 56 | `bsc` | BSC | BNB | `https://bscscan.com` |
| `Networks.bscTestnet` | 97 | `bsc-testnet` | BSC Testnet | BNB | `https://testnet.bscscan.com` |
| `Networks.arbitrumNova` | 42170 | `arbitrum-nova` | Arbitrum Nova | ETH | `https://nova.arbiscan.io` |
| `Networks.avalanche` | 43114 | `avalanche` | Avalanche | AVAX | `https://subnets.avax.network/c-chain` |
| `Networks.avalancheTestnet` | 43113 | `avalanche-testnet` | Avalanche Testnet | AVAX | `https://subnets-test.avax.network/c-chain` |
| `Networks.katana` | 747474 | `katana` | Katana | ETH | `https://katanascan.com` |

## Sending Transactions

Expand Down
Loading
Loading