Skip to main content
This document is in progress and will be refined.

Installation

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

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
Friend requests are not stored as values in the PDA; they are sent as transactions.

When is it created?

It is created automatically the first time you call 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
A blocked connection can only be unblocked by the blocker.

Database Tables

Store JSON data in tables like a database.

How are tables created?

There is no dedicated “create table” function. The first write via writeRow() creates the table automatically.
A table is uniquely identified by the combination of dbRootId and tableSeed (table name).

Function Details

Data Storage and Retrieval

codeIn()

Parametersinput: { connection, signer } object
data: data to upload (string or string[])
filename: optional filename (string)
method: upload method (number, default: 0)
filetype: file type hint (string, default: ”)
onProgress: optional progress callback (percent: number) => void
ReturnsTransaction signature (string)
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()

ParameterstxSignature: transaction signature (string)
speed: rate limit profile (optional, ‘light’ | ‘medium’ | ‘heavy’ | ‘extreme’)
onProgress: optional progress callback (percent: number) => void
Returns{ metadata: string, data: string | 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()

Parametersconnection: Solana RPC Connection
signer: Signer
dbRootId: database ID (Uint8Array or string)
partyA: first user pubkey (string)
partyB: second user pubkey (string)
tableName: connection table name (string or Uint8Array)
columns: column list (Array<string | Uint8Array>)
idCol: ID column (string or Uint8Array)
extKeys: extension keys (Array<string | Uint8Array>)
ReturnsTransaction signature (string)
import iqlabs from '@iqlabs-official/solana-sdk';

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

manageConnection()

There is no high-level SDK wrapper for this function. Use the contract-level instruction builder directly.
Parametersbuilder: InstructionBuilder
accounts: { db_root, connection_table, signer } (PublicKey)
args: { db_root_id, connection_seed, new_status }
ReturnsTransactionInstruction
import iqlabs from '@iqlabs-official/solana-sdk';

const builder = iqlabs.contract.createInstructionBuilder(iqlabs.contract.PROGRAM_ID);

// 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()

ParametersdbRootId: database ID (Uint8Array or string)
partyA: first wallet (string)
partyB: second wallet (string)
Returns{ status: 'pending' | 'approved' | 'blocked' | 'unknown', requester: 'a' | 'b', blocker: 'a' | 'b' | 'none' }
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()

Parametersconnection: Solana RPC Connection
signer: Signer
dbRootId: database ID (Uint8Array or string)
connectionSeed: connection seed (Uint8Array or string)
rowJson: JSON data (string)
ReturnsTransaction signature (string)
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.
ParametersuserPubkey: user public key (string or PublicKey)
options: optional settings
Optionslimit: max number of transactions to fetch
before: signature to paginate from
speed: rate limit profile (‘light’ | ‘medium’ | ‘heavy’ | ‘extreme’)
ReturnsArray of { dbRootId, connectionPda, partyA, partyB, status, requester, blocker, timestamp }
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()

Parametersconnection: Solana RPC Connection
signer: Signer
dbRootId: database ID (Uint8Array or string)
meta: metadata to store (Uint8Array or string)
ReturnsTransaction signature (string)
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

writeRow()

Parametersconnection: Solana RPC Connection
signer: Signer
dbRootId: database ID (Uint8Array or string)
tableSeed: table name (Uint8Array or string)
rowJson: JSON row data (string)
skipConfirmation: skip tx confirmation (boolean, default: false)
ReturnsTransaction signature (string)
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()

Parametersaccount: table PDA (PublicKey or string)
options: optional settings
Optionslimit: max number of rows to fetch
before: signature cursor for pagination
signatures: pre-collected signature array (skips RPC fetch if provided)
speed: rate limit profile (‘light’, ‘medium’, ‘heavy’, ‘extreme’)
ReturnsArray<Record<string, unknown>>
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().
Parametersaccount: table PDA (PublicKey or string)
maxSignatures: max number of signatures to collect (optional, fetches all if omitted)
Returnsstring[] (signature strings)
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()

Parametersconnection: Solana RPC Connection
dbRootId: database ID (Uint8Array or string)
Returns{ rootPda: PublicKey, creator: string | null, tableSeeds: string[], globalTableSeeds: string[] }
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()

ParameterspublicKey: user public key (PublicKey)
limit: max count (number)
before: pagination cursor (optional, string)
ReturnsTransaction array
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}`);
  }
});

Environment Settings

setRpcUrl()

Parametersurl: Solana RPC URL (string)
Returnsvoid
import iqlabs from '@iqlabs-official/solana-sdk';

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

Advanced Functions

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

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.
Modulewriter
Parametersconnection: Solana RPC Connection
signer: Signer
dbRootId: database ID (Uint8Array or string)
seed: table or connection seed (Uint8Array or string)
rowJson: JSON row data (string)
tableName: required for table edits (optional, string or Uint8Array)
targetTx: reference tx for table edits (optional, string or Uint8Array)
ReturnsTransaction signature (string)
Use CaseCustom row management, updating existing rows
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.
Modulereader
ParametersuserPubkey: user public key (string)
Returns{ owner: string, metadata: string | null, totalSessionFiles: bigint, profileData?: string }
Use CaseFetching user profile data, checking upload counts
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.
Modulereader
ParameterstxSignature: transaction signature (string)
Returns{ onChainPath: string, metadata: string }
Use CaseExtracting file metadata from inventory transactions
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.
Modulereader
ParametersuserPubkey: user public key (string)
Returnsstring[] (session PDA base58 strings)
Use CaseSession management, active session tracking
import iqlabs from '@iqlabs-official/solana-sdk';

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

Utility Functions

deriveDmSeed()

Derives a deterministic seed for direct messaging (DM) between two users. Sorts the two pubkeys alphabetically and hashes with keccak256.
Moduleutils
ParametersuserA: first user pubkey (string)
userB: second user pubkey (string)
ReturnsUint8Array (seed bytes)
Use CaseCreating consistent connection identifiers, DM channel setup
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.
Moduleutils
Parametersvalue: seed string or Uint8Array
ReturnsUint8Array
Use CaseCustom PDA derivation, low-level seed manipulation
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
);