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
| Condition | Escrow Timer | Can Request Refund | Can Release |
|---|
| Normal (unfrozen, period active) | Running | Yes | Partial only |
| Frozen | Paused | Yes | No |
| Escrow period passed (unfrozen) | Stopped | No | Full 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