> ## Documentation Index
> Fetch the complete documentation index at: https://docs.portalhq.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Wallet lifecycle

> Portal's wallet model — one wallet per client — and how its signing and backup shares change state.

## One client, one wallet

In Portal, a **client** is the identity associated with a wallet's MPC key shares, and each client has **exactly one wallet**. One wallet covers every supported chain: a single `generate` produces key shares on two curves — `SECP256K1` (EVM, Bitcoin, Tron) and `ED25519` (Solana, Stellar) — and each chain's address is derived from the curve it uses. Read them from the SDKs via `portal.addresses`, or from the API via the [Get a client](/api-reference/clients/get-a-client) endpoint.

You cannot generate a second wallet for a client that already has one. If you call `generate` for a client that already has a wallet, Portal rejects it with HTTP `400 Wallet Already Exists` rather than creating new shares. Treat this as expected — read the client's existing addresses via `portal.addresses` instead of regenerating.

### Supporting multiple wallets per user

One client is one wallet — but your end users aren't limited to one wallet. To give a user more than one wallet (for example a **treasury wallet** and an **operational wallet**), create **one client per wallet** and store which clients belong to that user in your own system.

Each client is independent: its own shares, its own backup and recovery, and its own addresses.

```mermaid theme={null}
flowchart LR
  U["Your end user"] --> C1["Portal Client A<br/>(treasury)"]
  U --> C2["Portal Client B<br/>(operational)"]
  C1 --> W1["Wallet A"]
  C2 --> W2["Wallet B"]
```

To add a wallet for an existing user, create another client with [Create a client](/api-reference/clients/create-a-client) and run the normal generate flow on the new client. Don't call generate again on a client that already has a wallet.

## MPC operations

There are 4 core MPC operations that can take place with your wallet:

* Generate
* Backup
* Recover
* Sign

The first 3 (Generate, Backup, Recover) are all *state changing* operations that affect either the set of **signing shares** or **backup shares**. Let's explore the details of each one.

### **Generate**

Creates new signing shares from randomly generated values.

<Note>
  This only affects **signing shares.**
</Note>

### Steps

When you call `portal.createWallet()` two steps happen under the hood.

1. **Create Signing Shares -** After this completes the custodian signing share will have been successfully stored in Portal's database and the client signing share is stored on the Portal client's device. This updates the signing share status to `STORED_DATABASE`.
2. **Store Client Signing Share to Keychain -** After the WebSocket successfully closes the client *signing* share is written to the device keychain and the SDK notifies the server that it succeeded. This updates the *signing* shares status to `STORED_CLIENT`.

### **Backup**

Creates new backup shares using the signing shares.

<Note>
  This only affects **backup shares.**
</Note>

### Steps

These are the steps of what happens when you call `portal.backupWallet()`.

1. **Create Backup Shares -** After this completes the custodian backup share is saved in Portal's backend (if you are using [Self-Managed Backups](./self-managed-backups), the custodian backup share is then saved in your backend via the configured webhook). This updates the backup share pair's status to `STORED_CUSTODIAN_BACKUP_SHARE`.
2. **Encrypt Client Backup Share** - The client backup share is encrypted on the client-side and the encryption key is uploaded to the client's cloud storage.
3. **Store Client Backup Share** - By default, Portal will store the encrypted client backup share for you. (If you are using Self-Managed Backups, you are required to store the encrypted client backup share and, if successful, call `portal.api.storedClientBackupShare()` to notify our backend that the client backup share was successfully stored.) This updates the backup shares status to `STORED_CLIENT_BACKUP_SHARE`.

<Danger>
  If backup fails you are expected to handle the error (see the [Error Reference](./error-codes)) and re-run backup after handling it. Recover cannot be run until Backup succeeds.
</Danger>

### **Recover**

Creates new signing shares using the backup shares.

<Danger>
  This only affects **signing shares.**
</Danger>

### Steps

These are the steps of what happens when you call `portal.recoverWallet()`.

1. **Create Signing Shares -** After this completes the custodian signing share will have been successfully updated in Portal's database and the client signing share is available on the client's device.
2. **Store Client Signing Share to Keychain -** After the WebSocket successfully closes the client signing share is written to the client's device's keychain. The SDK notifies the server that it succeeded. This updates the signing shares status to `STORED_CLIENT`.

<Danger>
  If recovery fails you are expected to handle the error (see the [Error Reference](./error-codes)) and re-run recovery after handling it. Signing functionality will not work until a successful recover on a device that does not have a signing share in its keychain.
</Danger>
