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

# Developer Docs

> Core concepts and functions for the IQLabs SDK

<Note>
  This document is in progress and will be refined.
</Note>

## Installation

```bash theme={null}
npm i @iqlabs-official/solana-sdk
```

***

## Core Concepts

These are the key concepts to know before using the IQLabs SDK.

***

### Data Storage (Code In)

This is how you store any data (files, text, JSON) on-chain.

#### How is it stored?

Depending on data size, the SDK picks the optimal method:

* **Small data (\< 900 bytes)**: store immediately, fastest
* **Medium data (\< 8.5 KB)**: split into multiple transactions
* **Large data (>= 8.5 KB)**: upload in parallel for speed

#### Key related functions

* [`codeIn()`](#codein): upload data and get a transaction ID
* [`readCodeIn()`](#readcodein): read data back from a transaction ID

***

### User State PDA

An on-chain profile account for a user.

#### What gets stored?

* Profile info (name, profile picture, bio, etc.)
* Number of uploaded files
* Friend request records

<Note>
  Friend requests are not stored as values in the PDA; they are sent as transactions.
</Note>

#### When is it created?

It is created automatically the first time you call [`codeIn()`](#codein). No extra setup is required, but the first user may need to sign twice.

***

### Connection PDA

An on-chain account that manages relationships between two users (friends, messages, etc.).

#### What states can it have?

* **pending**: a friend request was sent but not accepted yet
* **approved**: the request was accepted and the users are connected
* **blocked**: one side blocked the other

<Warning>
  A blocked connection can only be unblocked by the blocker.
</Warning>

#### Key related functions

* [`requestConnection()`](#requestconnection): send a friend request (creates pending)
* [`manageConnection()`](#manageconnection): approve/reject/block/unblock a request
* [`readConnection()`](#readconnection): check current relationship status
* [`writeConnectionRow()`](#writeconnectionrow): exchange messages/data with a connected friend
* [`fetchUserConnections()`](#fetchuserconnections): fetch all connections (sent & received friend requests)

***

### Database Tables

Store JSON data in tables like a database.

#### How are tables created?

Use [`createTable()`](#createtable) to create a table explicitly. If the DbRoot PDA is running low on space, the SDK automatically expands it in the same transaction — no extra steps needed.

<Note>
  A table is uniquely identified by the combination of `dbRootId` and `tableSeed`. The `tableSeed` is internally hashed (keccak256) to derive the on-chain PDA. A human-readable `tableHint` (e.g. `"users"`, `"chatroom:general"`) is stored in `DbRoot.table_seeds` so that anyone reading the DbRoot can discover tables without a hardcoded lookup.
</Note>

#### Key related functions

* [`createTable()`](#createtable): create a new table (auto-reallocs DbRoot if needed)
* [`writeRow()`](#writerow): add a new row to an existing table
* [`readTableRows()`](#readtablerows): read rows from a table
* [`getTablelistFromRoot()`](#gettablelistfromroot): list all tables in a database
* [`fetchInventoryTransactions()`](#fetchinventorytransactions): list uploaded files

***

### Token & Collection Gating

Tables can be gated so that only users holding a specific token or NFT collection can write data.

#### Gate Types

| Type           | `GateType`            | Description                                                            |
| -------------- | --------------------- | ---------------------------------------------------------------------- |
| **Token**      | `GateType.Token`      | User must hold >= `amount` of the specified SPL token mint             |
| **Collection** | `GateType.Collection` | User must hold any NFT from the specified Metaplex verified collection |

#### How it works

* **Table creator** sets the gate when creating or updating a table
* **Writers** don't need to do anything special — the SDK automatically resolves the required token account (and metadata account for collections) when calling `writeRow()` or `manageRowData()`
* If no gate is set, the table is public (default behavior, no change for existing users)

#### Gate parameter

```typescript theme={null}
gate?: {
  mint: PublicKey;       // token mint address OR collection address
  amount?: number;       // minimum token amount (default: 1, ignored for collections)
  gateType?: GateType;   // GateType.Token (default) or GateType.Collection
}
```

<Note>
  For **collection gates**, the user can present any NFT from that collection. `amount` is ignored since NFTs always have amount=1.
</Note>

***

### Table Creation Permissions

The database owner (DbRoot creator) can control who is allowed to create tables.

#### Two levels of table creation

| Type              | Stored in                            | Visibility                          | Permission field |
| ----------------- | ------------------------------------ | ----------------------------------- | ---------------- |
| **Public table**  | `table_seeds` + `global_table_seeds` | Listed in `getTablelistFromRoot()`  | `table_creators` |
| **Private table** | `global_table_seeds` only            | Only accessible if you know the PDA | `ext_creators`   |

<Note>
  `table_seeds` and `global_table_seeds` store human-readable hints (e.g. `"users"`), not hashed seeds. To derive the table PDA from a hint, hash it with `toSeedBytes()` first.
</Note>

* If the permission list is **empty**, anyone can create tables (default, backward-compatible)
* If the permission list has wallets, only those wallets + the DbRoot creator can create tables

#### Managing permissions

The DbRoot creator can set both lists in a single call:

```typescript theme={null}
const ix = iqlabs.contract.manageTableCreatorsInstruction(
  builder,
  { signer: wallet.publicKey, db_root: dbRootPda, system_program: SystemProgram.programId },
  {
    db_root_id: dbRootIdBytes,
    table_creators: [adminWallet1, adminWallet2],  // who can create public tables
    ext_creators: [],                               // empty = anyone can create private tables
  }
);
```

Pass empty arrays to make creation public again.

#### Onboarding (private to public)

A private table (exists in `global_table_seeds` only) can be promoted to public (`table_seeds`) by anyone in the `table_creators` list:

```typescript theme={null}
const ix = iqlabs.contract.onboardTableInstruction(
  builder,
  { signer: wallet.publicKey, db_root: dbRootPda },
  { db_root_id: dbRootIdBytes, table_seed: Buffer.from("my-board") }
  // table_seed here matches the hint stored in global_table_seeds
);
```

This is useful when you want anyone to create content (e.g. threads) but only admins decide which ones are publicly listed (e.g. boards).

#### Reading table hints

`getTablelistFromRoot()` returns the hints stored in `table_seeds` and `global_table_seeds` as hex strings. Decode them to get the original readable identifiers:

```typescript theme={null}
const { tableSeeds } = await iqlabs.reader.getTablelistFromRoot(connection, dbRootId);

for (const seedHex of tableSeeds) {
  const hint = Buffer.from(seedHex, "hex").toString("utf8");
  console.log(hint); // "users", "po", "chatroom:general", etc.

  // To access the table, hash the hint to derive the PDA
  const tablePda = iqlabs.contract.getTablePda(
    dbRoot, iqlabs.utils.toSeedBytes(hint), programId
  );
}
```

***

### Encryption (Crypto)

The SDK includes a built-in encryption module (`iqlabs.crypto`) for encrypting data before storing it on-chain.

#### Three encryption modes

* **DH Encryption** (single recipient): Ephemeral X25519 ECDH → HKDF-SHA256 → AES-256-GCM. Use when encrypting data for one specific recipient.
* **Password Encryption**: PBKDF2-SHA256 (250k iterations) → AES-256-GCM. Use for password-protected data that anyone with the password can decrypt.
* **Multi-recipient Encryption** (PGP-style hybrid): Generates a random content encryption key (CEK), encrypts data once, then wraps the CEK for each recipient via ECDH. Use when encrypting data for multiple recipients.

#### Key derivation

Users can derive a deterministic X25519 keypair from their wallet signature using [`deriveX25519Keypair()`](#derivex25519keypair). This means users don't need to manage separate encryption keys — their wallet is the key.

#### Key related functions

* [`deriveX25519Keypair()`](#derivex25519keypair): derive encryption keypair from wallet
* [`dhEncrypt()`](#dhencrypt) / [`dhDecrypt()`](#dhdecrypt): single-recipient encryption
* [`passwordEncrypt()`](#passwordencrypt) / [`passwordDecrypt()`](#passworddecrypt): password-based encryption
* [`multiEncrypt()`](#multiencrypt) / [`multiDecrypt()`](#multidecrypt): multi-recipient encryption

***

## Function Details

### Data Storage and Retrieval

#### `codeIn()`

| **Parameters** | `input`: `{ connection, signer }` object<br />`data`: data to upload (string or string\[])<br />`filename`: optional filename (string)<br />`method`: upload method (number, default: 0)<br />`filetype`: file type hint (string, default: '')<br />`onProgress`: optional progress callback `(percent: number) => void` |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Returns**    | Transaction signature (string)                                                                                                                                                                                                                                                                                           |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

// Upload data
const signature = await iqlabs.writer.codeIn(
  { connection, signer },
  'Hello, blockchain!'
);

// Upload with filename
const sig = await iqlabs.writer.codeIn(
  { connection, signer },
  'file contents here',
  'hello.txt'
);
```

***

#### `readCodeIn()`

| **Parameters** | `txSignature`: transaction signature (string)<br />`speed`: rate limit profile (optional, 'light' \| 'medium' \| 'heavy' \| 'extreme')<br />`onProgress`: optional progress callback `(percent: number) => void` |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Returns**    | `{ metadata: string, data: string \| null }`                                                                                                                                                                     |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

const result = await iqlabs.reader.readCodeIn('5Xg7...');
console.log(result.data);      // 'Hello, blockchain!'
console.log(result.metadata);  // JSON string with file metadata
```

***

### Connection Management

#### `requestConnection()`

| **Parameters** | `connection`: Solana RPC Connection<br />`signer`: Signer<br />`dbRootId`: database ID (Uint8Array or string)<br />`partyA`: first user pubkey (string)<br />`partyB`: second user pubkey (string)<br />`tableName`: connection table name (string or Uint8Array)<br />`columns`: column list (Array\<string \| Uint8Array>)<br />`idCol`: ID column (string or Uint8Array)<br />`extKeys`: extension keys (Array\<string \| Uint8Array>) |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Returns**    | Transaction signature (string)                                                                                                                                                                                                                                                                                                                                                                                                            |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

await iqlabs.writer.requestConnection(
  connection, signer, 'my-db',
  myWalletAddress, friendWalletAddress,
  'dm_table', ['message', 'timestamp'], 'message_id', []
);
```

***

#### `manageConnection()`

<Note>
  There is no high-level SDK wrapper for this function. Use the contract-level instruction builder directly.
</Note>

| **Parameters** | `builder`: InstructionBuilder<br />`accounts`: `{ db_root, connection_table, signer }` (PublicKey)<br />`args`: `{ db_root_id, connection_seed, new_status }` |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Returns**    | TransactionInstruction                                                                                                                                        |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

const builder = iqlabs.contract.createInstructionBuilder();

// Approve a friend request
const approveIx = iqlabs.contract.manageConnectionInstruction(
  builder,
  { db_root, connection_table, signer: myPubkey },
  { db_root_id, connection_seed, new_status: iqlabs.contract.CONNECTION_STATUS_APPROVED }
);

// Block a user
const blockIx = iqlabs.contract.manageConnectionInstruction(
  builder,
  { db_root, connection_table, signer: myPubkey },
  { db_root_id, connection_seed, new_status: iqlabs.contract.CONNECTION_STATUS_BLOCKED }
);
```

***

#### `readConnection()`

| **Parameters** | `dbRootId`: database ID (Uint8Array or string)<br />`partyA`: first wallet (string)<br />`partyB`: second wallet (string) |
| -------------- | ------------------------------------------------------------------------------------------------------------------------- |
| **Returns**    | `{ status: 'pending' \| 'approved' \| 'blocked' \| 'unknown', requester: 'a' \| 'b', blocker: 'a' \| 'b' \| 'none' }`     |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

const { status, requester, blocker } = await iqlabs.reader.readConnection('my-db', partyA, partyB);
console.log(status); // 'pending' | 'approved' | 'blocked'
```

***

#### `writeConnectionRow()`

| **Parameters** | `connection`: Solana RPC Connection<br />`signer`: Signer<br />`dbRootId`: database ID (Uint8Array or string)<br />`connectionSeed`: connection seed (Uint8Array or string)<br />`rowJson`: JSON data (string) |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Returns**    | Transaction signature (string)                                                                                                                                                                                 |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

await iqlabs.writer.writeConnectionRow(
  connection, signer, 'my-db', connectionSeed,
  JSON.stringify({ message_id: '123', message: 'Hello friend!', timestamp: Date.now() })
);
```

***

#### `fetchUserConnections()`

Fetch all connections (friend requests) for a user by analyzing their UserState PDA transaction history. Each connection includes its `dbRootId`, identifying which app the connection belongs to.

| **Parameters** | `userPubkey`: user public key (string or PublicKey)<br />`options`: optional settings                                                                                 |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Options**    | `limit`: max number of transactions to fetch<br />`before`: signature to paginate from<br />`speed`: rate limit profile ('light' \| 'medium' \| 'heavy' \| 'extreme') |
| **Returns**    | Array of `{ dbRootId, connectionPda, partyA, partyB, status, requester, blocker, timestamp }`                                                                         |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

const connections = await iqlabs.reader.fetchUserConnections(myPubkey, {
  speed: 'light',
  limit: 100
});

// Filter by status
const pendingRequests = connections.filter(c => c.status === 'pending');
const friends = connections.filter(c => c.status === 'approved');
const blocked = connections.filter(c => c.status === 'blocked');

// Check connection details
connections.forEach(conn => {
  console.log(`${conn.partyA} <-> ${conn.partyB}, status: ${conn.status}`);
});
```

***

#### `updateUserMetadata()`

| **Parameters** | `connection`: Solana RPC Connection<br />`signer`: Signer<br />`dbRootId`: database ID (Uint8Array or string)<br />`meta`: metadata to store (Uint8Array or string) |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Returns**    | Transaction signature (string)                                                                                                                                      |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

// Store a codeIn tx signature as user metadata
const txSig = await iqlabs.writer.updateUserMetadata(
  connection, signer, 'my-db', metaTxSignature
);
```

***

### Table Management

#### `createTable()`

| **Parameters** | `connection`: Solana RPC Connection<br />`signer`: Signer<br />`dbRootId`: database ID (Uint8Array or string)<br />`tableSeed`: table identifier — hashed internally via `toSeedBytes()` for PDA derivation<br />`tableName`: display name (Uint8Array or string)<br />`columnNames`: column list (Array\<Uint8Array \| string>)<br />`idCol`: ID column (Uint8Array or string)<br />`extKeys`: extension keys (Array\<Uint8Array \| string>)<br />`gate`: optional access gate (see [Token & Collection Gating](#token--collection-gating))<br />`writers`: optional allowed writers (PublicKey\[])<br />`tableHint`: human-readable identifier stored in DbRoot for discovery (string) |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Returns**    | Transaction signature (string)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |

The `tableHint` is stored in `DbRoot.table_seeds` so anyone reading the DbRoot can discover tables. The `tableSeed` is hashed via `toSeedBytes()` to derive the on-chain PDA — it is **not** stored. If the DbRoot PDA is running low on space, a realloc instruction is automatically prepended.

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

// Basic table — tableHint "users" is stored in DbRoot for discovery
await iqlabs.writer.createTable(
  connection, signer, 'my-db', 'users',
  'users',
  ['name', 'email', 'age'],
  'name',
  [],
  undefined, undefined,
  'users'  // tableHint — readable identifier stored in DbRoot
);

// With token gate
await iqlabs.writer.createTable(
  connection, signer, 'my-db', 'vip', 'vip',
  ['name'], 'user_id', [],
  { mint: tokenMintPubkey, amount: 100, gateType: iqlabs.contract.GateType.Token },
  undefined,
  'vip'
);

// With NFT collection gate
await iqlabs.writer.createTable(
  connection, signer, 'my-db', 'holders', 'holders',
  ['name'], 'user_id', [],
  { mint: collectionPubkey, gateType: iqlabs.contract.GateType.Collection },
  undefined,
  'holders'
);
```

***

#### `writeRow()`

| **Parameters** | `connection`: Solana RPC Connection<br />`signer`: Signer<br />`dbRootId`: database ID (Uint8Array or string)<br />`tableSeed`: table name (Uint8Array or string)<br />`rowJson`: JSON row data (string)<br />`skipConfirmation`: skip tx confirmation (boolean, default: false)<br />`remainingAccounts`: optional array of PublicKey to append as read-only reference accounts (e.g. feed PDA for indexing) |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Returns**    | Transaction signature (string)                                                                                                                                                                                                                                                                                                                                                                                |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

// Write the first row to create the table
await iqlabs.writer.writeRow(connection, signer, 'my-db', 'users', JSON.stringify({
  id: 1, name: 'Alice', email: 'alice@example.com'
}));

// Add another row to the same table
await iqlabs.writer.writeRow(connection, signer, 'my-db', 'users', JSON.stringify({
  id: 2, name: 'Bob', email: 'bob@example.com'
}));
```

***

#### `readTableRows()`

| **Parameters** | `account`: table PDA (PublicKey or string)<br />`options`: optional settings                                                                                                                                                                |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Options**    | `limit`: max number of rows to fetch<br />`before`: signature cursor for pagination<br />`signatures`: pre-collected signature array (skips RPC fetch if provided)<br />`speed`: rate limit profile ('light', 'medium', 'heavy', 'extreme') |
| **Returns**    | `Array<Record<string, unknown>>`                                                                                                                                                                                                            |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

// Basic usage
const rows = await iqlabs.reader.readTableRows(tablePda, { limit: 50 });

// Cursor-based pagination
const olderRows = await iqlabs.reader.readTableRows(tablePda, { limit: 50, before: 'sig...' });

// With pre-collected signatures (skips signature fetching, decodes directly)
const sigs = await iqlabs.reader.collectSignatures(tablePda);
const targetIdx = sigs.indexOf('abc123');
const slice = sigs.slice(targetIdx - 25, targetIdx + 25);
const rows = await iqlabs.reader.readTableRows(tablePda, { signatures: slice });
```

***

#### `collectSignatures()`

Collects all (or up to `maxSignatures`) transaction signatures for an account. Lightweight — no transaction decoding, only signature strings. Useful for pagination: fetch the full signature list once, then slice and pass to `readTableRows()`.

| **Parameters** | `account`: table PDA (PublicKey or string)<br />`maxSignatures`: max number of signatures to collect (optional, fetches all if omitted) |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **Returns**    | `string[]` (signature strings)                                                                                                          |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

// Collect all signatures
const allSigs = await iqlabs.reader.collectSignatures(tablePda);

// Collect up to 3000 signatures
const sigs = await iqlabs.reader.collectSignatures(tablePda, 3000);

// Use with readTableRows to read from the middle
const targetIdx = sigs.indexOf('abc123');
const chunk = sigs.slice(targetIdx - 25, targetIdx + 25);
const rows = await iqlabs.reader.readTableRows(tablePda, { signatures: chunk });
```

***

#### `getTablelistFromRoot()`

| **Parameters** | `connection`: Solana RPC Connection<br />`dbRootId`: database ID (Uint8Array or string)             |
| -------------- | --------------------------------------------------------------------------------------------------- |
| **Returns**    | `{ rootPda: PublicKey, creator: string \| null, tableSeeds: string[], globalTableSeeds: string[] }` |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

const result = await iqlabs.reader.getTablelistFromRoot(connection, 'my-db');
console.log('Creator:', result.creator);
console.log('Table seeds:', result.tableSeeds);
```

***

#### `fetchInventoryTransactions()`

| **Parameters** | `publicKey`: user public key (PublicKey)<br />`limit`: max count (number)<br />`before`: pagination cursor (optional, string) |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **Returns**    | Transaction array                                                                                                             |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

const myFiles = await iqlabs.reader.fetchInventoryTransactions(myPubkey, 20);
myFiles.forEach(tx => {
  let metadata: { data?: unknown } | null = null;
  try {
    metadata = JSON.parse(tx.metadata);
  } catch {
    metadata = null;
  }

  if (metadata && metadata.data !== undefined) {
    const inlineData = typeof metadata.data === 'string'
      ? metadata.data
      : JSON.stringify(metadata.data);
    console.log(`Inline data: ${inlineData}`);
  } else {
    console.log(`Signature: ${tx.signature}`);
  }
});
```

***

### Encryption

#### `deriveX25519Keypair()`

Derive a deterministic X25519 keypair from a wallet signature. The same wallet always produces the same keypair.

| **Parameters** | `signMessage`: wallet sign function `(msg: Uint8Array) => Promise<Uint8Array>` |
| -------------- | ------------------------------------------------------------------------------ |
| **Returns**    | `{ privKey: Uint8Array, pubKey: Uint8Array }`                                  |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

const { privKey, pubKey } = await iqlabs.crypto.deriveX25519Keypair(
  wallet.signMessage
);
```

***

#### `dhEncrypt()`

| **Parameters** | `recipientPubHex`: recipient's X25519 public key (hex string)<br />`plaintext`: data to encrypt (Uint8Array) |
| -------------- | ------------------------------------------------------------------------------------------------------------ |
| **Returns**    | `{ senderPub: string, iv: string, ciphertext: string }` (all hex)                                            |

#### `dhDecrypt()`

| **Parameters** | `privKey`: recipient's private key (Uint8Array)<br />`senderPubHex`: sender's ephemeral public key (hex string)<br />`ivHex`: IV (hex string)<br />`ciphertextHex`: ciphertext (hex string) |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Returns**    | `Uint8Array` (decrypted plaintext)                                                                                                                                                          |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

// Encrypt for a single recipient
const encrypted = await iqlabs.crypto.dhEncrypt(
  recipientPubHex,
  new TextEncoder().encode('secret message')
);

// Decrypt (recipient side)
const decrypted = await iqlabs.crypto.dhDecrypt(
  recipientPrivKey,
  encrypted.senderPub,
  encrypted.iv,
  encrypted.ciphertext
);
console.log(new TextDecoder().decode(decrypted)); // 'secret message'
```

***

#### `passwordEncrypt()`

| **Parameters** | `password`: password (string)<br />`plaintext`: data to encrypt (Uint8Array) |
| -------------- | ---------------------------------------------------------------------------- |
| **Returns**    | `{ salt: string, iv: string, ciphertext: string }` (all hex)                 |

#### `passwordDecrypt()`

| **Parameters** | `password`: password (string)<br />`saltHex`: salt (hex string)<br />`ivHex`: IV (hex string)<br />`ciphertextHex`: ciphertext (hex string) |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| **Returns**    | `Uint8Array` (decrypted plaintext)                                                                                                          |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

// Encrypt with password
const encrypted = await iqlabs.crypto.passwordEncrypt(
  'my-password',
  new TextEncoder().encode('secret data')
);

// Decrypt with same password
const decrypted = await iqlabs.crypto.passwordDecrypt(
  'my-password',
  encrypted.salt,
  encrypted.iv,
  encrypted.ciphertext
);
```

***

#### `multiEncrypt()`

| **Parameters** | `recipientPubHexes`: recipient public keys (string\[])<br />`plaintext`: data to encrypt (Uint8Array) |
| -------------- | ----------------------------------------------------------------------------------------------------- |
| **Returns**    | `{ recipients: RecipientEntry[], iv: string, ciphertext: string }`                                    |

#### `multiDecrypt()`

| **Parameters** | `privKey`: your private key (Uint8Array)<br />`pubKeyHex`: your public key (hex string)<br />`encrypted`: the MultiEncryptResult object |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **Returns**    | `Uint8Array` (decrypted plaintext)                                                                                                      |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

// Encrypt for multiple recipients
const encrypted = await iqlabs.crypto.multiEncrypt(
  [alicePubHex, bobPubHex, carolPubHex],
  new TextEncoder().encode('group secret')
);

// Each recipient decrypts with their own key
const plaintext = await iqlabs.crypto.multiDecrypt(
  alicePrivKey, alicePubHex, encrypted
);
```

***

### Environment Settings

#### `setRpcUrl()`

| **Parameters** | `url`: Solana RPC URL (string) |
| -------------- | ------------------------------ |
| **Returns**    | void                           |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

iqlabs.utils.setRpcUrl('https://your-rpc.example.com');
```

***

## Advanced Functions

<Warning>
  These are low-level SDK functions. Not needed for typical usage, but useful when building custom features or debugging.
</Warning>

### Writer Functions

#### `manageRowData()`

Unified function that handles both table row writes and connection row writes. Auto-detects whether to write to a table or connection based on existing PDAs.

| **Module**     | `writer`                                                                                                                                                                                                                                                                                                                                                                     |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Parameters** | `connection`: Solana RPC Connection<br />`signer`: Signer<br />`dbRootId`: database ID (Uint8Array or string)<br />`seed`: table or connection seed (Uint8Array or string)<br />`rowJson`: JSON row data (string)<br />`tableName`: required for table edits (optional, string or Uint8Array)<br />`targetTx`: reference tx for table edits (optional, string or Uint8Array) |
| **Returns**    | Transaction signature (string)                                                                                                                                                                                                                                                                                                                                               |
| **Use Case**   | Custom row management, updating existing rows                                                                                                                                                                                                                                                                                                                                |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

await iqlabs.writer.manageRowData(
  connection, signer,
  'my-db',       // dbRootId
  'users',       // seed (table seed)
  JSON.stringify({ id: 1, name: 'Updated Name' }),
  'users',       // tableName
  originalTxSig  // targetTx
);
```

***

### Reader Functions

#### `readUserState()`

Reads the UserState PDA for a given user.

| **Module**     | `reader`                                                                                       |
| -------------- | ---------------------------------------------------------------------------------------------- |
| **Parameters** | `userPubkey`: user public key (string)                                                         |
| **Returns**    | `{ owner: string, metadata: string \| null, totalSessionFiles: bigint, profileData?: string }` |
| **Use Case**   | Fetching user profile data, checking upload counts                                             |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

const userState = await iqlabs.reader.readUserState(userPubkey);
console.log('Owner:', userState.owner);
console.log('Session files:', userState.totalSessionFiles);
console.log('Profile data:', userState.profileData);
```

***

#### `readInventoryMetadata()`

Reads metadata associated with a user's inventory transaction.

| **Module**     | `reader`                                             |
| -------------- | ---------------------------------------------------- |
| **Parameters** | `txSignature`: transaction signature (string)        |
| **Returns**    | `{ onChainPath: string, metadata: string }`          |
| **Use Case**   | Extracting file metadata from inventory transactions |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

const result = await iqlabs.reader.readInventoryMetadata(txSignature);
console.log('Path:', result.onChainPath);
console.log('Metadata:', result.metadata);
```

***

#### `getSessionPdaList()`

Retrieves a list of session PDA addresses for a user.

| **Module**     | `reader`                                    |
| -------------- | ------------------------------------------- |
| **Parameters** | `userPubkey`: user public key (string)      |
| **Returns**    | `string[]` (session PDA base58 strings)     |
| **Use Case**   | Session management, active session tracking |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

const sessions = await iqlabs.reader.getSessionPdaList(userPubkey);
sessions.forEach(pda => {
  console.log(`Session PDA: ${pda}`);
});
```

***

### Utility Functions

#### Session speed profiles

Many writer/reader functions accept a `speed` parameter that controls RPS
and concurrency for the call. The SDK ships four presets and lets you
override raw values directly:

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

// Preset name
await iqlabs.writer.writeRow(connection, signer, dbRootId, tableSeed, rowJson, {
  speed: 'heavy',
});

// Raw override — any subset works; missing dials inherit the default preset
await iqlabs.writer.writeRow(connection, signer, dbRootId, tableSeed, rowJson, {
  speed: { maxRps: 80, maxConcurrencyUpload: 30 },
});
```

| Preset            | `maxRps` | `maxConcurrency` | `maxConcurrencyUpload` |
| ----------------- | -------- | ---------------- | ---------------------- |
| `light` (default) | 2        | 5                | 1                      |
| `medium`          | 50       | 50               | 5                      |
| `heavy`           | 100      | 100              | 50                     |
| `extreme`         | 250      | 250              | 100                    |

Profiles are mutable — `iqlabs.utils.SESSION_SPEED_PROFILES.heavy.maxRps = 200`
will affect every future `speed: 'heavy'` call.

Exports: `SESSION_SPEED_PROFILES`, `DEFAULT_SESSION_SPEED`,
`resolveSessionSpeed`, `resolveSessionConfig`, types `SessionSpeedKey`,
`SessionSpeedConfig`, `SessionSpeedOption`.

***

#### `deriveDmSeed()`

Derives a deterministic seed for direct messaging (DM) between two users. Sorts the two pubkeys alphabetically and hashes with keccak256.

| **Module**     | `utils`                                                                       |
| -------------- | ----------------------------------------------------------------------------- |
| **Parameters** | `userA`: first user pubkey (string)<br />`userB`: second user pubkey (string) |
| **Returns**    | `Uint8Array` (seed bytes)                                                     |
| **Use Case**   | Creating consistent connection identifiers, DM channel setup                  |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

const seed1 = iqlabs.utils.deriveDmSeed(walletA, walletB);
const seed2 = iqlabs.utils.deriveDmSeed(walletB, walletA);
// seed1 and seed2 are identical — order doesn't matter
```

***

#### `toSeedBytes()`

Converts a seed identifier to bytes. If the input is a 64-character hex string, it returns hex bytes directly. Otherwise, it applies keccak256 hash.

| **Module**     | `utils`                                            |
| -------------- | -------------------------------------------------- |
| **Parameters** | `value`: seed string or Uint8Array                 |
| **Returns**    | `Uint8Array`                                       |
| **Use Case**   | Custom PDA derivation, low-level seed manipulation |

```typescript theme={null}
import iqlabs from '@iqlabs-official/solana-sdk';

const seedString = 'my-custom-seed';
const seedBytes = iqlabs.utils.toSeedBytes(seedString);

const [pda, bump] = PublicKey.findProgramAddressSync(
  [seedBytes, otherSeed],
  programId
);
```
