> ## Documentation Index
> Fetch the complete documentation index at: https://sdk-guide.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Wallets

> Create, read, and manage personal MPC wallets with TrustodianSDK

Sending value lives in [Transactions](https://sdk-guide.mintlify.site/sdk-guide/transactions.md); pre-signing simulation lives in
[Preflight](https://sdk-guide.mintlify.site/sdk-guide/preflight.md).

## Prerequisites

Read-only calls (`getWallets`, `getWallet`, `getPortfolio`, `getTotalAssetValue`,
`getSupportedTokens`, `getWalletHistory`) need only the API key.

Anything that touches key material (`createWallet`, `importWallet`) additionally
needs a configured `shareStore` (omitting it throws `shareStore is required for SDK v2
wallet-password flows`), an unlocked wallet-password session, and `userRef` passed explicitly or
through `sdk.runWithUserRef(userRef, fn)`. See [Authentication](https://sdk-guide.mintlify.site/sdk-guide/authentication.md) for
unlocking a session and [Key Management](https://sdk-guide.mintlify.site/sdk-guide/key-management.md#the-share-store) for what
`shareStore` implements.

## Chain types

`EChainType` is the enum every wallet method takes. Its members are exactly the `ChainType`
string literals, so the two are interchangeable at the call site.

| Member | Value | Native currency | Decimals |
| - | - | - | - |
| `EChainType.EVM` | `"EVM"` | ETH | 18 |
| `EChainType.SOL` | `"SOL"` | SOL | 9 |
| `EChainType.TRON` | `"TRON"` | TRX | 6 |
| `EChainType.BTC` | `"BTC"` | BTC | 8 |
| `EChainType.BTC_T3` | `"BTC_T3"` | BTC | 8 |
| `EChainType.ADA` | `"ADA"` | ADA | 6 |
| `EChainType.XRP` | `"XRP"` | XRP | 6 |

> [!NOTE]
>
> Bitcoin has two chain types: `BTC` on mainnet and `BTC_T3` on testnet. The exported helper
> `getBtcChainType()` returns the right one for the network the SDK was constructed with.

Chain ids are the `ChainIdValue` union, always strings: the EVM chain id (`"1"`, `"8453"`, …) or a
named id per chain (`"sol_mainnet"`, `"tron_mainnet"`, `"BTC"`, `"ada_mainnet"`, `"xrp_mainnet"`).
Each has a testnet counterpart. The full set is in
[Supported chains](https://sdk-guide.mintlify.site/sdk-guide/introduction.md#supported-chains).

## Wallet scope

`WalletScope` is `'personal' | 'organization'`. It appears on every method that fetches key shares
or acts on an ownership-specific resource, and it selects which share partition the SDK reads.
It defaults to `'personal'` when omitted.

```typescript theme={null}
type WalletScope = 'personal' | 'organization';
```

> [!WARNING]
>
> `walletScope` is accepted by `sendTransaction`, `estimateFee`, `activateXrpTrustLine`,
> `preflightOrder`, `preflightSend` and the order methods. It is not a parameter of
> `getWallets`, `getWallet`, `getPortfolio`, `getWalletsPortfolio`, `getTotalAssetValue`,
> `getWalletHistory`, `updateWalletName` or `deleteWallet`, which are personal-only. Organization
> wallets have their own dedicated methods (`listOrgWallets`, `getOrgWallet`, `getOrgWalletsPortfolio`,
> `renameOrgWallet`, `deleteOrgWallet`, …). See [Organization Wallets](https://sdk-guide.mintlify.site/sdk-guide/organization-wallets.md).

## Creating wallets

> [!NOTE]
>
> On an account whose root is a hardware wallet, `createWallet` throws `HardwareSignerRequiredError`.
> Use `prepareHardwareAddAccount` instead. See [Hardware Wallets](https://sdk-guide.mintlify.site/sdk-guide/hardware-wallets.md#method-map).

```typescript theme={null}
import { TrustodianSDK, InMemoryShareStore, EChainType } from 'vaults-multichain-sdk';

const userRef = await sdk.getSdkUserId();
await sdk.unlockWalletSession(userRef, password);

const { wallets } = await sdk.createWallet({
  chain: EChainType.EVM,
  userRef,
  password,
});
```

```typescript theme={null}
interface CreateWalletInput {
  chain: EChainType;
  userRef: string;
  password: string;
  mode?: 'generate' | 'ecdh';
}

interface CreateWalletOutput {
  wallets: TWallet[];
}
```

### Omit `mode`

`createWallet` takes an optional `mode`, but a normal integration never passes it. Every account with
an API key already has a recovery phrase, so the wallet is derived from that phrase: shareless,
with nothing written to your share store. See [Recovery Phrase](https://sdk-guide.mintlify.site/sdk-guide/recovery-phrase.md).

## Importing wallets

The private key is split locally and never uploaded. An imported key cannot be derived from the
account seed, so unlike a wallet you create, it is split into shares and its device share is
written to your `shareStore`. See [Key Management](https://sdk-guide.mintlify.site/sdk-guide/key-management.md#the-three-shares).

```typescript theme={null}
const { wallets } = await sdk.importWallet({
  chain: EChainType.EVM,
  privateKey: '0x...',
  userRef,
  password,
});
```

```typescript theme={null}
interface ImportWalletInput {
  chain: EChainType;
  privateKey: string;
  userRef: string;
  password: string;
}
// returns CreateWalletResponse: { wallets: TWallet[] }
```

Whether import is permitted at all is gated server-side, so handle a refusal rather than assuming
the option is always there.

## The wallet record

```typescript theme={null}
interface TWallet {
  id?: string;
  name: string | null;
  chain: EChainType;
  address: string;
  share: string;
  isImported: boolean;
  /** How this wallet's key is produced. Seed wallets carry `'seed'` plus an `index`. */
  derivationSource?: DerivationSource;   // 'seed' | 'legacy'
  /** BIP-standard derivation path (seed wallets only). */
  derivationPath?: string;
  /** Per-chain HD index used for derivation (seed wallets only). */
  index?: number;
  /** Generation this wallet belongs to. Absent reads as `active`. */
  accessState?: WalletAccessState;      // 'active' | 'archived' | 'access_lost'
  /** The recovery phrase a seed wallet derives from. Present on your own seed wallets. */
  derivationSeedId?: string;
  /** Whose phrase that is. */
  seedOwnerKind?: SeedOwnerKind;        // 'personal' | 'organization'
  /** BTC, ADA and XRP wallets. */
  publicKey?: string | null;
  /** What the account root is. `'hardware'` wallets sign only through `prepareHardware…`. */
  seedRootType?: SeedRootType | null;   // 'phrase' | 'hardware'
}
```

> [!TIP]
>
> `derivationSource`, `index` and `derivationSeedId` matter when provisioning an employee wallet:
> pass them straight from the listing into `provisionOrgMemberWalletAccess`, or a `seed` wallet takes
> the per-wallet share path and provisioning fails.

## Wallet generations

The account holder can replace their recovery phrase in the app. Wallets of the old phrase stay in
the listing and keep their balance and history, but `accessState` says what they can still do.

| `accessState` | Send its own funds | Sign a vault order | Co-own a new vault |
| :- | :- | :- | :- |
| `active` | yes | yes | yes |
| `archived` | yes | no | no |
| `access_lost` | no | no | no |

An absent value reads as `active`. The backend refuses in every one of these cases anyway, so the
helpers below are there to save a round trip and to tell the user why before they submit:

```typescript theme={null}
import {
  resolveWalletAccessState,
  isWalletSpendBlocked,
  canWalletSignVaultOrder,
  canWalletSignNewVault,
  vaultOrderSignerBlockedMessage,
} from 'vaults-multichain-sdk';

const blocked = vaultOrderSignerBlockedMessage(wallet);
if (blocked) {
  // user-ready wording; empty string while the wallet can still sign
}
```

Both gates are enforced in the SDK before the request goes out, and both raise
`PermissionDeniedError` with user-ready wording. `details` carries `walletAddress` and `accessState`
on a blocked spend, and `walletAddresses` on a refused vault owner set.

> [!NOTE]
>
> `archived` is stricter for vaults than for spending on purpose. A vault records its signer set
> once, and an archived generation no longer reproduces the signer the vault stored, so it cannot
> sign there. Moving the wallet's own funds keeps working.

## Listing wallets

```typescript theme={null}
interface GetWalletsInput {
  chain?: EChainType;
  chainTypes?: EChainType[];
  page?: number;
  limit?: number;
  /** Leave wallets of a replaced recovery phrase out of the page and its total. */
  excludeArchived?: boolean;
}

interface PaginatedWalletsResponse {
  wallets: TWallet[];
  total: number;
  page: number;
  limit: number;
}
```

`getWallets` returns everything or a page, depending on the input:

| Call | Result |
| - | - |
| `sdk.getWallets()` | Every wallet, unpaginated |
| any of `page`, `limit`, `chain`, non-empty `chainTypes`, `excludeArchived: true` | Paginated |

```typescript theme={null}
const { wallets } = await sdk.getWallets();

const page = await sdk.getWallets({
  page: 1,
  limit: 20,
  chainTypes: [EChainType.EVM, EChainType.SOL],
});
```

Defaults when paginating are `page: 1`, `limit: 10`. `chain` is a single-value convenience:
use it instead of `chainTypes` when you only want one chain.

> [!NOTE]
>
> `StatusFilterEnum` filters transaction and order status, not wallet listing. Do not pass it to
> `getWallets`.

## Reading one wallet

```typescript theme={null}
interface GetWalletInput {
  address: string;
}

const wallet: TWallet = await sdk.getWallet({ address: '0x...' });
```

> [!WARNING]
>
> `getWallet` takes only `address`. There is no `chain` field, because the server resolves the wallet
> from the address alone.

## Renaming and deleting

```typescript theme={null}
interface UpdateWalletNameInput {
  chain: EChainType;
  address: string;
  name: string;
}
// returns WalletRenameResponse: { wallet: TWallet }

interface DeleteWalletInput {
  chain: EChainType;
  address: string;
}
// returns DeleteResponse: { success: boolean }
```

```typescript theme={null}
await sdk.updateWalletName({
  chain: EChainType.EVM,
  address: walletAddress,
  name: 'Treasury',
});

await sdk.deleteWallet({
  chain: EChainType.EVM,
  address: walletAddress,
});
```

> [!WARNING]
>
> Deleting a wallet removes its server-side key shares. It is irreversible, so make sure the key
> material is backed up (via the Trustodian web app) first if it is still needed.

## Portfolio and balances

### Batch

`getWalletsPortfolio` is the primitive; you build the request array yourself.

```typescript theme={null}
interface PortfolioRequest {
  walletAddress: string;
  chainType: EChainType;
  chain: string;
}

interface GetWalletsPortfolioOptions {
  /** Force refresh from blockchain API instead of cache/DB. */
  force?: boolean;
}

interface WalletPortfolioResponse {
  walletAddress: string;
  chainType: EChainType;
  chain: ChainIdValue;
  tokens: TokensBalance[];
}

interface TokensBalance {
  name: string;
  symbol: string;
  balanceWei: string;
  balance: string;
  priceUSD: number;
  price24hChange: number;
  balanceUSD: number;
  decimals: number;
  address: string;
  logoURL: string;
}
```

```typescript theme={null}
const portfolios = await sdk.getWalletsPortfolio(
  [
    { walletAddress: '0x...', chainType: EChainType.EVM, chain: '1' },
    { walletAddress: '9x...', chainType: EChainType.SOL, chain: 'sol_mainnet' },
  ],
  { force: true },
);
```

`force: true` bypasses the cached balance and re-reads from the chain.

### Single wallet

`getPortfolio` is the one-wallet form of the same call.

```typescript theme={null}
interface GetPortfolioInput {
  chain: EChainType;
  address: string;
  chainId: ChainIdValue;
  force?: boolean;
}
```

```typescript theme={null}
const portfolio = await sdk.getPortfolio({
  chain: EChainType.EVM,
  address: walletAddress,
  chainId: '1',
});

for (const token of portfolio.tokens) {
  console.log(token.symbol, token.balance, token.balanceUSD);
}
```

### Aggregate value

```typescript theme={null}
const { totalAssetValueUSD } = await sdk.getTotalAssetValue();
// TotalAssetValueResponse: { totalAssetValueUSD: string }
```

### Token prices

> [!NOTE]
>
> There is no token-price method on `TrustodianSDK`. The per-token `priceUSD` and `price24hChange`
> on a portfolio response are the prices the SDK exposes; standalone price lookups are not part of
> the SDK surface.

## Supported tokens

```typescript theme={null}
interface GetSupportedTokensInput {
  chain: EChainType;
  chainId: ChainIdValue;
  page?: number;   // default 1
  limit?: number;  // default 50
  search?: string;
}

interface SupportedTokensResponse {
  tokens: SupportedToken[];
  total: number;
}

interface SupportedToken {
  address: string;
  symbol: string;
  name: string;
  decimals: number;
  chainType: EChainType;
  chainId: ChainIdValue;
  logoUrl?: string;
}
```

```typescript theme={null}
const { tokens, total } = await sdk.getSupportedTokens({
  chain: EChainType.EVM,
  chainId: '1',
  search: 'USDC',
  limit: 20,
});
```

## Transaction history

```typescript theme={null}
interface GetWalletHistoryInput {
  chain: EChainType;
  walletAddress: string;
  page?: number;   // default 1
  limit?: number;  // default 20
}

interface HistoryResponse {
  total: number;
  page: number;
  limit: number;
  items: TransactionHistoryItem[];
}
```

```typescript theme={null}
const history = await sdk.getWalletHistory({
  chain: EChainType.EVM,
  walletAddress,
  page: 1,
  limit: 20,
});
```

Each `TransactionHistoryItem` carries `status`
(`'success' | 'failed' | 'pending'`), `method`, `from`, `to`, `value`, `hash`, `time`, `token`
and `details`.

## UTXO chains (BTC and ADA)

> [!WARNING]
>
> There is no `getWalletUtxos` method on `TrustodianSDK` for personal wallets. The only
> UTXO method on the SDK surface is `getOrgWalletUtxos`, which is organization-scoped.

`getOrgWalletUtxos` is the only UTXO method the SDK exposes. See
[Organization Wallets](https://sdk-guide.mintlify.site/sdk-guide/organization-wallets.md).

Its response separates coins by spendability:

```typescript theme={null}
{
  allUtxos: UtxoDto[];     // everything on-chain
  lockedUtxos: UtxoDto[];  // reserved by a pending/processing multisig order
  freeUtxos: UtxoDto[];    // usable as inputs; for BTC also confirmed or own change
  summary: UtxoSummaryDto;
}
```

Each `UtxoDto` has `txHash`, `index`, `value` (lovelace for ADA, satoshis for BTC) and, on ADA,
`nativeAssets: [{ policyId, assetName, value }]`.

> [!NOTE]
>
> Unconfirmed BTC coins appear in `allUtxos` but never in `freeUtxos`, so a balance computed from
> `allUtxos` can exceed what a send is actually able to spend.

## Next

> **[Transactions](https://sdk-guide.mintlify.site/sdk-guide/transactions.md)**
>
> Sending value, fee estimation, memos, address validation, spend authorization

> **[Preflight](https://sdk-guide.mintlify.site/sdk-guide/preflight.md)**
>
> Simulate and verify before a signature is collected

> **[Organization Wallets](https://sdk-guide.mintlify.site/sdk-guide/organization-wallets.md)**
>
> Org-owned wallets, assignment, and provisioning

> **[Key Management](https://sdk-guide.mintlify.site/sdk-guide/key-management.md)**
>
> Sessions, shares, rotation, and recovery


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.