Skip to content

Support Dynamic Token Decimals in PaymentRequirements #37

@jolestar

Description

@jolestar

Background

The x402 protocol's PaymentRequirements schema currently only includes the asset field (token address) but does not provide token metadata such as decimals or symbol. This makes it difficult for client applications to correctly format and display payment amounts without hardcoding token-specific information.

When displaying amounts to users, clients need to convert from atomic units (e.g., 100000 for USDC) to human-readable decimals (e.g., 0.1 USDC). Without knowing the token's decimal places, clients must either:

  1. Hardcode decimals for known tokens (not scalable)
  2. Query token metadata on-chain (adds latency and complexity)
  3. Maintain a token registry (maintenance burden)

This limitation becomes critical when x402 needs to support multiple payment tokens beyond USDC, such as:

  • USDT (6 decimals)
  • DAI (18 decimals)
  • Custom ERC20 tokens (varying decimals)

Problem

Current state:

  1. x402 protocol limitation: The PaymentRequirements schema only includes asset (token address), not token metadata
  2. Client-side workarounds required: Applications must implement their own solutions to determine token decimals
  3. Inconsistent developer experience: Each project handles token formatting differently

Example of the impact:

// Showcase frontend currently hardcodes USDC decimals
// examples/showcase/client/src/components/PaymentDialog.tsx:413
(parseFloat(paymentRequirements.extra.businessAmount) / 1000000).toFixed(6)

This approach works only for USDC and breaks when supporting other tokens with different decimal places.

Proposed Solution

Option 1: Add tokenDecimals to extra field (Recommended)

Modify @x402x/core's addSettlementExtra function to automatically include token decimals:

// typescript/packages/core/src/utils.ts
export function addSettlementExtra(
  requirements: PaymentRequirements,
  params: { ... }
): PaymentRequirements {
  const config = getNetworkConfig(requirements.network);
  
  const extra: SettlementExtra = {
    // ... existing fields ...
    tokenDecimals: config.usdc.decimals, // 👈 Add this
    tokenSymbol: config.usdc.symbol,     // 👈 Optional: also add symbol
  };
  
  return {
    ...requirements,
    extra: {
      ...requirements.extra,
      ...extra,
    },
  };
}

Frontend usage:

const decimals = paymentRequirements.extra.tokenDecimals || 6; // Fallback to USDC
const displayAmount = (parseFloat(atomicAmount) / Math.pow(10, decimals)).toFixed(decimals);

Option 2: Fetch token metadata from blockchain

Query token decimals on-chain when needed, but this adds latency and complexity.

Benefits

  1. Future-proof: Ready for multi-token support
  2. Better DX: Developers don't need to manage token metadata
  3. Consistent: All SDK users get correct decimals automatically
  4. Backward compatible: Uses extra field, doesn't break x402 protocol

Related Files

  • typescript/packages/core/src/utils.ts - addSettlementExtra() function
  • typescript/packages/core/src/types.ts - SettlementExtra type definition
  • typescript/packages/core/src/config.ts - Network configs with USDC info
  • examples/showcase/client/src/components/PaymentDialog.tsx - Frontend display logic

Timeline

Priority: Medium

This should be implemented when x402 adds support for payment tokens other than USDC. The current USDC-only implementation can continue using hardcoded decimals without issues.

Trigger conditions:

  • When adding support for USDT, DAI, or other ERC20 tokens
  • When allowing resource servers to accept multiple token types
  • When building client SDKs that need to support various tokens

Context

This issue was identified during the dynamic facilitator fee implementation, where we noticed that client applications lack standardized token metadata in PaymentRequirements.

Related topics:

  • Multi-token payment support
  • Client SDK development
  • Payment UI/UX improvements
  • Gas metrics monitoring implementation
  • Dynamic fee calculation integration

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions