npm package: @kalp_studio/tresori-sdk-js
Description: The TreSori SDK provides a unified, high-level API for blockchain wallet operations. It supports three distinct wallet types to accommodate various security models and use cases:
Latest version: 1.2.1

# TreSori SDK

## Overview

The TreSori SDK provides a unified, high-level API for blockchain wallet operations. It supports three distinct wallet types to accommodate various security models and use cases:

- **Self-Custodial:** Full user control with local key management
- **Custodial:** Service-managed wallets linked to user accounts
- **MPC:** Enhanced security through distributed key shards with verification flows

## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [React Native](#react-native)
- [Quick Start](#quick-start)
- [API Reference](#api-reference)
  - [Initialization](#initialization)
  - [Self-Custodial Wallet](#self-custodial-wallet)
  - [Custodial Wallet](#custodial-wallet)
  - [MPC Wallet](#mpc-wallet)
  - [Wallet Balance](#wallet-balance)
  - [Chain Management](#chain-management)
  - [Error Handling](#error-handling)
  - [Response Formats](#response-formats)
- [Example App](#example-app)
- [Exports](#exports)
- [License](#license)
- [Support](#support)

## Features

### Self-Custodial Wallets

| Feature                    | Description                                                      |
| -------------------------- | ---------------------------------------------------------------- |
| Mnemonic Generation        | Generate BIP39-compliant mnemonic phrases (12/15/18/21/24 words) |
| Wallet Creation            | Create wallets from mnemonic with blockchain registration        |
| Mnemonic Import            | Restore wallets from existing mnemonic phrases                   |
| Private Key Import         | Import wallets directly from private keys                        |
| Token Transfers            | Transfer native tokens (ETH, MATIC, etc.)                        |
| Smart Contract Interaction | Sign and send contract transactions                              |

### Custodial Wallets

| Feature                     | Description                                      |
| --------------------------- | ------------------------------------------------ |
| Managed Wallet Creation     | Create server-managed wallets linked to user IDs |
| Token Transfers             | Transfer native and ERC-20 tokens                |
| Smart Contract Transactions | Execute contract write operations                |

### MPC Wallets

| Feature                    | Description                                                                                                   |
| -------------------------- | ------------------------------------------------------------------------------------------------------------- |
| Email / phone verification | OTP flows; after verify you receive session data for MPC signing                                              |
| Native & ERC-20 transfers  | Sign and broadcast via chain RPC using `clientShare`, `sessionId`, `rpcUrl`                                   |
| Gasless ERC-20 (optional)  | When the project has gasless enabled, `transferMpcTokens` with a token address uses permit + relay (EIP-2612) |
| Smart contract writes      | Encode locally, MPC `sign-simple`, broadcast raw tx on your `rpcUrl`                                          |

### Multi-Chain Support

- Dynamic chain configuration loaded at runtime
- Support for multiple EVM-compatible networks
- Chain-specific parameters (chain ID, explorer URL, currency)

## Installation

Published package name (see `package.json`):

```bash
npm install @kalp_studio/tresori-sdk-js
```

Or with yarn:

```bash
yarn add @kalp_studio/tresori-sdk-js
```

The package is ESM-only (`"type": "module"`). Use in Node.js (14+) or in bundlers (Vite, webpack, Metro, etc.).

The SDK lists **`@noble/hashes`**, **`@noble/ciphers`**, and related **`@noble/*`** packages for MPC DKG (ECDH, SHA-256, AES-256-GCM in pure JS, so React Native does not need `crypto.subtle` for that path).

The **`example`** and **`example-rn`** apps in this repo depend on the local package as **`tresori-sdk-js`** (`file:..`); their imports use that name. Published apps should import **`@kalp_studio/tresori-sdk-js`** as shown above.

## React Native

The SDK works in React Native (**iOS and Android**). The library does not use DOM-only APIs in its published code.

### MPC email flow (new vs returning user)

For **`sendEmailVerificationRequest`** and **`verifyEmail`**, the SDK calls **`POST /auth/is-mpc-exist`** with `blockchain`, `network` (mapped from your selected **`chain`**), and **`walletIdentifier`** (email is trimmed and lowercased before server calls). It sets the internal **new-user** flag to **`!isMPCExists`**, then sends/verifies the OTP with the correct backend shape. You do **not** pass **`isNewUser`** on these methods. If the wallet already exists, verify follows the returning-user path (no DKG). If DKG runs for a new user and fails (e.g. missing **`crypto.getRandomValues`** on React Native), the SDK returns the raw verify body and logs a **`console.warn`** with the error.

### Cryptography (MPC DKG on React Native)

**MPC DKG:** ECDH and SHA-256 use **`@noble/curves`** / **`@noble/hashes`**. **AES-256-GCM** uses **`@noble/ciphers`** inside the SDK (pure JavaScript) — you do **not** need **`react-native-webcrypto`** or **`crypto.subtle`**. UTF-8 for MPC payloads uses **ethers**’s **`toUtf8Bytes` / `toUtf8String`** so you do **not** need global **`TextDecoder`** / **`TextEncoder`** (Hermes often omits **`TextDecoder`**, which previously broke **`dkg-init`** after email verify).

**Secure random:** AES-GCM IVs use **`crypto.getRandomValues`**. On React Native you must load **`react-native-get-random-values`** once at app entry (**Android and iOS**). That is the standard, maintained polyfill used across the ecosystem (including with ethers).

### Dependencies in your React Native app

```bash
npm install react-native-get-random-values
```

### Polyfills at app entry (before any other imports)

In your **very first** entry file (e.g. `index.js`), run:

```js
import 'react-native-get-random-values';
```

This repo’s **`example-rn`** does this in **`polyfills.ts`**, imported first from **`index.js`**.

### (Optional) Register ethers secure random

If you use **`react-native-quick-crypto`** for performance, you can register it with ethers so mnemonic/key generation uses native bytes (unrelated to MPC AES, which stays in **`@noble/ciphers`**):

```js
import crypto from 'react-native-quick-crypto';
import { randomBytes } from 'ethers';
randomBytes.register((length) => new Uint8Array(crypto.randomBytes(length)));
```

### Use the SDK as on web

```js
import { TreSoriImpl, ChainListInstance } from '@kalp_studio/tresori-sdk-js';

await TreSoriImpl.instance.initialize(API_KEY);
// Chains are loaded by initialize()
const chain = ChainListInstance.all[0];
// Self-custodial, custodial, MPC APIs are unchanged.
```

### React Native summary

- Load **`react-native-get-random-values`** at entry on **Android and iOS** so **`crypto.getRandomValues`** exists.
- **No** **`react-native-webcrypto`** is required for TreSori MPC DKG.
- **Phone MPC** still requires an explicit **`isNewUser`** flag (no **`is-mpc-exist`** wiring in the SDK for phone yet).

## Quick Start

```javascript
import { TreSori, ChainListInstance } from '@kalp_studio/tresori-sdk-js';

async function main() {
  // 1. Initialize the SDK with your API key
  await TreSori().initialize('YOUR_API_KEY');

  // 2. Generate a mnemonic phrase
  const mnemonic = TreSori().generateMnemonic();
  console.log('Mnemonic:', mnemonic);

  // 3. Get a chain (after initialize, chains are loaded)
  const selectedChain = ChainListInstance.all[0];

  // 4. Create a wallet
  const wallet = await TreSori().createWallet({
    chain: selectedChain,
    mnemonic,
    userId: 'user_123',
  });

  console.log('Address:', wallet.address);
  console.log('Private Key:', wallet.privateKey);
}

main().catch(console.error);
```

## API Reference

### Initialization

#### `initialize(apiKey: string): Promise<void>`

Initializes the SDK with your API key. Must be called before any other operations. Loads supported chains from the backend.

```javascript
await TreSori().initialize('YOUR_API_KEY');
```

| Parameter | Type   | Required | Description          |
| --------- | ------ | -------- | -------------------- |
| apiKey    | string | ✅       | Your TreSori API key |

**Throws:** Error if the API key is empty.

#### `get mpcGaslessEnabled(): boolean`

Read-only flag: `true` when the backend **enabled-chains** response includes gasless for the project (cached when `initialize` runs, and refreshed via `Config` when available). Use it to show or hide gasless ERC-20 UI. Actual gasless transfers still require an EIP-2612–compatible token and the arguments described under **`transferMpcTokens`** below.

```javascript
await TreSori().initialize('YOUR_API_KEY');
if (TreSori().mpcGaslessEnabled) {
  // Offer gasless ERC-20 path (see transferMpcTokens)
}
```

---

### Self-Custodial Wallet

#### `generateMnemonic(opts?: { wordCount?: number }): string`

Generates a BIP39-compliant mnemonic phrase.

```javascript
// Generate 12-word mnemonic (default)
const mnemonic12 = TreSori().generateMnemonic();

// Generate 24-word mnemonic
const mnemonic24 = TreSori().generateMnemonic({ wordCount: 24 });
```

| Parameter      | Type   | Default | Description                             |
| -------------- | ------ | ------- | --------------------------------------- |
| opts.wordCount | number | 12      | Number of words (12, 15, 18, 21, or 24) |

**Returns:** string — Space-separated mnemonic phrase

#### `createWallet(args: { chain, mnemonic, userId }): Promise<object>`

Creates a new wallet and registers it with the backend.

```javascript
const result = await TreSori().createWallet({
  chain: selectedChain,
  mnemonic: 'word1 word2 word3 ... word12',
  userId: 'unique_user_id',
});

console.log('Address:', result.address);
console.log('Private Key:', result.privateKey);
console.log('Mnemonic:', result.mnemonic);
```

| Parameter | Type   | Required | Description               |
| --------- | ------ | -------- | ------------------------- |
| chain     | Chain  | ✅       | Target blockchain network |
| mnemonic  | string | ✅       | BIP39 mnemonic phrase     |
| userId    | string | ✅       | Unique user identifier    |

**Returns:** Promise\<object\>

```javascript
{
  mnemonic: string,
  privateKey: string,
  address: string,
  userId: string,
}
```

#### `importWalletFromMnemonic(mnemonic: string): object`

Imports an existing wallet from a mnemonic phrase.

```javascript
const result = TreSori().importWalletFromMnemonic(
  'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
);
console.log('Address:', result.address);
console.log('Private Key:', result.privateKey);
```

| Parameter | Type   | Required | Description                 |
| --------- | ------ | -------- | --------------------------- |
| mnemonic  | string | ✅       | Valid BIP39 mnemonic phrase |

**Returns:** object

```javascript
{
  privateKey: string,
  address: string,
}
```

#### `importWalletFromPrivateKey(privateKey: string): object`

Imports a wallet from a private key.

```javascript
const result = TreSori().importWalletFromPrivateKey(
  '0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318',
);
console.log('Address:', result.address);
```

| Parameter  | Type   | Required | Description                                         |
| ---------- | ------ | -------- | --------------------------------------------------- |
| privateKey | string | ✅       | Hex-encoded private key (with or without 0x prefix) |

**Returns:** object

```javascript
{
  privateKey: string,
  address: string,
}
```

#### `transferTokens(args): Promise<string>`

Transfers native tokens (ETH, MATIC, etc.) to a recipient address.

```javascript
const txHash = await TreSori().transferTokens({
  chain: selectedChain,
  privateKey: '0x...',
  toAddress: '0xRecipientAddress',
  amount: '1', // in ether (e.g. 1 ETH); uses ethers parseEther
  rpcUrl: 'https://rpc.example.com',
});
console.log('Transaction Hash:', txHash);
```

| Parameter  | Type   | Required | Description                          |
| ---------- | ------ | -------- | ------------------------------------ |
| chain      | Chain  | ✅       | Target blockchain network            |
| privateKey | string | ✅       | Sender's private key                 |
| toAddress  | string | ✅       | Recipient's wallet address           |
| amount     | string | ✅       | Amount in ether (e.g. "1" for 1 ETH) |
| rpcUrl     | string | ✅       | RPC endpoint URL                     |

**Returns:** Promise\<string\> — Transaction hash

#### `signContractTransaction(args): Promise<string>`

Signs and sends a smart contract transaction.

```javascript
const txHash = await TreSori().signContractTransaction({
  chain: selectedChain,
  privateKey: '0x...',
  contractAddress: '0xContractAddress',
  contractAbi: [...],
  functionName: 'transfer',
  parameters: ['0xRecipient', 1000],
  transaction: { value: 0n, maxGas: 100000n, ... },
  rpcUrl: 'https://rpc.example.com',
});
```

| Parameter       | Type                     | Required | Description                                                                       |
| --------------- | ------------------------ | -------- | --------------------------------------------------------------------------------- |
| chain           | Chain                    | ✅       | Target blockchain network                                                         |
| privateKey      | string                   | ✅       | Sender's private key                                                              |
| contractAddress | string                   | ✅       | Deployed contract address                                                         |
| contractAbi     | ContractInterface \| any | ✅       | Contract ABI (ethers-compatible)                                                  |
| functionName    | string                   | ✅       | Contract function to call                                                         |
| parameters      | any[]                    | No       | Function parameters                                                               |
| transaction     | object                   | ✅       | Ethers `TransactionRequest` fields; use `{}` to let the wallet fill gas and nonce |
| rpcUrl          | string                   | ✅       | RPC endpoint URL                                                                  |

**Returns:** Promise\<string\> — Transaction hash

---

### Custodial Wallet

#### `createCustodialWallet(args: { chain, userId }): Promise<object>`

Creates a service-managed custodial wallet.

```javascript
const result = await TreSori().createCustodialWallet({
  chain: selectedChain,
  userId: 'unique_user_id',
});
console.log('Custodial Address:', result.address);
```

| Parameter | Type   | Required | Description               |
| --------- | ------ | -------- | ------------------------- |
| chain     | Chain  | ✅       | Target blockchain network |
| userId    | string | ✅       | Unique user identifier    |

**Returns:** Promise\<object\> — Wallet details including address

#### `transferCustodialTokens(args): Promise<object>`

Transfers tokens from a custodial wallet.

```javascript
const result = await TreSori().transferCustodialTokens({
  fromAddress: '0xCustodialWalletAddress',
  toAddress: '0xRecipientAddress',
  amount: '1000000000000000000', // 1 token in wei
  chain: selectedChain,
  tokenAddress: '0xTokenContractAddress', // Optional: for ERC-20 tokens
});
```

| Parameter    | Type   | Required | Description                                     |
| ------------ | ------ | -------- | ----------------------------------------------- |
| fromAddress  | string | ✅       | Custodial wallet address                        |
| toAddress    | string | ✅       | Recipient address                               |
| amount       | string | ✅       | Amount in wei (as string)                       |
| chain        | Chain  | ✅       | Target blockchain network                       |
| tokenAddress | string | No       | ERC-20 token contract address (omit for native) |

**Returns:** Promise\<object\> — Transaction response

#### `writeCustodialSmartContractTransaction(args): Promise<object>`

Executes a smart contract write operation from a custodial wallet.

```javascript
const result = await TreSori().writeCustodialSmartContractTransaction({
  contractAddress: '0xContractAddress',
  functionName: 'transfer',
  parameters: ['0xRecipient', 1000],
  contractAbi: [...],
  fromAddress: '0xCustodialWalletAddress',
  chain: selectedChain,
});
```

| Parameter       | Type   | Required | Description               |
| --------------- | ------ | -------- | ------------------------- |
| contractAddress | string | ✅       | Deployed contract address |
| functionName    | string | ✅       | Contract function to call |
| parameters      | any[]  | ✅       | Function parameters       |
| contractAbi     | any[]  | ✅       | Contract ABI as array     |
| fromAddress     | string | ✅       | Custodial wallet address  |
| chain           | Chain  | ✅       | Target blockchain network |

**Returns:** Promise\<object\> — Transaction response

---

### MPC Wallet

#### `isMpcExist(args): Promise<object>`

Checks whether an MPC wallet already exists for a chain + identifier (typically the same email you use for OTP).

```javascript
const { isMPCExists, mpcExistRequest } = await TreSori().isMpcExist({
  chain: selectedChain,
  walletIdentifier: 'user@example.com',
});
```

| Parameter        | Type   | Required | Description                                                |
| ---------------- | ------ | -------- | ---------------------------------------------------------- |
| chain            | Chain  | ✅       | Target chain (`blockchain` / `network` mapped for the API) |
| walletIdentifier | string | ✅       | e.g. email address                                         |

**Returns:** **`isMPCExists`** (boolean) and **`mpcExistRequest`** — the `{ blockchain, network, walletIdentifier }` body sent to **`/auth/is-mpc-exist`** (useful for logging).

#### Email Verification

##### `sendEmailVerificationRequest(args): Promise<object>`

Calls **`isMpcExist`** with **`walletIdentifier`** = trimmed **`email`**, then sends the OTP with the correct new-user vs returning-user payload. **`chain`** selects **`blockchain` / `network`** for that check and for DKG when applicable.

```javascript
const result = await TreSori().sendEmailVerificationRequest({
  chain: selectedChain,
  email: 'user@example.com',
  userId: 'unique_user_id',
});
```

| Parameter | Type   | Required | Description               |
| --------- | ------ | -------- | ------------------------- |
| chain     | Chain  | ✅       | Target blockchain network |
| email     | string | ✅       | User's email address      |
| userId    | string | ✅       | Unique user identifier    |

**Returns:** Promise\<object\> — Backend OTP response. On a normal JSON object response, the SDK also merges **`isMPCExists`** and **`mpcExistRequest`** for client logging.

##### `verifyEmail(args): Promise<object>`

Calls **`isMpcExist`** again (same rules as send), then verifies the OTP with the matching new-user vs returning-user behavior.

```javascript
const result = await TreSori().verifyEmail({
  chain: selectedChain,
  email: 'user@example.com',
  userId: 'unique_user_id',
  otp: '123456',
});
```

| Parameter | Type   | Required | Description               |
| --------- | ------ | -------- | ------------------------- |
| chain     | Chain  | ✅       | Target blockchain network |
| email     | string | ✅       | User's email address      |
| userId    | string | ✅       | Unique user identifier    |
| otp       | string | ✅       | OTP code from email       |

**Returns:** Promise\<object\> — **Returning users** (MPC already exists for that email/chain): response often includes **`walletAddress`**, **`sessionId`**, and **`clientShare`** without DKG. **New users**: the SDK runs **`initDKG`** (**`dkg-init`** then **`dkg-contribute`**) and returns those fields on success; on failure it returns the raw email-verify payload (see the [React Native](#react-native) section). Merged fields **`isMPCExists`** and **`mpcExistRequest`** are included when the payload is a plain object.

#### Phone Verification

##### `sendPhoneVerificationCode(args): Promise<object>`

Sends an OTP verification code via SMS.

```javascript
const result = await TreSori().sendPhoneVerificationCode({
  chain: selectedChain,
  countryCode: '+1',
  phone: '5551234567',
  userId: 'unique_user_id',
  isNewUser: true,
});
```

| Parameter   | Type    | Required | Description                             |
| ----------- | ------- | -------- | --------------------------------------- |
| chain       | Chain   | ✅       | Target blockchain network               |
| countryCode | string  | ✅       | Country code (e.g. '+1', '+44')         |
| phone       | string  | ✅       | Phone number without country code       |
| userId      | string  | ✅       | Unique user identifier                  |
| isNewUser   | boolean | ✅       | Whether this is a new user registration |

**Returns:** Promise\<object\> — Response indicating OTP was sent

##### `verifyPhone(args): Promise<object>`

Verifies the phone number using the OTP received.

```javascript
const result = await TreSori().verifyPhone({
  chain: selectedChain,
  countryCode: '+1',
  phone: '5551234567',
  userId: 'unique_user_id',
  otp: '123456',
  isNewUser: true,
});
```

| Parameter   | Type    | Required | Description                             |
| ----------- | ------- | -------- | --------------------------------------- |
| chain       | Chain   | ✅       | Target blockchain network               |
| countryCode | string  | ✅       | Country code                            |
| phone       | string  | ✅       | Phone number                            |
| userId      | string  | ✅       | Unique user identifier                  |
| otp         | string  | ✅       | OTP code from SMS                       |
| isNewUser   | boolean | ✅       | Whether this is a new user registration |

**Returns:** Promise\<object\> — Verification result with wallet details

#### MPC token transfers

##### `transferMpcTokens(args): Promise<Record<string, any>>`

Single entry point for **native**, **ERC-20 (user pays gas)**, and **gasless ERC-20** MPC transfers. You must complete email (or phone) verification first and persist **`clientShare`**, **`sessionId`**, and the MPC wallet **`fromAddress`**. All paths need a working **`rpcUrl`** to the same chain as `chain`.

**Modes**

1. **Native (chain currency)** — Omit `tokenAddress` or pass an empty string. Amount is parsed with **`decimals`** (default **18**), e.g. `"1"` for one ETH-style unit. The wallet pays gas.
2. **ERC-20 (user pays gas)** — Set `tokenAddress` to the token contract. Set **`decimals`** if not 18. Amount is a human-readable token string in those decimals (e.g. `"1.5"`).
3. **Gasless ERC-20** — When **`mpcGaslessEnabled`** is `true` and **`tokenAddress`** is non-empty, the implementation routes to the gasless pipeline (permit + relayer). Use **`platformFee`** for the fee in token units (same decimals); defaults apply per backend/facilitator rules. Token must support **EIP-2612** permits.

When gasless is used via this method, **`TreSoriImpl`** supplies built-in **sponsor** and **relayer** addresses for the relay flow. To use your own relayer/sponsor, call **`gaslessTransferTokens`** instead.

```javascript
// Native (default 18 decimals → "1" = 1 ETH-style unit)
const native = await TreSori().transferMpcTokens({
  fromAddress: mpcAddress,
  toAddress: '0xRecipient',
  amount: '1',
  chain: selectedChain,
  clientShare,
  sessionId,
  userIdentity: 'email',
  rpcUrl: 'https://sepolia.drpc.org',
});

// ERC-20 (payer pays gas)
const erc20 = await TreSori().transferMpcTokens({
  fromAddress: mpcAddress,
  toAddress: '0xRecipient',
  amount: '10',
  chain: selectedChain,
  clientShare,
  sessionId,
  userIdentity: 'email',
  rpcUrl: 'https://sepolia.drpc.org',
  tokenAddress: '0xToken',
  decimals: 18,
});

// Gasless ERC-20 (only when mpcGaslessEnabled && tokenAddress set)
const gasless = await TreSori().transferMpcTokens({
  fromAddress: mpcAddress,
  toAddress: '0xRecipient',
  amount: '10',
  chain: selectedChain,
  clientShare,
  sessionId,
  userIdentity: 'email',
  rpcUrl: 'https://sepolia.drpc.org',
  tokenAddress: '0xEip2612Token',
  decimals: 18,
  platformFee: '1',
});
```

| Parameter    | Type   | Required | Description                                                                  |
| ------------ | ------ | -------- | ---------------------------------------------------------------------------- |
| fromAddress  | string | ✅       | MPC wallet address                                                           |
| toAddress    | string | ✅       | Recipient address                                                            |
| amount       | string | ✅       | Human amount; **`decimals`** applies (default 18) for both native and ERC-20 |
| chain        | Chain  | ✅       | Target chain                                                                 |
| clientShare  | string | ✅       | Client share from verify / DKG                                               |
| sessionId    | string | ✅       | MPC session id                                                               |
| userIdentity | string | ✅       | Identity channel, e.g. `'email'`                                             |
| rpcUrl       | string | ✅       | Chain JSON-RPC URL                                                           |
| tokenAddress | string | No       | Omit or empty for native; required for ERC-20 and gasless                    |
| decimals     | number | No       | Token decimals (default **18**) when `tokenAddress` is set                   |
| platformFee  | string | No       | Gasless: fee in token units (default **`'1'`** when gasless path is used)    |

**Returns:** Promise\<Record\<string, any\>\> — Backend/RPC payload; often includes `txHash` / `transactionHash` (shape may vary by path).

##### `gaslessTransferTokens(args): Promise<Record<string, any>>`

Lower-level **gasless** transfer with explicit **`relayerAddress`** and **`sponsorAddress`**. The **`userShards`** argument is the same secret material as **`clientShare`** in other MPC APIs (naming follows the abstract API).

```javascript
await TreSori().gaslessTransferTokens({
  fromAddress: mpcAddress,
  toAddress: '0xRecipient',
  amount: '10',
  chain: selectedChain,
  userShards: clientShare,
  userIdentity: 'email',
  rpcUrl: 'https://...',
  sessionId,
  tokenAddress: '0xToken',
  relayerAddress: '0x...',
  sponsorAddress: '0x...',
  decimals: 18,
  platformFee: '1',
});
```

| Parameter      | Type   | Required | Description                                    |
| -------------- | ------ | -------- | ---------------------------------------------- |
| fromAddress    | string | ✅       | MPC wallet address                             |
| toAddress      | string | ✅       | Recipient                                      |
| amount         | string | ✅       | Human amount (token decimals)                  |
| chain          | Chain  | ✅       | Target chain                                   |
| userShards     | string | ✅       | Client share (same as `clientShare` elsewhere) |
| userIdentity   | string | ✅       | e.g. `'email'`                                 |
| rpcUrl         | string | ✅       | Chain RPC                                      |
| sessionId      | string | ✅       | MPC session                                    |
| tokenAddress   | string | ✅       | EIP-2612 token                                 |
| relayerAddress | string | ✅       | Relayer contract / service address             |
| sponsorAddress | string | ✅       | Sponsor address                                |
| decimals       | number | No       | Default 18                                     |
| platformFee    | string | No       | Fee in token units                             |

##### `writeMpcSmartContractTransaction(args): Promise<object>`

Encodes the call locally, signs with MPC (`/v2/wallet/mpc/sign-simple`), and broadcasts via your chain RPC (no relayer write-transaction).

```javascript
const result = await TreSori().writeMpcSmartContractTransaction({
  contractAddress: '0xContractAddress',
  functionName: 'transfer',
  params: ['0xRecipient', '1000000000000000000'],
  abi: ['function transfer(address to, uint256 amount) returns (bool)'],
  fromAddress: '0xMpcWalletAddress',
  chain: selectedChain,
  clientShare: '...', // from verify / DKG
  sessionId: '...',
  rpcUrl: 'https://...',
  value: '0', // optional: wei sent with call (payable)
});
```

| Parameter       | Type     | Required | Description                                                                                                   |
| --------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------- |
| contractAddress | string   | ✅       | Contract address                                                                                              |
| functionName    | string   | ✅       | Function to call                                                                                              |
| params          | any[]    | ✅       | Function arguments (ethers-compatible)                                                                        |
| abi             | string[] | ✅       | Human-readable ethers fragments only (e.g. `'function foo(uint256) external'`) — not `{ type, name, … }` JSON |
| fromAddress     | string   | ✅       | MPC wallet address                                                                                            |
| chain           | Chain    | ✅       | Target blockchain network                                                                                     |
| clientShare     | string   | ✅       | Client share from verify / DKG                                                                                |
| sessionId       | string   | ✅       | MPC session id                                                                                                |
| rpcUrl          | string   | ✅       | Chain JSON-RPC URL                                                                                            |
| value           | string   | No       | Wei sent with tx (payable functions)                                                                          |

**Returns:** Promise\<object\> — e.g. `{ txHash }` from `eth_sendRawTransaction`

---

### Wallet Balance

#### `getWalletBalance(args: { address, chain }): Promise<object>`

Retrieves the balance of a wallet.

```javascript
const result = await TreSori().getWalletBalance({
  address: '0xWalletAddress',
  chain: selectedChain,
});
console.log('Balance:', result.balance);
```

| Parameter | Type   | Required | Description               |
| --------- | ------ | -------- | ------------------------- |
| address   | string | ✅       | Wallet address to query   |
| chain     | Chain  | ✅       | Target blockchain network |

**Returns:** Promise\<object\> — Balance information

---

### Chain Management

After `initialize()` is called, supported chains are loaded from the backend. Use `ChainListInstance` or the `chain` proxy to obtain a `Chain` for API calls.

```javascript
import { TreSori, ChainListInstance, chain } from '@kalp_studio/tresori-sdk-js';

await TreSori().initialize('YOUR_API_KEY');

// All available chains
const allChains = ChainListInstance.all;

for (const c of ChainListInstance.all) {
  console.log(`${c.blockchain} - ${c.network}`);
  console.log('  Chain ID:', c.chainId);
  console.log('  Currency:', c.currency);
  console.log('  Explorer:', c.explorerUrl);
}

// Get chain by blockchain and network (e.g. Ethereum Mainnet)
const ethereumMainnet = chain.ethereum.mainnet;

// Get chain by chainId
const chainById = ChainListInstance.ofChainId('1');
```

#### Chain shape

Chains are plain objects with the following fields:

| Property    | Type   | Description                |
| ----------- | ------ | -------------------------- |
| id          | number | Internal ID                |
| blockchain  | string | e.g. "Ethereum", "Polygon" |
| network     | string | e.g. "Mainnet", "Amoy"     |
| chainId     | string | e.g. "1", "80002"          |
| explorerUrl | string | Block explorer URL         |
| currency    | string | e.g. "ETH", "MATIC"        |
| logo        | string | Logo URL                   |
| createdAt   | string | Creation timestamp         |

---

### Error Handling

The SDK throws errors for invalid or failed operations. Wrap calls in try/catch (or handle promise rejections).

```javascript
try {
  await TreSori().initialize('YOUR_API_KEY');

  const wallet = await TreSori().createWallet({
    chain: selectedChain,
    mnemonic,
    userId,
  });
} catch (err) {
  console.error('Error:', err.message);
}
```

#### Common errors

| Error                                                                 | Cause                                                                 |
| --------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `Wallet not initialized. Call initialize() first.`                    | SDK methods called before `initialize()`                              |
| `API key is required. Call initialize() first.`                       | Empty API key passed to `initialize()`                                |
| `ChainList not loaded. Call "await ChainList.instance.load()" first.` | Chains accessed before `initialize()` (load runs inside `initialize`) |
| Network/HTTP errors                                                   | API connectivity or backend failures                                  |

---

### Response Formats

#### Successful wallet creation

```javascript
{
  mnemonic: 'word1 word2 ... word12',
  privateKey: '4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318',
  address: '0x742d35Cc6634C0532925a3b844Bc9e7595f8fE56',
  userId: 'user_123',
}
```

#### Wallet import (mnemonic / private key)

```javascript
{
  privateKey: '4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318',
  address: '0x742d35Cc6634C0532925a3b844Bc9e7595f8fE56',
}
```

---

## Example apps

### Web (`/example`)

React + Vite demo: initialization, self-custodial and custodial flows, MPC email/phone verification, native and ERC-20 MPC transfers (including gasless when enabled), MPC contract writes with fragment ABI, and chain selection.

```bash
cd example
npm install
npm run dev
```

### React Native (`/example-rn`)

Expo-based (**SDK 55**, React Native with **New Architecture**) Android/iOS demo with the same TreSori APIs. The checked-in app targets **Android** in `app.json`. Entry **`index.js`** loads **`./polyfills`** first so **`react-native-get-random-values`** runs before other imports (**Android and iOS**). MPC DKG uses **`@noble/ciphers`** in the SDK, not `react-native-webcrypto`. Use **`npm run android:dev`** / **`expo run:android`** or **`expo run:ios`** when you need a full native project; after major Expo upgrades run **`npm run prebuild:android`** (or `npx expo prebuild --platform android`) so **`android/`** stays in sync. See [React Native](#react-native) above and **`example-rn/MPC_FLOW_RN.md`**. On the MPC tab, **new vs returning** email flow is determined by **`/auth/is-mpc-exist`**; the demo logs **`MPC: New user`** or **`MPC: User exists`** after Send OTP / Verify.

```bash
# from repo root
npm run build
cd example-rn
npm install
npm run android:dev
# iOS: npm run ios
```

Build the SDK from the repo root first when using a local `file:..` dependency:

```bash
npm run build
cd example && npm install && npm run dev
```

---

## Exports

The package exports:

- **`TreSori`** — Function that returns the singleton SDK instance: `TreSori().initialize()`, `TreSori().createWallet()`, etc.
- **`TreSoriImpl`** — Concrete class with `TreSoriImpl.instance` (same singleton; useful in RN when you prefer a static entry point).
- **`ChainListInstance`** — Chain list: `load()`, `all`, `get`, `ofChainId`, `isLoaded`, and related helpers.
- **`chain`** — Proxy: `chain.<blockchain>.<network>` (e.g. `chain.ethereum.mainnet`).
- **`parseMpcAbiFragments`** / **`parseMpcAbiFragmentsFromText`** — Normalize MPC contract **`abi`** to `string[]` fragments (human-readable ethers fragments only; rejects JSON ABI objects). The text helper accepts one fragment per line, strict JSON `string[]`, or bracketed lists (including common single-quote paste formats).
- **Abstract `TreSori` type** and re-exports from **`./core`** where applicable for advanced integration.

---

## License

See LICENSE for details.

## Support

For issues, feature requests, or questions, please contact the TreSori team.