> ## 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.

# Quickstart

> From npm install to a broadcast transaction in one file

This page walks through a complete first integration on testnet: construct the SDK, unlock the wallet session, derive a wallet, read its balances, and send a transaction.

> [!WARNING]
>
> Everything here runs on your backend. The API key and the end-user wallet password must never
> reach a browser or mobile client.

## Before you start

* Node.js 18 or newer.
* An API key, issued from the Trustodian app for an account that has already completed onboarding.
  See [Authentication](https://sdk-guide.mintlify.site/sdk-guide/authentication.md).
* The account holder's wallet password, supplied per request.
* Somewhere to persist device shares. The steps below sketch a database-backed `IntegratorShareStore`.

**1. Install the package**

```bash npm theme={null}
npm install vaults-multichain-sdk
```

```bash yarn theme={null}
yarn add vaults-multichain-sdk
```

**2. Implement a share store**

The SDK persists one device share per wallet through the store you provide. `InMemoryShareStore` is bundled for local experiments. Production needs durable storage, because losing a device share forces a password-based recovery on the next signing attempt.

```typescript share-store.ts theme={null}
import type {
  IntegratorShareStore,
  DeviceShareRecord,
} from 'vaults-multichain-sdk';

export class PostgresShareStore implements IntegratorShareStore {
  constructor(private readonly db: DatabaseClient) {}

  async save(record: DeviceShareRecord): Promise<void> {
    await this.db.query(
      `INSERT INTO device_shares
         (user_ref, wallet_id, address, chain_type, device_share_hex, version)
       VALUES ($1, $2, $3, $4, $5, $6)
       ON CONFLICT (user_ref, wallet_id) DO UPDATE
         SET device_share_hex = EXCLUDED.device_share_hex,
             version          = EXCLUDED.version`,
      [
        record.userRef,
        record.walletId,
        record.address,
        record.chainType,
        record.deviceShareHex,
        record.version,
      ],
    );
  }

  async load(userRef: string, walletId: string): Promise<DeviceShareRecord | null> {
    return this.db.one('... WHERE user_ref = $1 AND wallet_id = $2', [userRef, walletId]);
  }

  async findByAddress(
    userRef: string,
    chainType: string,
    address: string,
  ): Promise<DeviceShareRecord | null> {
    // XRP addresses must match case-insensitively; every other chain matches exactly.
    return this.db.one('... WHERE user_ref = $1 AND chain_type = $2 AND address = $3', [
      userRef,
      chainType,
      chainType === 'XRP' ? address.toLowerCase() : address,
    ]);
  }

  async delete(userRef: string, walletId: string): Promise<void> {
    await this.db.query('DELETE FROM device_shares WHERE user_ref = $1 AND wallet_id = $2', [
      userRef,
      walletId,
    ]);
  }
}
```

> [!NOTE]
>
> A device share is key material. Encrypt the column at rest, keep it out of logs and backups you
> would not treat as secrets, and never return it to a client.

**3. Construct the SDK**

```typescript theme={null}
import { TrustodianSDK } from 'vaults-multichain-sdk';
import { PostgresShareStore } from './share-store';

const sdk = new TrustodianSDK({
  apiKey: process.env.TRUSTODIAN_API_KEY!,
  network: 'testnet',
  shareStore: new PostgresShareStore(db),
});
```

`apiKey`, `network`, and `shareStore` are the three fields the constructor refuses to run without. Every other option is covered in the [configuration table](https://sdk-guide.mintlify.site/sdk-guide/introduction.md#configuration-options).

**4. Resolve the userRef**

`userRef` is the stable account identifier that ties session keys and `shareStore` rows together. Resolve it once and reuse it.

```typescript theme={null}
const userRef = await sdk.getSdkUserId();
```

**5. Unlock the session**

The account already has a wallet password from onboarding, and your integration only unlocks it. The password is supplied by your end user on each request, never hardcoded and never stored.

```typescript theme={null}
await sdk.unlockWalletSession(userRef, walletPassword);
```

The session stays unlocked for `keyTtlMs` (5 minutes by default). See [Authentication](https://sdk-guide.mintlify.site/sdk-guide/authentication.md) for the full lifecycle.

**6. Create a wallet**

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

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

const wallet = wallets[0];
console.log(wallet.address, wallet.derivationSource, wallet.derivationPath);
```

Omit `mode`. The account has a recovery phrase, so the wallet is derived from that seed: shareless, with a `derivationPath` and an `index`. A normal integration never passes `mode`.

**7. Read the portfolio**

```typescript theme={null}
const portfolio = await sdk.getPortfolio({
  chain: EChainType.EVM,
  address: wallet.address,
  chainId: '11155111', // Ethereum Sepolia
});

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

`chainId` here is a `ChainIdValue` string (`'1'`, `'8453'`, `'sol_mainnet'`, `'BTC_T3'`, …). Add `force: true` to bypass the cached balance and re-read from the chain.

**8. Send a transaction**

```typescript theme={null}
const { txHash } = await sdk.sendTransaction({
  userRef,
  chain: EChainType.EVM,
  from: wallet.address,
  to: '0xRecipient…',
  amount: '0.01',
  chainId: '11155111',
  tokenAddress: '',
  decimals: 18,
  isNativeToken: true,
});
```

For an ERC-20 transfer, set `tokenAddress` to the contract, `decimals` to the token's decimals, `symbol` to its ticker, and drop `isNativeToken`. Signing needs an unlocked session; if the device share is missing on this host, pass `walletPassword` so the SDK can rotate it back in. See [Transactions](https://sdk-guide.mintlify.site/sdk-guide/transactions.md).

**9. Lock the session**

```typescript theme={null}
sdk.lockWalletSession(userRef);
```

This wipes the session key immediately instead of waiting for `keyTtlMs` to expire. Call it as soon as the user's request is done.

## Putting it together

```typescript theme={null}
import { TrustodianSDK, EChainType } from 'vaults-multichain-sdk';
import { PostgresShareStore } from './share-store';

const sdk = new TrustodianSDK({
  apiKey: process.env.TRUSTODIAN_API_KEY!,
  network: 'testnet',
  shareStore: new PostgresShareStore(db),
});

export async function createWalletAndSend(walletPassword: string, to: string) {
  const userRef = await sdk.getSdkUserId();

  try {
    await sdk.unlockWalletSession(userRef, walletPassword);

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

    return await sdk.sendTransaction({
      userRef,
      chain: EChainType.EVM,
      from: wallets[0].address,
      to,
      amount: '0.01',
      chainId: '11155111',
      tokenAddress: '',
      decimals: 18,
      isNativeToken: true,
    });
  } finally {
    sdk.lockWalletSession(userRef);
  }
}
```

> [!TIP]
>
> If you would rather not thread `userRef` through every call, wrap the block in
> `sdk.runWithUserRef(userRef, async () => { … })`. Concurrent requests stay isolated from one
> another.

## What to read next

> **[Authentication](https://sdk-guide.mintlify.site/sdk-guide/authentication.md)**
>
> Headers, session lifecycle, password changes and hints

> **[Recovery Phrase](https://sdk-guide.mintlify.site/sdk-guide/recovery-phrase.md)**
>
> Reveal, verification state, derivation paths

> **[Key Management](https://sdk-guide.mintlify.site/sdk-guide/key-management.md)**
>
> Device shares, rotation, recovery on a new host

> **[Transactions](https://sdk-guide.mintlify.site/sdk-guide/transactions.md)**
>
> Token transfers, fee estimation, and history

> **[Preflight](https://sdk-guide.mintlify.site/sdk-guide/preflight.md)**
>
> Catch a failing transaction before you broadcast it

> **[Errors](https://sdk-guide.mintlify.site/sdk-guide/errors.md)**
>
> What each typed error means and how to recover


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