API Reference
Core SDK

SDK Reference

Complete API reference for the Veridex Core SDK (@veridex/sdk).

Installation

npm install @veridex/sdk ethers

Initialization

createSDK

Creates a new VeridexSDK instance with sensible defaults.

import { createSDK } from '@veridex/sdk';
 
const sdk = createSDK('base', {
  network: 'testnet',
  relayerUrl: 'https://relayer.veridex.network',
});

Parameters:

ParameterTypeDescription
chainChainNameChain name — see supported chains below
configSimpleSDKConfigOptional configuration

Supported chains: 'base', 'optimism', 'arbitrum', 'ethereum', 'polygon', 'monad', 'solana', 'aptos', 'sui', 'starknet', 'stacks'

interface SimpleSDKConfig {
  network?: 'testnet' | 'mainnet';
  rpcUrl?: string;
  relayerUrl?: string;
  relayerApiKey?: string;
  sponsorPrivateKey?: string;
  integratorSponsorKey?: string;
  rpcUrls?: Record<ChainName, string>;
}

Convenience Factories

import { createHubSDK, createTestnetSDK, createMainnetSDK, createSessionSDK } from '@veridex/sdk';
 
const hubSdk  = createHubSDK();                 // Base hub chain
const testSdk = createTestnetSDK('optimism');    // Force testnet
const mainSdk = createMainnetSDK('base');        // Force mainnet
const sessSdk = createSessionSDK('base');        // Session-optimized

VeridexSDK (Direct Constructor)

For full control, use the class directly with an EVMClient:

import { VeridexSDK, EVMClient } from '@veridex/sdk';
 
const evmClient = new EVMClient({
  chainId: 84532,
  wormholeChainId: 10004,
  rpcUrl: 'https://sepolia.base.org',
  hubContractAddress: '0x66D87dE68327f48A099c5B9bE97020Feab9a7c82',
  wormholeCoreBridge: '0x79A1027a6A159502049F10906D333EC57E95F083',
  name: 'Base Sepolia',
  explorerUrl: 'https://sepolia.basescan.org',
  vaultFactory: '0xCFaEb5652aa2Ee60b2229dC8895B4159749C7e53',
  vaultImplementation: '0x0d13367C16c6f0B24eD275CC67C7D9f42878285c',
});
 
const sdk = new VeridexSDK({
  chain: evmClient,
  testnet: true,
  relayerUrl: '/api/relayer',
  relayerApiKey: process.env.NEXT_PUBLIC_RELAYER_API_KEY,
  sponsorPrivateKey: process.env.NEXT_PUBLIC_VERIDEX_SPONSOR_KEY,
  chainRpcUrls: {
    10004: 'https://sepolia.base.org',
    10005: 'https://sepolia.optimism.io',
    10003: 'https://sepolia-rollup.arbitrum.io/rpc',
  },
});

Dynamic Import (SSR-Safe)

For Next.js apps, use dynamic imports to avoid SSR issues:

let sdkInstance: VeridexSDK | null = null;
 
export async function getVeridexSDK() {
  if (!sdkInstance) {
    const { createSDK } = await import('@veridex/sdk');
    sdkInstance = createSDK('base', {
      network: 'testnet',
      relayerUrl: process.env.NEXT_PUBLIC_RELAYER_URL,
    });
  }
  return sdkInstance;
}

Passkey Management (sdk.passkey)

sdk.passkey.register

Register a new passkey with biometric authentication.

const credential = await sdk.passkey.register('alice', 'Alice Doe');
ParameterTypeDescription
usernamestringUsername (shown in passkey prompt)
displayNamestringDisplay name for the credential

Returns: Promise<PasskeyCredential>

sdk.passkey.authenticate

Authenticate with an existing passkey (shows passkey picker).

const { credential, signature } = await sdk.passkey.authenticate();

Returns: Promise<{ credential: PasskeyCredential; signature: WebAuthnSignature }>

sdk.passkey.getCredential

const credential = sdk.passkey.getCredential(); // PasskeyCredential | null

sdk.passkey.setCredential

sdk.passkey.setCredential(credential);

sdk.passkey.getAllStoredCredentials

const credentials = sdk.passkey.getAllStoredCredentials(); // PasskeyCredential[]

sdk.passkey.sign

const signature = await sdk.passkey.sign(challenge); // WebAuthnSignature

Local Storage

sdk.passkey.saveToLocalStorage();
const credential = sdk.passkey.loadFromLocalStorage();
const hasStored = sdk.passkey.hasStoredCredential();

Relayer Sync (Cross-Device)

await sdk.passkey.saveCredentialToRelayer();
const credential = await sdk.passkey.loadCredentialFromRelayer();

Credential Management

sdk.setCredential(credential);    // Set active credential
sdk.clearCredential();             // Clear active credential
const cred = sdk.getCredential(); // Get active credential
const has = sdk.hasCredential();  // Check if credential is set

Vault & Identity

sdk.getVaultAddress

Get the deterministic vault address derived from the passkey. Same address on all EVM chains.

const address = sdk.getVaultAddress(); // string

sdk.vaultExists

const exists = await sdk.vaultExists(); // boolean

sdk.getVaultInfo

const info = await sdk.getVaultInfo(targetChainId?); // VaultInfo | null

sdk.getUnifiedIdentity

const identity = await sdk.getUnifiedIdentity();
for (const addr of identity.addresses) {
  console.log(`${addr.chainName}: ${addr.address} (deployed: ${addr.deployed})`);
}

sdk.getMultiChainAddresses

Get vault addresses across all configured chains.

const addresses = sdk.getMultiChainAddresses();
// { 10004: '0x...', 10005: '0x...', 10003: '0x...' }

Returns: Record<number, string> — Wormhole chain ID → vault address

sdk.getMultiChainPortfolio

Get combined portfolio across multiple chains.

const portfolios = await sdk.getMultiChainPortfolio([10004, 10005]);
for (const p of portfolios) {
  console.log(`${p.chainName}: $${p.totalUsdValue}`);
}

Returns: Promise<PortfolioBalance[]>

sdk.getChainConfig

const config = sdk.getChainConfig(); // ChainConfig

sdk.getReceiveAddress

const addr = sdk.getReceiveAddress(); // ReceiveAddress

sdk.getNonce

const nonce = await sdk.getNonce(); // bigint

Feature Detection

sdk.supportsFeature

Check if the current chain supports a specific feature.

if (sdk.supportsFeature('sessions')) {
  // Sessions are available
}
if (sdk.supportsFeature('crossChainBridge')) {
  // Bridging is available
}

Returns: boolean

sdk.getCapabilityMatrix

Get the full capability matrix for the current chain and platform.

const matrix = sdk.getCapabilityMatrix({
  webauthnSupported: true,
  conditionalUISupported: true,
  platformAuthenticatorAvailable: true,
});

Returns: PlatformCapabilityMatrix


Transfers

sdk.prepareTransfer

Prepare a transfer with gas estimation. Call before signing to show costs.

const prepared = await sdk.prepareTransfer({
  targetChain: 10004,
  token: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
  recipient: '0x742d35Cc6634C0532925a3b844Bc9e7595f5A234',
  amount: 1_000_000n, // 1 USDC
});
 
console.log('Gas cost:', prepared.formattedCost);
console.log('Expires:', new Date(prepared.expiresAt));

Returns: Promise<PreparedTransfer>

sdk.executeTransfer

Execute a prepared transfer (requires a signer to pay gas).

const result = await sdk.executeTransfer(prepared, signer);
console.log('Tx:', result.transactionHash);

Returns: Promise<TransferResult>

sdk.transferViaRelayer

Gasless transfer through the relayer — the primary transfer method. The user only needs their passkey.

import { ethers } from 'ethers';
 
const result = await sdk.transferViaRelayer({
  targetChain: 10004,
  token: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
  recipient: '0x742d35Cc6634C0532925a3b844Bc9e7595f5A234',
  amount: ethers.parseUnits('10', 6),
});

Returns: Promise<TransferResult>

sdk.transferWithTracking

const result = await sdk.transferWithTracking(params, signer, (status) => {
  console.log(status.state); // 'pending' → 'confirmed'
});

