@dcprotocol/client

Universal TypeScript SDK for integrating DCP into any AI agent or application. Automatically detects local or relay mode and provides a simple async API.

Installation

npm install @dcprotocol/client

Quick Start

import { createDCPClient } from '@dcprotocol/client';

// Create client (auto-detects local server or relay)
const client = await createDCPClient({
  agentName: 'my-trading-bot'
});

// Get wallet address
const { address } = await client.getAddress('solana');
console.log('Solana:', address);

// Sign transaction
const { signed_tx, signature } = await client.signTx({
  chain: 'solana',
  unsigned_tx: unsignedTxBase64,
  amount: 1.5,
  currency: 'SOL'
});

// Read credential
const { value } = await client.readCredential({
  scope: 'identity.email'
});

console.log('Email:', value);

Overview

The DCP client provides a unified interface for both local and remote vault access:

  • Local mode: Connects to @dcprotocol/server on localhost:8420
  • Relay mode: Connects via @dcprotocol/relay using pairing token
  • Auto-detection: Tries local first, falls back to relay if configured

Architecture

LOCAL MODE:
┌─────────────────────┐
│  Your Agent         │
│  @dcprotocol/client │
└──────────┬──────────┘
           │ HTTP
           ▼
┌─────────────────────┐
│  @dcprotocol/server │ localhost:8420
└──────────┬──────────┘
           ▼
┌─────────────────────┐
│  Vault (SQLite)     │
└─────────────────────┘

RELAY MODE (VPS):
┌─────────────────────┐
│  Your Agent (VPS)   │
│  @dcprotocol/client │
└──────────┬──────────┘
           │ WSS
           ▼
┌─────────────────────┐      ┌─────────────────┐
│  Relay Server       │◀────▶│  Desktop Vault  │
│  relay.dcp.1ly.store│      │  (Your Machine) │
└─────────────────────┘      └─────────────────┘

API Reference

createDCPClient(options)

Create and initialize DCP client.

