Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nhestrompia/shielded-x402/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The MultiChainCreditClient class enables fast credit-based payments across multiple chains (Base, Solana) using sequencer-authorized intents. The sequencer enforces real-time nonce/balance constraints, while per-chain relayers execute payments only after sequencer authorization.

Constructor

const client = new MultiChainCreditClient(config: MultiChainCreditClientConfig)
config
MultiChainCreditClientConfig
required
Configuration object for the client

Methods

pay

Execute a unified authorize + relay payment flow.
await client.pay(
  request: UnifiedPayRequestV1
): Promise<UnifiedPayResultV1>
request
UnifiedPayRequestV1
required
UnifiedPayResultV1
object

Example

import { MultiChainCreditClient } from '@shielded-x402/client';
import { normalizeHex, deriveAgentIdFromPubKey } from '@shielded-x402/shared-types';
import { createHash, sign, createPrivateKey } from 'node:crypto';

const client = new MultiChainCreditClient({
  sequencerUrl: 'http://127.0.0.1:3200',
  relayerUrls: {
    'eip155:8453': 'http://127.0.0.1:3100'
  }
});

const agentPubKey = '0x...';
const agentId = deriveAgentIdFromPubKey(agentPubKey);
const agentPrivateKeyPem = process.env.AGENT_ED25519_PRIVATE_KEY_PEM;

const result = await client.pay({
  chainRef: 'eip155:8453',
  amountMicros: '1500000', // $1.50
  merchant: {
    serviceRegistryId: 'demo/base',
    endpointUrl: 'https://merchant.example/pay'
  },
  merchantRequest: {
    url: 'https://merchant.example/pay',
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    bodyBase64: Buffer.from(JSON.stringify({ orderId: 'o-123' })).toString('base64')
  },
  agent: {
    agentId,
    agentPubKey,
    signatureScheme: 'ed25519-sha256-v1',
    agentNonce: '0',
    signIntent: async ({ canonicalBytes }) => {
      const digest = createHash('sha256').update(canonicalBytes).digest();
      const privateKey = createPrivateKey(agentPrivateKeyPem);
      return normalizeHex(`0x${sign(null, digest, privateKey).toString('hex')}`);
    }
  }
});

console.log('Auth ID:', result.authorize.authorization.authId);
console.log('Execution TX:', result.relay.executionTxHash);
console.log('Status:', result.relay.status);

authorize

Request sequencer authorization for a payment intent.
await client.authorize(
  request: AuthorizeRequestV1
): Promise<AuthorizeResponseV1>
request
AuthorizeRequestV1
required
AuthorizeResponseV1
object

Example

import {
  buildIntentTypedDataPayload,
  canonicalIntentBytes,
  deriveMerchantId
} from '@shielded-x402/shared-types';

const intent: IntentV1 = {
  version: 1,
  agentId,
  agentPubKey,
  signatureScheme: 'ed25519-sha256-v1',
  agentNonce: '0',
  amountMicros: '1500000',
  merchantId: deriveMerchantId({
    serviceRegistryId: 'demo/base',
    endpointUrl: 'https://merchant.example/pay'
  }),
  requiredChainRef: 'eip155:8453',
  expiresAt: String(Math.floor(Date.now() / 1000) + 300),
  requestId: '0x...'
};

const agentSig = signIntent(intent, privateKey);

const authResponse = await client.authorize({ intent, agentSig });
console.log('Auth ID:', authResponse.authorization.authId);

relayPay

Execute a payment via the chain-specific relayer.
await client.relayPay(
  request: RelayPayRequestV1
): Promise<RelayPayResponseV1>
request
RelayPayRequestV1
required
RelayPayResponseV1
object

adminCredit

Credit an agent’s balance (requires sequencerAdminToken).
await client.adminCredit(
  request: AdminCreditRequestV1
): Promise<AdminCreditResponseV1>
request
AdminCreditRequestV1
required
AdminCreditResponseV1
object
adminCredit is only for local test/dev environments. Production systems must use shielded settlement (FundingSignal) to credit balances.

Example

await client.adminCredit({
  agentId: '0x1234...',
  amountMicros: '10000000' // $10.00
});

latestCommitment

Fetch the latest commitment epoch metadata.
await client.latestCommitment(): Promise<LatestCommitmentResponseV1>
LatestCommitmentResponseV1
object

Example

const latest = await client.latestCommitment();
console.log('Epoch:', latest.latestEpochId);
console.log('Root:', latest.root);

commitmentProof

Fetch the inclusion proof for a specific authorization.
await client.commitmentProof(
  authId: Hex
): Promise<InclusionProofV1>
authId
Hex
required
Authorization ID to prove
InclusionProofV1
object

Example

const proof = await client.commitmentProof('0xabc123...');
console.log('Epoch:', proof.epochId);
console.log('Leaf:', proof.leafHash);
console.log('Proof siblings:', proof.siblings);

reclaim

Reclaim an expired or failed authorization.
await client.reclaim(
  request: ReclaimRequestV1
): Promise<{ ok: true; authId: Hex }>
request
ReclaimRequestV1
required

Example

await client.reclaim({
  authId: '0xexpired...',
  callerType: 'sequencer',
  requestedAt: String(Math.floor(Date.now() / 1000))
});

Multi-Chain Example

Execute payments on both Base and Solana:
const client = new MultiChainCreditClient({
  sequencerUrl: 'http://127.0.0.1:3200',
  relayerUrls: {
    'eip155:8453': 'http://127.0.0.1:3100',
    'solana:devnet': 'http://127.0.0.1:3101'
  },
  sequencerAdminToken: 'change-me'
});

// Credit agent balance
await client.adminCredit({
  agentId,
  amountMicros: '10000000'
});

// Base payment
const baseResult = await client.pay({
  chainRef: 'eip155:8453',
  amountMicros: '1500000',
  merchant: {
    serviceRegistryId: 'demo/base',
    endpointUrl: 'https://merchant.base.example/pay'
  },
  merchantRequest: { /* ... */ },
  agent: { agentNonce: '0', /* ... */ }
});

// Solana payment (nonce incremented)
const solanaResult = await client.pay({
  chainRef: 'solana:devnet',
  amountMicros: '2500000',
  merchant: {
    serviceRegistryId: 'demo/solana',
    endpointUrl: 'https://merchant.solana.example/pay'
  },
  merchantRequest: { /* ... */ },
  agent: { agentNonce: '1', /* ... */ }
});

console.log('Base TX:', baseResult.relay.executionTxHash);
console.log('Solana TX:', solanaResult.relay.executionTxHash);

Best Practices

Agent nonces must be strictly increasing. Track the current nonce locally:
let currentNonce = 0;

async function executePay(params: UnifiedPayRequestV1) {
  const result = await client.pay({
    ...params,
    agent: {
      ...params.agent,
      agentNonce: String(currentNonce++)
    }
  });
  return result;
}
Track remaining balance after each payment to avoid insufficient balance errors:
let balance = 10_000_000n; // micros

const result = await client.pay({ amountMicros: '1500000', ... });
balance -= 1_500_000n;

console.log('Remaining balance:', balance);
Handle authorization and relay errors separately:
try {
  const result = await client.pay(request);
  console.log('Success:', result.relay.status);
} catch (error) {
  if (error.message.includes('insufficient balance')) {
    // Top up balance
  } else if (error.message.includes('nonce')) {
    // Resync nonce
  } else {
    // Other error
  }
}
Periodically verify commitments are posted onchain:
const latest = await client.latestCommitment();
if (latest.postedTxHash) {
  console.log('Committed onchain:', latest.postedTxHash);
}

See Also

Build docs developers (and LLMs) love