sdk.getTransactionSummary

const summary = await sdk.getTransactionSummary(prepared);
console.log(summary.title);       // "Transfer"
console.log(summary.description); // "Send 1.0 USDC to 0x742d...5A234"
console.log(summary.risks);       // [{level:'warning', message:'Large transaction'}]

Cross-Chain Bridges

sdk.prepareBridge

const prepared = await sdk.prepareBridge({
  sourceChain: 10004,
  destinationChain: 10005,
  token: '0x...',
  recipient: '0x...',
  amount: 1_000_000n,
});

sdk.executeBridge

const result = await sdk.executeBridge(prepared, signer, (progress) => {
  console.log(`Step ${progress.step}/${progress.totalSteps}: ${progress.message}`);
});

sdk.bridgeViaRelayer

Gasless bridge using the relayer.

const result = await sdk.bridgeViaRelayer(params, onProgress?);

sdk.getBridgeFees

const fees = await sdk.getBridgeFees(params);
console.log('Total:', fees.formattedTotal); // "0.0012 ETH"

Spending Limits

sdk.getSpendingLimits

Get raw on-chain spending limits.

const limits = await sdk.getSpendingLimits(chainId?);
console.log('Daily remaining:', limits.dailyRemaining);

sdk.getFormattedSpendingLimits

Get spending limits formatted for UI display.

const formatted = await sdk.getFormattedSpendingLimits();
console.log(`${formatted.dailyUsedPercentage}% used`);
console.log(`Resets in: ${formatted.timeUntilReset}`);
console.log(`Per-tx limit: ${formatted.transactionLimit}`);

sdk.checkSpendingLimit

Check if a transaction amount is within limits.

const check = await sdk.checkSpendingLimit(ethers.parseEther('1.0'));
if (!check.allowed) {
  console.log(check.message);
  console.log('Suggestions:', check.suggestions);
}

sdk.prepareSetDailyLimit

const prepared = await sdk.prepareSetDailyLimit(ethers.parseEther('5.0'));
await sdk.executeTransfer(prepared, signer);

sdk.preparePauseVault / sdk.prepareUnpauseVault

const pause = await sdk.preparePauseVault();
const unpause = await sdk.prepareUnpauseVault();

Balance Watching

sdk.watchBalance

Subscribe to vault balance changes via polling. Returns an unsubscribe function.

const unsub = sdk.watchBalance(
  (event) => {
    for (const change of event.changes) {
      console.log(`${change.token.symbol}: ${change.delta > 0n ? '+' : ''}${change.delta}`);
    }
  },
  { intervalMs: 10_000, emitInitial: true },
  (error) => console.error('Watch error:', error),
);
 
// Later: stop watching
unsub();

See the Balance Watching guide for full usage patterns.


Balance Management

sdk.getVaultBalances

const portfolio = await sdk.getVaultBalances();
for (const entry of portfolio.tokens) {
  console.log(`${entry.token.symbol}: ${entry.formatted}`);
}

sdk.getVaultNativeBalance

const native = await sdk.getVaultNativeBalance();
console.log(`${native.token.symbol}: ${native.formatted}`);

sdk.getVaultTokenBalance

const balance = await sdk.getVaultTokenBalance(tokenAddress);

sdk.getMultiChainBalances

const results = await sdk.getMultiChainBalances([10004, 10005, 10003]);

Sponsored Vault Creation

sdk.isSponsorshipAvailable

const canSponsor = sdk.isSponsorshipAvailable(); // boolean

sdk.ensureSponsoredVaultsOnAllChains

Auto-create vaults on all configured chains using the sponsor key.

const result = await sdk.ensureSponsoredVaultsOnAllChains();
console.log(`Created on ${result.results.filter(r => r.success).length} chains`);

sdk.createSponsoredVault

const result = await sdk.createSponsoredVault(10004); // Base Sepolia

sdk.getSponsoredChains

const chains = sdk.getSponsoredChains();

Backup Passkeys (Multi-Key Identity)

sdk.addBackupPasskey

const newCred = await sdk.passkey.register('alice-backup', 'Alice Backup');
const result = await sdk.addBackupPasskey(newCred, signer);

