Skip to main content
The Client SDK provides methods to interact with the escrow system, including freezing payments during disputes and querying escrow period timing. All methods documented on this page are fully functional.

Freeze Operations

Freezing a payment pauses the escrow timer, preventing the merchant from releasing funds while a dispute is being resolved. Freeze operations interact with the Freeze contract.

freezePayment

Freeze a payment to pause the escrow timer. Only the payer can freeze a payment.
const freezeAddress = '0x...'; // Freeze contract address

const { txHash } = await client.freezePayment(paymentInfo, freezeAddress);
console.log(`Payment frozen: ${txHash}`);

Signature

freezePayment(
  paymentInfo: PaymentInfo,
  freezeAddress: `0x${string}`
): Promise<{ txHash: `0x${string}` }>
Freezing is useful when:
  • You need more time to resolve a dispute with the merchant
  • You are waiting for additional information before deciding on a refund
  • The merchant is unresponsive to your refund request

unfreezePayment

Unfreeze a previously frozen payment. The receiver (merchant) or arbiter can unfreeze a payment once the dispute is resolved.
const { txHash } = await client.unfreezePayment(paymentInfo, freezeAddress);
console.log(`Payment unfrozen: ${txHash}`);

Signature

unfreezePayment(
  paymentInfo: PaymentInfo,
  freezeAddress: `0x${string}`
): Promise<{ txHash: `0x${string}` }>

isFrozen

Check whether a payment is currently frozen.
const frozen = await client.isFrozen(paymentInfo, freezeAddress);

if (frozen) {
  console.log('Payment is frozen - escrow timer paused');
} else {
  console.log('Payment is not frozen - escrow timer running');
}

Signature

isFrozen(
  paymentInfo: PaymentInfo,
  freezeAddress: `0x${string}`
): Promise<boolean>

Escrow Period Operations

These methods interact with the EscrowPeriod contract to query timing information about a payment’s escrow window.

getAuthorizationTime

Get the timestamp (in seconds) when a payment was authorized on-chain. This is the starting point of the escrow period.
const escrowPeriodAddress = '0x...'; // EscrowPeriod contract address

const authTime = await client.getAuthorizationTime(paymentInfo, escrowPeriodAddress);
const authDate = new Date(Number(authTime) * 1000);

console.log(`Payment authorized at: ${authDate.toISOString()}`);

Signature

getAuthorizationTime(
  paymentInfo: PaymentInfo,
  escrowPeriodAddress: `0x${string}`
): Promise<bigint>

isDuringEscrowPeriod

Check whether a payment is still within its escrow period. Returns true if the escrow period has not yet passed, meaning the payment can still be refunded.
const duringEscrow = await client.isDuringEscrowPeriod(
  paymentInfo,
  escrowPeriodAddress
);

if (duringEscrow) {
  console.log('Still in escrow period - refund is possible');
} else {
  console.log('Escrow period has passed - funds can be fully released');
}

Signature

isDuringEscrowPeriod(
  paymentInfo: PaymentInfo,
  escrowPeriodAddress: `0x${string}`
): Promise<boolean>
The method name is isDuringEscrowPeriod, not isEscrowPeriodPassed. It returns true when the escrow period is still active (refund window is open), and false when it has passed.

Understanding Escrow Timing

ConditionEscrow TimerCan Request RefundCan Release
Normal (unfrozen, period active)RunningYesPartial only
FrozenPausedYesNo
Escrow period passed (unfrozen)StoppedNoFull amount
The escrow period length is configured at the contract level when the EscrowPeriod condition is deployed. Common values are 7 days, 14 days, or 30 days. You can calculate the remaining time from getAuthorizationTime and the configured period.

Example: Escrow Status Dashboard

import { X402rClient } from '@x402r/client';
import type { PaymentInfo } from '@x402r/core';

async function showEscrowStatus(
  client: X402rClient,
  paymentInfo: PaymentInfo,
  freezeAddress: `0x${string}`,
  escrowPeriodAddress: `0x${string}`
) {
  // Query all escrow-related state
  const frozen = await client.isFrozen(paymentInfo, freezeAddress);
  const duringEscrow = await client.isDuringEscrowPeriod(
    paymentInfo,
    escrowPeriodAddress
  );
  const authTime = await client.getAuthorizationTime(
    paymentInfo,
    escrowPeriodAddress
  );

  const authDate = new Date(Number(authTime) * 1000);

  console.log('Escrow Status:');
  console.log(`  Authorized: ${authDate.toISOString()}`);
  console.log(`  Frozen: ${frozen ? 'Yes' : 'No'}`);
  console.log(`  During Escrow Period: ${duringEscrow ? 'Yes' : 'No'}`);

  if (frozen) {
    console.log('  Action: Timer paused due to freeze. Awaiting dispute resolution.');
  } else if (duringEscrow) {
    console.log('  Action: Escrow period active. Refund can still be requested.');
  } else {
    console.log('  Action: Escrow period passed. Merchant can fully release funds.');
  }
}

Example: Freeze and Request Refund

A common pattern is to freeze a payment before submitting a refund request, ensuring the merchant cannot release funds while the request is pending.
import { X402rClient } from '@x402r/client';
import type { PaymentInfo } from '@x402r/core';

async function freezeAndRequestRefund(
  client: X402rClient,
  paymentInfo: PaymentInfo,
  freezeAddress: `0x${string}`,
  refundAmount: bigint
) {
  // Step 1: Check if already frozen
  const alreadyFrozen = await client.isFrozen(paymentInfo, freezeAddress);

  if (!alreadyFrozen) {
    // Freeze the payment first
    const { txHash: freezeTx } = await client.freezePayment(
      paymentInfo,
      freezeAddress
    );
    console.log(`Payment frozen: ${freezeTx}`);
  }

  // Step 2: Check if refund request already exists
  const nonce = 0n;
  const hasRequest = await client.hasRefundRequest(paymentInfo, nonce);

  if (!hasRequest) {
    // Submit the refund request
    const { txHash: refundTx } = await client.requestRefund(
      paymentInfo,
      refundAmount,
      nonce
    );
    console.log(`Refund requested: ${refundTx}`);
  }

  // Step 3: Watch for resolution
  const { unsubscribe } = client.watchFreezeEvents(
    freezeAddress,
    (event) => {
      if (event.eventName === 'PaymentUnfrozen') {
        console.log('Payment was unfrozen - dispute may be resolved');
        unsubscribe();
      }
    }
  );
}

Freeze / Unfreeze Flow

Next Steps