avax-warp-pay
v2.3.1
Published
Cross-chain payment receipt SDK for Avalanche Subnets using Teleporter
Maintainers
Readme
avax-warp-pay
Cross-chain payment SDK for Avalanche implementing HTTP 402 Payment Required.
Send payments on Chain A, verify them on Chain B — powered by Avalanche Teleporter.
Installation
npm install avax-warp-pay ethers@^6Quick Start
import { Warp402 } from "avax-warp-pay";
import { ethers } from "ethers";
const warp = new Warp402({
privateKey: process.env.PRIVATE_KEY!,
senderChain: {
rpc: process.env.SENDER_RPC!,
chainId: +process.env.SENDER_CHAIN_ID!,
blockchainId: process.env.SENDER_BLOCKCHAIN_ID!,
messenger: "0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf",
sender: process.env.SENDER_ADDRESS!
},
receiverChain: {
rpc: process.env.RECEIVER_RPC!,
chainId: +process.env.RECEIVER_CHAIN_ID!,
blockchainId: process.env.RECEIVER_BLOCKCHAIN_ID!,
messenger: "0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf",
receiver: process.env.RECEIVER_ADDRESS!
}
});
// Send payment
const paymentId = await warp.pay(ethers.parseEther("0.1"));
// Wait for ICM Relayer to deliver message (~10-30s)
await new Promise(r => setTimeout(r, 15000));
// Verify on receiver chain
const verified = await warp.verify(paymentId);
console.log("Verified:", verified); // true ✅⚠️ Important: ICM Relayer Required
Cross-chain messages need the ICM Relayer to work!
The SDK sends messages from Chain A → Chain B, but you need the Avalanche ICM Relayer running to actually deliver these messages.
Quick Setup (Local Development)
# 1. Start your Avalanche network
avalanche network start
# 2. Start the ICM Relayer (in a separate terminal)
avalanche interchain relayer start --local
# 3. Now use the SDK - verify() will return true! ✅For Production (Fuji/Mainnet)
# Configure relayer for your subnets
avalanche interchain relayer configure
# Start relayer
avalanche interchain relayer startChecking Relayer Status
# View recent relayer activity
avalanche interchain relayer logs --local --last 10
# Check if relayer is running
ps aux | grep relayerWithout the relayer:
- ✅
pay()works (sends transaction on sender chain) - ❌
verify()returnsfalse(message never reaches receiver)
With the relayer:
- ✅
pay()works - ✅
verify()returnstrue(after 10-30 seconds) 🎉
Basic API
warp.pay(amount)
Send a payment from the sender chain.
const paymentId = await warp.pay(ethers.parseEther("1"));
// Returns the secure payment ID (different from input ID for security)warp.verify(paymentId)
Check if the payment has been delivered to the receiver chain.
const verified = await warp.verify(paymentId);
// Returns true after ICM Relayer delivers the message (~10-30 seconds)warp.consume(paymentId)
Mark a verified payment as consumed (prevents replay).
await warp.consume(paymentId);
// Only works after verify() returns trueConfiguration
These values must point to your deployed WarpSender & WarpReceiver contracts:
PRIVATE_KEY=0x...
SENDER_RPC=...
SENDER_CHAIN_ID=...
SENDER_BLOCKCHAIN_ID=...
SENDER_ADDRESS=0x...
RECEIVER_RPC=...
RECEIVER_CHAIN_ID=...
RECEIVER_BLOCKCHAIN_ID=...
RECEIVER_ADDRESS=0x...Teleporter Messenger (Fuji): 0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf
Contract Deployment
Warp-402 requires two contracts:
WarpSender— deployed on the payment chainWarpReceiver— deployed on the verification chain
Option 1: CLI Deployment (Easiest - 30 seconds)
Deploy contracts with a single command:
npx avax-warp-pay deploy \
--sender-rpc http://127.0.0.1:9650/ext/bc/C/rpc \
--sender-chain-id 43112 \
--sender-blockchain-id 0x7fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5 \
--receiver-rpc http://127.0.0.1:9650/ext/bc/subnet/rpc \
--receiver-chain-id 99999 \
--receiver-blockchain-id 0xc063de20d9e6e3b3e5b0f5e1e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5 \
--private-key $PRIVATE_KEYCLI Options Explained:
| Option | Required | Description | Example |
|--------|----------|-------------|---------|
| --sender-rpc | ✅ Yes | RPC URL for payment chain | http://localhost:9650/ext/bc/C/rpc |
| --sender-chain-id | ✅ Yes | Chain ID of payment chain | 43112 (Fuji) or 43114 (Mainnet) |
| --sender-blockchain-id | ✅ Yes | Blockchain ID (hex with 0x) | 0x7fc93d85c6d62c5b... |
| --receiver-rpc | ✅ Yes | RPC URL for verification chain | http://localhost:9650/ext/bc/subnet/rpc |
| --receiver-chain-id | ✅ Yes | Chain ID of verification chain | 99999 |
| --receiver-blockchain-id | ✅ Yes | Blockchain ID (hex with 0x) | 0xc063de20d9e6e3b3... |
| --private-key | ✅ Yes | Deployer private key | 0x... or $PRIVATE_KEY |
| --messenger | ❌ No | Custom Teleporter Messenger | Defaults to Fuji messenger |
Example Output:
🚀 Deploying Warp-402 contracts...
📍 Deploying WarpSender on Chain 43112...
✅ Deployed at: 0x5aa01B3b5877255cE50cc55e8986a7a5fe29C70e
📍 Deploying WarpReceiver on Chain 99999...
✅ Deployed at: 0x52C84043CD9c865236f11d9Fc9F56aa003c1f922
🔗 Configuring cross-chain handshake...
✅ Configured successfully!
╔════════════════════════════════════════════════════════════════╗
║ ✅ DEPLOYMENT SUCCESSFUL! ║
╚════════════════════════════════════════════════════════════════╝
💾 Add these to your .env file:
SENDER_ADDRESS=0x5aa01B3b5877255cE50cc55e8986a7a5fe29C70e
RECEIVER_ADDRESS=0x52C84043CD9c865236f11d9Fc9F56aa003c1f922Using Environment Variables:
# Set in your .env file
PRIVATE_KEY=0x...
SENDER_RPC=http://127.0.0.1:9650/ext/bc/C/rpc
SENDER_CHAIN_ID=43112
SENDER_BLOCKCHAIN_ID=0x7fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5
RECEIVER_RPC=http://127.0.0.1:9650/ext/bc/subnet/rpc
RECEIVER_CHAIN_ID=99999
RECEIVER_BLOCKCHAIN_ID=0xc063de20d9e6e3b3e5b0f5e1e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5
# Then deploy (no flags needed!)
npx avax-warp-pay deployOption 2: SDK Code Deployment (Programmatic)
Deploy contracts directly from your TypeScript code:
import { Warp402Factory } from "avax-warp-pay";
// Automated deployment with configuration
const warp = await Warp402Factory.quickSetup({
privateKey: process.env.PRIVATE_KEY!,
// Payment chain (where users pay)
senderChain: {
rpc: "http://127.0.0.1:9650/ext/bc/C/rpc",
chainId: 43112,
blockchainId: "0x7fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5",
messenger: "0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf" // Optional
},
// Verification chain (where you verify)
receiverChain: {
rpc: "http://127.0.0.1:9650/ext/bc/subnet/rpc",
chainId: 99999,
blockchainId: "0xc063de20d9e6e3b3e5b0f5e1e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5",
messenger: "0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf" // Optional
}
});
// ✅ Contracts deployed and configured!
// ✅ SDK ready to use immediately
// Access deployed addresses
const senderAddress = warp.sender.address;
const receiverAddress = warp.receiver.address;
console.log("✅ WarpSender deployed at:", senderAddress);
console.log("✅ WarpReceiver deployed at:", receiverAddress);
// Save these addresses to your .env file
console.log("\n💾 Add to .env:");
console.log(`SENDER_ADDRESS=${senderAddress}`);
console.log(`RECEIVER_ADDRESS=${receiverAddress}`);
// Start using immediately
const paymentId = await warp.pay(ethers.parseEther("0.1"));Alternative: Deploy Only (Get Addresses Without SDK Init):
import { Warp402Factory } from "avax-warp-pay";
// Just deploy contracts, don't initialize SDK
const result = await Warp402Factory.deployOnly({
privateKey: process.env.PRIVATE_KEY!,
senderChain: {
rpc: "http://127.0.0.1:9650/ext/bc/C/rpc",
chainId: 43112,
blockchainId: "0x7fc93d85..."
},
receiverChain: {
rpc: "http://127.0.0.1:9650/ext/bc/subnet/rpc",
chainId: 99999,
blockchainId: "0xc063de20..."
}
});
// Get deployment info
console.log("Sender Address:", result.senderAddress);
console.log("Receiver Address:", result.receiverAddress);
console.log("Sender TX Hash:", result.senderTxHash);
console.log("Receiver TX Hash:", result.receiverTxHash);Option 3: Manual Foundry Deployment
Full deployment guide: https://github.com/jayasurya0007/wrap-x402/tree/main/wrapx402
SDK Features
✅ Core Payment Methods
warp.pay(amount)- Send cross-chain paymentwarp.verify(paymentId)- Verify payment receiptwarp.consume(paymentId)- Mark payment as usedwarp.getPayment(paymentId)- Get payment details
✅ Automated Deployment
- CLI Tool:
npx avax-warp-pay deploy- One-command deployment - SDK Factory:
Warp402Factory.quickSetup()- Programmatic deployment - Manual Option: Full Foundry support for custom setups
✅ Contract Interaction
- Sender Contract:
warp.sender.contract- Access WarpSender methods - Receiver Contract:
warp.receiver.contract- Access WarpReceiver methods - Configuration:
warp.sender.getConfiguration()- Read contract settings
✅ TypeScript Support
- Full TypeScript definitions included
- Typed interfaces for all methods
- Auto-completion in IDEs
Examples
Complete Payment Flow
// 1. Send payment on sender chain
const paymentId = await warp.pay(ethers.parseEther("0.1"));
console.log("Payment sent:", paymentId);
// 2. Wait for ICM Relayer to deliver message
console.log("Waiting for cross-chain delivery...");
await new Promise(r => setTimeout(r, 15000)); // 15 seconds
// 3. Verify payment on receiver chain
const verified = await warp.verify(paymentId);
console.log("Verified:", verified); // true ✅
// 4. Consume payment (mark as used)
if (verified) {
await warp.consume(paymentId);
console.log("Payment consumed!");
}Production Usage Pattern
// Send payment immediately
const paymentId = await warp.pay(amount);
// Poll for verification (recommended for production)
async function waitForVerification(paymentId: string, timeout = 60000) {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
const verified = await warp.verify(paymentId);
if (verified) {
return true; // ✅ Payment verified!
}
// Wait 2 seconds before checking again
await new Promise(r => setTimeout(r, 2000));
}
return false; // ❌ Timeout
}
// Usage
const verified = await waitForVerification(paymentId);
if (verified) {
// Grant access, deliver content, etc.
await warp.consume(paymentId);
}Troubleshooting
Problem: verify() always returns false
# Check if ICM Relayer is running
avalanche interchain relayer logs --local --last 5
# If not running, start it:
avalanche interchain relayer start --localProblem: Relayer shows "message delivered" but verify() still false
This was a bug in v2.2.0 and earlier. Update to v2.3.0+:
npm install avax-warp-pay@latestProblem: Want to see what's happening
Enable debug logging:
import { setLogLevel, LogLevel } from 'avax-warp-pay';
setLogLevel(LogLevel.DEBUG); // See all SDK operationsLicense
MIT © 2025 Warp-402 Team