sdk.listAuthorizedPasskeys

const keys = await sdk.listAuthorizedPasskeys();

sdk.hasBackupPasskeys

const hasBackup = await sdk.hasBackupPasskeys(); // boolean

sdk.removePasskey

await sdk.removePasskey(keyHash, signer);

Multisig (ADR-0037)

Access via sdk.multisig:

// Configure policy
await sdk.multisig.configurePolicy({
  signature, threshold: 2, signer,
});
 
// Create proposal
const proposal = await sdk.multisig.proposeTransfer(params, signer);
 
// Approve
const approval = await sdk.multisig.approveProposal(proposal.proposalId, signer);
 
// Execute (once threshold reached)
if (approval.thresholdReached) {
  const result = await sdk.multisig.executeProposal(proposal.proposalId, signer);
}

See the Multisig Wallet guide for full setup.


Enterprise Manager

Orchestration layer for batch operations and admin dashboards.

import { EnterpriseManager } from '@veridex/sdk';
 
const enterprise = new EnterpriseManager({ sdk, maxConcurrency: 5 });

enterprise.batchCreateVaults

const result = await enterprise.batchCreateVaults(
  { keyHashes: ['0xabc...', '0xdef...'], maxConcurrency: 3 },
  (event) => console.log(event.type, event),
);
console.log(`${result.succeeded}/${result.total} vaults created`);

enterprise.batchTransfer

const result = await enterprise.batchTransfer({
  transfers: [
    { targetChain: 10004, token: USDC, recipient: '0xAlice', amount: 1_000_000n },
    { targetChain: 10004, token: USDC, recipient: '0xBob', amount: 2_000_000n },
  ],
  signer,
  maxConcurrency: 2,
});

enterprise.batchSetSpendingLimits

const result = await enterprise.batchSetSpendingLimits({
  updates: [
    { newLimit: ethers.parseEther('5.0') },
    { newLimit: ethers.parseEther('10.0') },
  ],
  signer,
});

enterprise.watchVaultBalance

const unsub = enterprise.watchVaultBalance(10004, vaultAddress, (event) => {
  callWebhook(event);
});

enterprise.getSpendingLimitsForVault

const limits = await enterprise.getSpendingLimitsForVault('0xVault...', 10004);

See the Enterprise Manager guide for full patterns.


Error Handling

All SDK methods throw VeridexError with unified error codes:

import { VeridexError, VeridexErrorCode, normalizeError } from '@veridex/sdk';
 
try {
  await sdk.transferViaRelayer(params);
} catch (err) {
  if (err instanceof VeridexError) {
    switch (err.code) {
      case VeridexErrorCode.INSUFFICIENT_FUNDS:
        showToast('Not enough tokens');
        break;
      case VeridexErrorCode.DAILY_LIMIT_EXCEEDED:
        showToast('Daily limit reached');
        break;
      default:
        if (err.retryable) { /* retry */ }
    }
  }
}

See Error Handling for the full error code reference and chain-specific mappings.


Token Management

const tokens = sdk.getTokenList();
const usdc = sdk.getTokenBySymbol('USDC');

Token Constants

import {
  NATIVE_TOKEN_ADDRESS,
  BASE_SEPOLIA_TOKENS,
  TOKEN_REGISTRY,
  getTokenList,
  getTokenBySymbol,
  getTokenByAddress,
  isNativeToken,
} from '@veridex/sdk';

Chain Presets & Utilities

import {
  getChainConfig,
  getSupportedChains,
  getHubChains,
  isChainSupported,
  getDefaultHub,
  CHAIN_PRESETS,
  CHAIN_NAMES,
} from '@veridex/sdk';
 
const chains = getSupportedChains('testnet');
const config = getChainConfig('stacks', 'testnet');

Chain Clients

import {
  EVMClient,
  SolanaClient,
  AptosClient,
  SuiClient,
  StarknetClient,
  StacksClient,
} from '@veridex/sdk';

Session Key Utilities

import {
  SessionManager,
  generateSecp256k1KeyPair,
  computeSessionKeyHash,
  signWithSessionKey,
  hashAction,
  verifySessionSignature,
  deriveEncryptionKey,
  encrypt,
  decrypt,
  createSessionStorage,
} from '@veridex/sdk';

