Skip to main content
The @x402r/arbiter package provides everything arbiters need to resolve disputes: reviewing cases, making decisions, executing refunds, and integrating AI for automated resolution.

Prerequisites

Before starting, ensure you have:
  • Node.js 20+
  • A funded wallet on Base Sepolia designated as an arbiter
  • Understanding of the X402r refund lifecycle

Installation

npm install @x402r/arbiter @x402r/core viem

Setup

Create the X402rArbiter instance:
import { createPublicClient, createWalletClient, http } from 'viem';
import { baseSepolia } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';
import { X402rArbiter } from '@x402r/arbiter';
import { getNetworkConfig } from '@x402r/core';

const publicClient = createPublicClient({
  chain: baseSepolia,
  transport: http(),
});

const account = privateKeyToAccount(process.env.ARBITER_PRIVATE_KEY as `0x${string}`);
const walletClient = createWalletClient({
  account,
  chain: baseSepolia,
  transport: http(),
});

const config = getNetworkConfig('eip155:84532')!;

const arbiter = new X402rArbiter({
  publicClient,
  walletClient,
  operatorAddress: '0x...',
  refundRequestAddress: config.refundRequest,
  arbiterRegistryAddress: config.arbiterRegistry,
});

Review Pending Cases

Get refund requests awaiting your decision:
// Get paginated refund requests for a receiver
const { keys, total } = await arbiter.getPendingRefundRequests(
  0n,   // offset
  10n,  // count
  '0xReceiverAddress...' // optional: defaults to wallet account
);
console.log(`${total} cases to review, showing first ${keys.length}`);

// Review each case
for (const key of keys) {
  const request = await arbiter.getRefundRequestByKey(key);
  console.log(`Amount: ${request.amount}, Status: ${request.status}`);
}

Make a Decision

Approve or deny refund requests:
// Approve a refund request
const { txHash: approveTx } = await arbiter.approveRefundRequest(paymentInfo, 0n);
console.log('Approved:', approveTx);

// Deny a refund request
const { txHash: denyTx } = await arbiter.denyRefundRequest(paymentInfo, 0n);
console.log('Denied:', denyTx);

Execute Approved Refunds

After approving, execute the refund to transfer funds back to the payer:
// Approve the request first
await arbiter.approveRefundRequest(paymentInfo, 0n);

// Execute the refund (defaults to paymentInfo.maxAmount if no amount specified)
const { txHash } = await arbiter.executeRefundInEscrow(paymentInfo);
console.log('Refund executed:', txHash);

// Or specify a partial refund amount
const { txHash: partialTx } = await arbiter.executeRefundInEscrow(
  paymentInfo,
  BigInt('500000') // partial amount
);

Batch Operations

Process multiple cases efficiently:
// Approve multiple refunds
const results = await arbiter.batchApprove([
  { paymentInfo: paymentInfo1, nonce: 0n },
  { paymentInfo: paymentInfo2, nonce: 0n },
]);
console.log(`Approved ${results.length} refunds`);

// Deny multiple refunds
const denyResults = await arbiter.batchDeny([
  { paymentInfo: paymentInfo3, nonce: 0n },
]);

Register as an Arbiter

Register yourself in the on-chain ArbiterRegistry:
// Register with your API endpoint
const { txHash } = await arbiter.registerArbiter('https://arbiter.example.com/api/disputes');
console.log('Registered:', txHash);

// Check registration
const isRegistered = await arbiter.isArbiterRegistered(account.address);
console.log('Is registered:', isRegistered);

Watch for New Cases

Subscribe to incoming refund requests:
const { unsubscribe } = arbiter.watchNewCases((event) => {
  console.log('New case:', event);
});

// Watch for decisions (status updates)
const { unsubscribe: unsubDecisions } = arbiter.watchDecisions((event) => {
  console.log('Decision made:', event);
});

Complete Example

import { createPublicClient, createWalletClient, http } from 'viem';
import { baseSepolia } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';
import { X402rArbiter } from '@x402r/arbiter';
import { getNetworkConfig, RequestStatus } from '@x402r/core';

async function main() {
  const publicClient = createPublicClient({
    chain: baseSepolia,
    transport: http(),
  });

  const account = privateKeyToAccount(process.env.ARBITER_KEY as `0x${string}`);
  const walletClient = createWalletClient({
    account,
    chain: baseSepolia,
    transport: http(),
  });

  const config = getNetworkConfig('eip155:84532')!;

  const arbiter = new X402rArbiter({
    publicClient,
    walletClient,
    operatorAddress: '0x...',
    refundRequestAddress: config.refundRequest,
    arbiterRegistryAddress: config.arbiterRegistry,
  });

  // Register as an arbiter
  await arbiter.registerArbiter('https://arbiter.example.com/api');

  // Get pending cases
  const { keys, total } = await arbiter.getPendingRefundRequests(0n, 10n, '0xReceiver...');
  console.log(`${total} pending cases`);

  // Watch for new cases
  arbiter.watchNewCases(async (event) => {
    console.log('New case to review:', event);
  });

  console.log('Arbiter is watching for new cases...');
}

main().catch(console.error);

Next Steps