Quick Start
Build a passkey wallet and send your first transaction in under 5 minutes.
Frontend Required: Passkey operations (register, authenticate) use WebAuthn browser APIs and must run in a client-side browser context (HTTPS or localhost). They cannot run in Node.js, server components, or terminal scripts. All code examples below must execute in a browser — either in a React/Next.js client component, a plain HTML page with a <script> tag, or any other browser environment.
1. Initialize the SDK
import { createSDK } from '@veridex/sdk';
// Initialize with testnet (default)
const sdk = createSDK('base', {
network: 'testnet',
relayerUrl: 'https://relayer.veridex.network',
});2. Register a Passkey
// Prompt user to create a passkey with biometric
const credential = await sdk.passkey.register(
'user@example.com', // Username (displayed in passkey prompt)
'My Veridex Wallet' // Display name
);
console.log('Credential ID:', credential.credentialId);
console.log('Key Hash:', credential.keyHash);
console.log('Vault Address:', sdk.getVaultAddress());The vault address is deterministic — the same passkey always generates the same address across all EVM chains.
3. Fund the Vault
For testnet, use these faucets:
| Chain | Faucet |
|---|---|
| Base Sepolia | base.org/faucet (opens in a new tab) |
| Optimism Sepolia | app.optimism.io/faucet (opens in a new tab) |
| Arbitrum Sepolia | faucet.quicknode.com/arbitrum (opens in a new tab) |
| Stacks Testnet | explorer.hiro.so/sandbox/faucet (opens in a new tab) |
4. Prepare and Execute a Transfer
// Prepare a transfer (shows gas cost before signing)
const prepared = await sdk.prepareTransfer({
targetChain: 10004, // Base Sepolia Wormhole chain ID
token: '0x036CbD53842c5426634e7929541eC2318f3dCF7e', // USDC
recipient: '0x742d35Cc6634C0532925a3b844Bc9e7595f5A234',
amount: 1000000n, // 1 USDC (6 decimals)
});
// Get human-readable summary
const summary = await sdk.getTransactionSummary(prepared);
console.log(summary.title); // "Transfer"
console.log(summary.description); // "Send 1.0 USDC to 0x742d...5A234"
console.log('Gas cost:', prepared.formattedCost);
// Execute with a signer (signs with passkey, then dispatches)
const result = await sdk.executeTransfer(prepared, signer);
console.log('Transaction hash:', result.transactionHash);5. Check Spending Limits
import { ethers } from 'ethers';
// Check current limits
const limits = await sdk.getSpendingLimits();
console.log('Daily remaining:', limits.dailyRemaining);
// Check if a specific amount is allowed
const check = await sdk.checkSpendingLimit(ethers.parseEther('1.0'));
if (check.allowed) {
console.log('Transfer allowed!');
} else {
console.log('Blocked:', check.message);
}
// Get formatted limits for UI
const formatted = await sdk.getFormattedSpendingLimits();
console.log(`${formatted.dailyUsedPercentage}% of daily limit used`);6. Check Balances
// Get all token balances on the current chain
const portfolio = await sdk.getVaultBalances();
for (const entry of portfolio.tokens) {
console.log(`${entry.token.symbol}: ${entry.formatted}`);
}
console.log('Total USD:', portfolio.totalUsdValue);Complete Example
import { createSDK } from '@veridex/sdk';
import { ethers } from 'ethers';
async function main() {
// 1. Initialize
const sdk = createSDK('base', {
network: 'testnet',
relayerUrl: 'https://relayer.veridex.network',
});
// 2. Check if user has existing passkeys
const storedCredentials = sdk.passkey.getAllStoredCredentials();
if (storedCredentials.length === 0) {
// 3. Register new passkey (prompts biometric)
console.log('Registering passkey...');
await sdk.passkey.register('user@example.com', 'My Wallet');
} else {
// 4. Authenticate with existing passkey (shows passkey picker)
console.log('Logging in...');
await sdk.passkey.authenticate();
}
// 5. Get vault info
const vault = sdk.getVaultAddress();
console.log('Vault:', vault);
// 6. Check spending limits
const limits = await sdk.getSpendingLimits();
console.log('Daily limit remaining:', limits.dailyRemaining);
// 7. Prepare a transfer
const prepared = await sdk.prepareTransfer({
targetChain: 10004,
token: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
recipient: '0x742d35Cc6634C0532925a3b844Bc9e7595f5A234',
amount: 100000n, // 0.1 USDC
});
// 8. Show summary to user
const summary = await sdk.getTransactionSummary(prepared);
console.log(`${summary.title}: ${summary.description}`);
console.log('Gas cost:', prepared.formattedCost);
if (summary.risks.length > 0) {
console.warn('Risks:', summary.risks.map(r => r.message));
}
}
main().catch(console.error);Multi-Chain Initialization (Advanced)
For apps that need multi-chain support, use the VeridexSDK class directly with an EVMClient:
import { VeridexSDK } from '@veridex/sdk';
import { EVMClient } from '@veridex/sdk/chains/evm';
// Create the EVM chain client (hub chain)
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,
persistWallet: true,
testnet: true,
relayerUrl: '/api/relayer',
relayerApiKey: process.env.NEXT_PUBLIC_RELAYER_API_KEY,
});The createSDK shorthand is great for getting started. For production apps with multi-chain vaults, use VeridexSDK + EVMClient directly — see the React Integration guide.
Gasless Transfer (via Relayer)
Send tokens without the user needing gas:
// Transfer via relayer — user pays no gas
const result = await sdk.transferViaRelayer({
targetChain: 10004,
token: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
recipient: '0x742d35Cc6634C0532925a3b844Bc9e7595f5A234',
amount: 1000000n, // 1 USDC
});
console.log('Transaction hash:', result.transactionHash);Agent SDK Quick Start
For AI agents that need to make autonomous payments:
import { createAgentWallet } from '@veridex/agentic-payments';
async function agentQuickStart() {
// 1. Create agent with spending limits and MCP tools
const agent = await createAgentWallet({
masterCredential: {
credentialId: process.env.CREDENTIAL_ID!,
publicKeyX: BigInt(process.env.PUBLIC_KEY_X!),
publicKeyY: BigInt(process.env.PUBLIC_KEY_Y!),
keyHash: process.env.KEY_HASH!,
},
session: {
dailyLimitUSD: 50,
perTransactionLimitUSD: 10,
expiryHours: 24,
allowedChains: [10004], // Base Sepolia
},
mcp: { enabled: true }, // Enable MCP tools for AI model integration
relayerUrl: 'https://relay.veridex.network',
});
// 2. Get MCP tools (for Gemini, Claude, GPT function calling)
const tools = agent.getMCPTools();
console.log('Available tools:', tools.map(t => t.name));
// 3. Fetch paid data (x402 handled automatically)
const response = await agent.fetch('https://paid-api.example.com/data');
const data = await response.json();
// 4. Direct payment
const receipt = await agent.pay({
chain: 10004,
token: 'USDC',
amount: '1000000',
recipient: '0x...',
});
// 5. Check spending
const status = agent.getSessionStatus();
console.log(`Spent: $${status.totalSpentUSD} / $${status.limits!.dailyLimitUSD}`);
// 6. Clean up
await agent.revokeSession();
}Next.js SSR-Safe Pattern
In Next.js, use dynamic imports to avoid SSR issues:
// lib/veridex-client.ts
let sdkInstance: any = 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;
}The await import('@veridex/sdk') ensures the SDK is only loaded in the browser, never during server-side rendering. See the Next.js Integration guide for the full pattern.
React Integration
'use client';
import { useState } from 'react';
import { createSDK } from '@veridex/sdk';
const sdk = createSDK('base', { network: 'testnet' });
export function WalletButton() {
const [vault, setVault] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const handleConnect = async () => {
setLoading(true);
try {
// Check for existing passkey
const hasStored = sdk.passkey.hasStoredCredential();
if (hasStored) {
await sdk.passkey.authenticate();
} else {
await sdk.passkey.register('user@example.com', 'My Wallet');
sdk.passkey.saveToLocalStorage();
}
setVault(sdk.getVaultAddress());
} catch (error) {
console.error('Failed to connect:', error);
}
setLoading(false);
};
if (vault) {
return (
<div>
<p>Connected: {vault.slice(0, 6)}...{vault.slice(-4)}</p>
</div>
);
}
return (
<button onClick={handleConnect} disabled={loading}>
{loading ? 'Connecting...' : 'Connect with Passkey'}
</button>
);
}