ERC-8004 Utilities

import {
  getERC8004Addresses,
  isERC8004Chain,
  ERC8004_CHAINS,
  IDENTITY_REGISTRY_ABI,
  REPUTATION_REGISTRY_ABI,
} from '@veridex/sdk';
 
const addresses = getERC8004Addresses('mainnet');
const supported = isERC8004Chain('base'); // true
RegistryMainnetTestnet
Identity0x8004A169FB4a3325136EB29fA0ceB6D2e539a4320x8004A818BFB912233c491871b3d84c89A494BD9e
Reputation0x8004BAa17C55a88189AE136b182e5fdA19dE9b630x8004B663056A597Dffe9eCcC1965A193B7388713

Transaction Tracking

sdk.transactions.track(txHash, chainId, (state) => {
  console.log(state); // 'pending' | 'confirmed' | 'failed'
}, sequence);
 
const state = await sdk.waitForTransaction(txHash);

Enterprise Factories

createEnterpriseSDK

import { createEnterpriseSDK } from '@veridex/sdk';
 
const sdk = createEnterpriseSDK(chain, {
  network: 'mainnet',
  multisig: { threshold: number, signers: Signer[] },
  recovery: { guardians: string[], delay: number },
});

Returns a VeridexSDK instance pre-configured with MultisigManager and RecoveryManager.

createHubSDK

import { createHubSDK } from '@veridex/sdk';
 
const sdk = createHubSDK(chain, { network: 'testnet' });

Hub SDK for shared passkey credential management across multiple applications.

createSessionSDK

import { createSessionSDK } from '@veridex/sdk';
 
const sdk = createSessionSDK(chain, {
  network: 'testnet',
  sessionKeyHash: '0x...',
});

Session-scoped SDK for backend services operating with a pre-existing session key.


MultisigManager

const multisig = sdk.multisig;
 
await multisig.propose({ to, value, data });   // Propose transaction
await multisig.approve(proposalId);             // Approve as signer
await multisig.execute(proposalId);             // Execute (threshold met)
await multisig.getProposal(proposalId);         // Get proposal details
await multisig.listPending();                   // List pending proposals

RecoveryManager

const recovery = sdk.recovery;
 
await recovery.initiateRecovery(newOwner);       // Start recovery
await recovery.attestAsGuardian(requestId, signer); // Guardian attestation
await recovery.executeRecovery(requestId);        // Execute after delay
await recovery.cancelRecovery(requestId);         // Cancel (by current owner)
await recovery.getRecoveryStatus(requestId);      // Check status

CrossOriginAuth

Enable Veridex passkey authentication in third-party applications. The SDK detects browser support and picks the best flow automatically.

import { createCrossOriginAuth } from '@veridex/sdk';
 
const auth = createCrossOriginAuth({
  rpId: 'veridex.network',              // default
  authPortalUrl: 'https://auth.veridex.network', // default
  relayerUrl: 'https://relayer.veridex.network/api/v1', // default
  mode: 'popup',                         // 'popup' | 'redirect'
  redirectUri: 'https://myapp.com/callback', // only for redirect mode
  timeout: 120000,                       // 2 minutes default
});

auth.supportsRelatedOrigins

Check if the browser supports WebAuthn Related Origin Requests (seamless, no popup).

const rorSupported = await auth.supportsRelatedOrigins(); // boolean

auth.authenticate

Native passkey authentication (only works when ROR is supported).

const { credential, signature } = await auth.authenticate();
console.log('Key hash:', credential.keyHash);
console.log('Vault:', '0x' + credential.keyHash.slice(-40));

auth.connectWithVeridex

Auth Portal flow (popup or redirect). Works on all browsers.

const session = await auth.connectWithVeridex();
console.log('Address:', session.address);
console.log('Credential:', session.credential.keyHash);

Options:

ParameterTypeDescription
registerbooleanOpen in registration mode
usernamestringPre-fill username for registration
displayNamestringPre-fill display name
createSessionbooleanHandle full session flow inside a single popup
permissionsstring[]Session permissions (e.g. ['read', 'transfer'])
expiresInMsnumberSession expiry duration in ms