OptionTypeDescription
agentNamestringAgent identifier for audit logs
pairingTokenstring?Pairing token for relay mode
localUrlstring?Local server URL (default: http://127.0.0.1:8420)
relayUrlstring?Relay server URL (default: wss://relay.dcp.1ly.store)
// Local mode (auto-detect)
const client = await createDCPClient({
  agentName: 'my-bot'
});

// Relay mode (VPS)
const client = await createDCPClient({
  agentName: 'production-bot',
  pairingToken: process.env.DCP_PAIRING_TOKEN
});

// Custom local server
const client = await createDCPClient({
  agentName: 'dev-bot',
  localUrl: 'http://localhost:3000'
});

client.getAddress(chain)

Get public address for a blockchain wallet.

ParameterTypeDescription
chainstring'solana' | 'ethereum' | 'base'

Returns:

{
  address: string;  // Public address
  chain: string;    // Chain name
}

Example:

const { address } = await client.getAddress('solana');
console.log(`Solana: ${address}`);

const { address: ethAddress } = await client.getAddress('ethereum');
console.log(`Ethereum: ${ethAddress}`);

client.signTx(params)

Sign a blockchain transaction (requires user approval).

ParameterTypeDescription
chainstring'solana' | 'ethereum' | 'base'
unsigned_txstringBase64-encoded unsigned transaction
amountnumberTransaction amount
currencystring'SOL' | 'ETH' | 'BASE_ETH' | 'USDC'
recipientstring?Recipient address (optional)

Returns:

{
  signed_tx: string;   // Base64-encoded signed transaction
  signature: string;   // Transaction signature
}

Example:

import { Transaction } from '@solana/web3.js';

// Create unsigned transaction
const transaction = new Transaction().add(
  SystemProgram.transfer({
    fromPubkey: fromAddress,
    toPubkey: toAddress,
    lamports: 1_500_000_000 // 1.5 SOL
  })
);

// Serialize to base64
const unsignedTx = transaction.serialize({ requireAllSignatures: false })
  .toString('base64');

// Sign with DCP
const { signed_tx, signature } = await client.signTx({
  chain: 'solana',
  unsigned_tx: unsignedTx,
  amount: 1.5,
  currency: 'SOL',
  recipient: toAddress.toBase58()
});

console.log(`Signature: ${signature}`);

// Broadcast signed transaction
const txBuffer = Buffer.from(signed_tx, 'base64');
const sig = await connection.sendRawTransaction(txBuffer);
console.log(`TX: ${sig}`);

client.readCredential(params)

Read stored credential from vault (requires user approval).

ParameterTypeDescription
scopestringData scope (e.g., "identity.email")

Returns:

{
  value: string;       // Decrypted value
  scope: string;       // Scope identifier
  sensitivity: string; // 'standard' | 'sensitive' | 'critical'
}

Example:

// Read email
const { value: email } = await client.readCredential({
  scope: 'identity.email'
});

// Read API key
const { value: apiKey } = await client.readCredential({
  scope: 'api.openai'
});

// Use in API call
const openai = new OpenAI({ apiKey });

client.budgetCheck(params)

Check if transaction is within budget limits.

ParameterTypeDescription
chainstring'solana' | 'ethereum' | 'base'
amountnumberTransaction amount
currencystring'SOL' | 'ETH' | 'BASE_ETH' | 'USDC'

Returns:

{
  allowed: boolean;            // Can transaction proceed?
  requires_approval: boolean;  // Needs user approval?
  remaining_daily: number;     // Remaining daily budget
  remaining_tx: number;        // Remaining per-tx budget
  reason?: string;             // Error reason if not allowed
}

Example:

const check = await client.budgetCheck({
  chain: 'solana',
  amount: 1.5,
  currency: 'SOL'
});

if (!check.allowed) {
  console.error(`Cannot send: ${check.reason}`);
  return;
}

if (check.requires_approval) {
  console.log('This will require user approval');
} else {
  console.log('Under auto-approve threshold');
}

console.log(`Remaining today: ${check.remaining_daily} SOL`);

client.disconnect()

Close client connection (important for relay mode).

await client.disconnect();

Complete Examples

Solana Trading Bot

import { createDCPClient } from '@dcprotocol/client';
import { Connection, PublicKey, Transaction, SystemProgram } from '@solana/web3.js';

const SOLANA_RPC = 'https://api.mainnet-beta.solana.com';

async function main() {
  // Initialize DCP client
  const dcp = await createDCPClient({
    agentName: 'solana-trader'
  });

  // Get Solana address
  const { address } = await dcp.getAddress('solana');
  console.log(`Wallet: ${address}`);

  // Connect to Solana
  const connection = new Connection(SOLANA_RPC);
  const pubkey = new PublicKey(address);

  // Check balance
  const balance = await connection.getBalance(pubkey);
  console.log(`Balance: ${balance / 1e9} SOL`);

  // Send SOL
  const recipient = new PublicKey('RECIPIENT_ADDRESS');
  const amount = 0.1; // SOL

  // Check budget first
  const budgetCheck = await dcp.budgetCheck({
    chain: 'solana',
    amount,
    currency: 'SOL'
  });

  if (!budgetCheck.allowed) {
    console.error(`Budget exceeded: ${budgetCheck.reason}`);
    return;
  }

  // Create transaction
  const transaction = new Transaction().add(
    SystemProgram.transfer({
      fromPubkey: pubkey,
      toPubkey: recipient,
      lamports: amount * 1e9
    })
  );

  // Get recent blockhash
  const { blockhash } = await connection.getLatestBlockhash();
  transaction.recentBlockhash = blockhash;
  transaction.feePayer = pubkey;

  // Serialize unsigned transaction
  const unsignedTx = transaction.serialize({
    requireAllSignatures: false
  }).toString('base64');

  // Sign with DCP
  const { signed_tx, signature } = await dcp.signTx({
    chain: 'solana',
    unsigned_tx: unsignedTx,
    amount,
    currency: 'SOL',
    recipient: recipient.toBase58()
  });

  console.log(`Signed! Signature: ${signature}`);

  // Broadcast
  const txBuffer = Buffer.from(signed_tx, 'base64');
  const txSig = await connection.sendRawTransaction(txBuffer);

  console.log(`Transaction: https://solscan.io/tx/${txSig}`);

  // Cleanup
  await dcp.disconnect();
}

main().catch(console.error);

Ethereum Contract Interaction

import { createDCPClient } from '@dcprotocol/client';
import { ethers } from 'ethers';

async function main() {
  const dcp = await createDCPClient({
    agentName: 'eth-agent'
  });

  // Get Ethereum address
  const { address } = await dcp.getAddress('ethereum');
  console.log(`ETH Address: ${address}`);

  // Connect to Ethereum
  const provider = new ethers.JsonRpcProvider('https://eth-mainnet.g.alchemy.com/v2/KEY');

  // Create transaction
  const tx = {
    to: '0xRECIPIENT',
    value: ethers.parseEther('0.05'),
    gasLimit: 21000,
    gasPrice: await provider.getFeeData().then(d => d.gasPrice)
  };

  // Serialize unsigned transaction
  const unsignedTx = ethers.Transaction.from(tx).unsignedSerialized;

  // Sign with DCP
  const { signed_tx } = await dcp.signTx({
    chain: 'ethereum',
    unsigned_tx: Buffer.from(unsignedTx.slice(2), 'hex').toString('base64'),
    amount: 0.05,
    currency: 'ETH'
  });

  // Broadcast
  const signedTxHex = '0x' + Buffer.from(signed_tx, 'base64').toString('hex');
  const receipt = await provider.broadcastTransaction(signedTxHex);

  console.log(`TX: https://etherscan.io/tx/${receipt.hash}`);

  await dcp.disconnect();
}

main().catch(console.error);

Multi-Chain Agent

import { createDCPClient } from '@dcprotocol/client';

async function main() {
  const dcp = await createDCPClient({
    agentName: 'multi-chain-bot'
  });

  // Get all addresses
  const chains = ['solana', 'ethereum', 'base'] as const;
  const addresses = await Promise.all(
    chains.map(async (chain) => {
      const { address } = await dcp.getAddress(chain);
      return { chain, address };
    })
  );

  console.log('Wallet Addresses:');
  addresses.forEach(({ chain, address }) => {
    console.log(`  ${chain}: ${address}`);
  });

  // Read API keys
  const { value: solscanKey } = await dcp.readCredential({
    scope: 'api.solscan'
  });

  const { value: etherscanKey } = await dcp.readCredential({
    scope: 'api.etherscan'
  });

  console.log('API Keys loaded successfully');

  await dcp.disconnect();
}

main().catch(console.error);

Environment Variables

VariableDefaultDescription
DCP_LOCAL_URLhttp://127.0.0.1:8420Local server URL
DCP_RELAY_URLwss://relay.dcp.1ly.storeRelay server URL
DCP_PAIRING_TOKENundefinedPairing token for relay mode

Error Handling

import { createDCPClient } from '@dcprotocol/client';

async function main() {
  const dcp = await createDCPClient({
    agentName: 'error-handler-bot'
  });

  try {
    const { signed_tx } = await dcp.signTx({
      chain: 'solana',
      unsigned_tx: unsignedTx,
      amount: 100, // Too high!
      currency: 'SOL'
    });
  } catch (error) {
    if (error.message.includes('budget')) {
      console.error('Transaction exceeds budget limits');
    } else if (error.message.includes('denied')) {
      console.error('User denied the request');
    } else if (error.message.includes('locked')) {
      console.error('Vault is locked');
    } else {
      console.error('Unexpected error:', error);
    }
  }

  await dcp.disconnect();
}

main().catch(console.error);

TypeScript Types

import type {
  DCPClient,
  GetAddressResult,
  SignTxParams,
  SignTxResult,
  ReadCredentialParams,
  ReadCredentialResult,
  BudgetCheckParams,
  BudgetCheckResult
} from '@dcprotocol/client';

// Chain types
type Chain = 'solana' | 'ethereum' | 'base';
type Currency = 'SOL' | 'ETH' | 'BASE_ETH' | 'USDC';

// Client creation options
interface CreateClientOptions {
  agentName: string;
  pairingToken?: string;
  localUrl?: string;
  relayUrl?: string;
}

See Also