auth.authenticateAndCreateSession

Recommended. Full flow: authenticate + create a server-validated session in one call. Uses a single popup when ROR is unavailable (no double-prompt).

const { session, serverSession } = await auth.authenticateAndCreateSession({
  permissions: ['read', 'transfer'],
  expiresInMs: 3600000, // 1 hour
});
 
console.log('Vault address:', session.address);
console.log('Server session ID:', serverSession.id);
console.log('Expires at:', new Date(serverSession.expiresAt));
 
// Store for subsequent API calls
localStorage.setItem('veridex_session_id', serverSession.id);

auth.createServerSession

Create a server-validated session token from a cross-origin session.

const serverSession = await auth.createServerSession(session);

auth.validateServerSession

Validate an existing session token (returns null if expired/revoked).

const valid = await auth.validateServerSession('ses_abc123');
if (valid) {
  console.log('Session valid, expires:', new Date(valid.expiresAt));
}

auth.revokeServerSession

await auth.revokeServerSession('ses_abc123');

auth.completeRedirectAuth

Call on your redirect callback page to extract the session from URL params.

const session = auth.completeRedirectAuth(); // CrossOriginSession | null

WalletBackupManager

PRF-based encrypted wallet backup and recovery. Backs up credentials to the relayer encrypted with a key derived from your passkey's PRF extension.

import { WalletBackupManager } from '@veridex/sdk';
 
const backup = new WalletBackupManager({
  passkey: sdk.passkey,
  relayerUrl: 'https://relayer.veridex.network/api/v1',
});

backup.backupCredentials

Encrypt and store your wallet credentials on the relayer. The encryption key is derived from your passkey's PRF output — only you can decrypt it.

const result = await backup.backupCredentials(keyHash);
console.log('Archive ID:', result.archiveId);
console.log('Backed up:', result.credentialCount, 'credentials');

What happens:

  1. SDK triggers a WebAuthn ceremony and extracts the PRF seed from your authenticator
  2. Derives an AES-GCM-256 encryption key via HKDF
  3. Encrypts all locally-stored credentials
  4. Uploads the encrypted archive to the relayer

If your device doesn't support PRF (the passkey extension for deterministic secrets), the SDK falls back to a credential-ID-based key. This is less secure — the account page will show a warning.

backup.recoverCredentials

Recover your credentials on a new device. Requires a passkey that was previously registered for this identity.

const recovered = await backup.recoverCredentials(keyHash);
console.log('Recovered:', recovered.credentials.length, 'credentials');
// Credentials are automatically saved to localStorage

What happens:

  1. Fetches the encrypted archive from the relayer
  2. Triggers a WebAuthn ceremony to extract the same PRF seed
  3. Derives the same AES-GCM key
  4. Decrypts and restores all credentials to localStorage

backup.getBackupStatus

Check backup readiness for an identity.

const status = await backup.getBackupStatus(keyHash);
console.log('Has archive:', status.hasArchive);
console.log('PRF supported:', status.prfSupported);
console.log('Guardians:', status.guardianCount);

Returns: BackupStatus

FieldTypeDescription
hasArchivebooleanWhether an encrypted backup exists
archiveVersionnumber | nullArchive schema version
archiveUpdatedAtnumber | nullLast update timestamp (epoch ms)
guardianCountnumberNumber of active social recovery guardians
prfSupportedbooleanWhether PRF extension is available

WalletBackupManager.checkPlatformSupport (static)

Check if the current platform supports PRF-based backup.

const support = await WalletBackupManager.checkPlatformSupport();
console.log('WebAuthn:', support.webauthnSupported);
console.log('PRF:', support.prfSupported);
 
if (!support.prfSupported) {
  showWarning('Your device uses weaker backup encryption');
}

InjectedWalletAdapter

import { InjectedWalletAdapter } from '@veridex/sdk';
 
const adapter = new InjectedWalletAdapter();
const session = await adapter.createSessionFromInjected(sessionConfig);

Bridges MetaMask, WalletConnect, and other injected wallets into Veridex session key management.