Integration Guide
Complete step-by-step guide for integrating Pons Network into your dApp. Build cross-chain experiences with decentralized execution.
Table of Contents
- Getting Started
- Integration Steps
- Building Actions
- Transfer Flow
- Dynamic Fees
- Error Handling
- Production Deployment
Getting Started
Prerequisites
- Node.js 18+
- npm or yarn
- Basic understanding of:
- Cross-chain concepts
- Smart contracts
- TypeScript/JavaScript
- viem or ethers.js
Installation
npm install @pons-network/pons.js viem
Quick Test
import { calculateFeesSync, DEFAULT_FEES } from '@pons-network/pons.js';
import { parseUnits, formatUnits } from 'viem';
// Test fee calculation
const fees = calculateFeesSync(parseUnits('15', 6));
console.log(`15 USDC → ${formatUnits(fees.amountForAction, 6)} USDC after fees`);
Integration Steps
Step 1: Initialize PonsClient
import { PonsClient, Chain } from '@pons-network/pons.js';
const pons = await PonsClient.create({
from: Chain.SEPOLIA,
to: Chain.ARC_TESTNET,
sourceRpcUrl: process.env.SOURCE_RPC_URL,
destinationRpcUrl: process.env.DEST_RPC_URL,
});
// Get user's Smart Account address (deterministic!)
const smartAccount = await pons.calculateSmartAccountAddress(userAddress, 0n);
Step 2: Calculate Fees (Dynamic)
import { calculateFeesSync, calculateBurnForAction } from '@pons-network/pons.js';
// User chooses speed - fees are dynamic like ETH gas!
const feeOptions = {
fast: { indexerFee: parseUnits('0.2', 6), resolverFee: parseUnits('0.3', 6) },
standard: { indexerFee: parseUnits('0.1', 6), resolverFee: parseUnits('0.15', 6) },
economy: { indexerFee: parseUnits('0.05', 6), resolverFee: parseUnits('0.08', 6) },
};
// Calculate with chosen fee level
const fees = calculateFeesSync(parseUnits('15', 6), feeOptions.standard);
// Show to user
displayFees({
send: formatUnits(fees.burnAmount, 6),
receive: formatUnits(fees.amountForAction, 6),
speed: 'Standard (~15 min)',
});
Step 3: Build Action
import { encodeFunctionData } from 'viem';
// Example: Swap USDC → WETH
const swapCalldata = encodeFunctionData({
abi: UNISWAP_ABI,
functionName: 'exactInputSingle',
args: [{
tokenIn: USDC_ADDRESS,
tokenOut: WETH_ADDRESS,
fee: 3000,
recipient: smartAccount,
deadline: BigInt(Math.floor(Date.now() / 1000) + 1800),
amountIn: fees.amountForAction,
amountOutMinimum: minOutputAmount,
sqrtPriceLimitX96: 0n,
}],
});
const action = {
target: UNISWAP_ROUTER,
callData: swapCalldata,
value: 0n,
feeConfig: {
paymentToken: USDC_ADDRESS,
indexerFee: fees.indexerFee,
resolverFee: fees.resolverFee,
},
permit2Setup: [],
funding: {
ethNeeded: 0n,
tokensNeeded: [],
tokenAmounts: [],
maxReimbursement: fees.amountForAction,
},
};
Step 4: Execute Transfer
// Send message on source chain
const result = await pons.execute({
amount: fees.burnAmount,
action,
}, walletClient);
console.log('Message TX:', result.txHash);
console.log('Smart Account:', result.smartAccountAddress);
console.log('Waiting for indexer and resolver...');
Step 5: Track Status
import { TransferStatus } from '@pons-network/pons.js';
const tracker = pons.trackTransfer(
result.txHash,
result.smartAccountAddress,
result.nonce
);
tracker.on('statusChange', (status, data) => {
updateUI(status);
switch (status) {
case TransferStatus.SENT:
console.log('Message sent on source chain');
break;
case TransferStatus.INDEXED:
console.log('Message indexed on destination');
break;
case TransferStatus.EXECUTED:
console.log('Action executed!');
break;
}
});
Building Actions
Action Types
No Action (Simple Bridge)
const action = {
target: '0x0000000000000000000000000000000000000000',
callData: '0x',
value: 0n,
feeConfig: {
paymentToken: USDC_ADDRESS,
indexerFee: fees.indexerFee,
resolverFee: fees.resolverFee,
},
permit2Setup: [],
funding: {
ethNeeded: 0n,
tokensNeeded: [],
tokenAmounts: [],
maxReimbursement: 0n,
},
};
With ETH Value (Payable Functions)
const action = {
target: NFT_CONTRACT,
callData: mintCalldata,
value: parseEther('0.01'), // ETH to send
feeConfig: { ... },
funding: {
ethNeeded: parseEther('0.01'), // Resolver provides this
maxReimbursement: fees.amountForAction, // Resolver gets USDC back
},
};
Batch Actions
import { ActionBuilder } from '@pons-network/pons.js';
const action = new ActionBuilder()
.addCall(USDC_ADDRESS, approveCalldata)
.addCall(UNISWAP_ROUTER, swapCalldata)
.addCall(STAKING_CONTRACT, stakeCalldata)
.withFees(USDC_ADDRESS, fees.indexerFee, fees.resolverFee)
.build(nonce, deadline, fees.expectedAmount);
Transfer Flow
Status Timeline
User signs + sends message
│
▼
PENDING ─── Waiting for source chain confirmation
│
▼
SENT ──── Message sent on source chain
│
▼
ATTESTED ─── Attestation verified
│
▼
INDEXING ─── Indexer processing
│
▼
INDEXED ─── Message indexed on destination
│
▼
EXECUTING ─── Resolver executing action
│
▼
EXECUTED ✓ Action complete!
Who Does What?
| Step | Who | Fee |
|---|---|---|
| Send message | User | Network fee |
| Index message | Indexer (decentralized) | Dynamic indexer fee |
| Execute action | Resolver (decentralized) | Dynamic resolver fee |
Dynamic Fees
Pons Network uses dynamic fees, just like Ethereum gas.
How It Works
| Speed | Indexer Fee | Resolver Fee | Est. Time |
|---|---|---|---|
| Fast | 0.2 USDC | 0.3 USDC | 5-10 min |
| Standard | 0.1 USDC | 0.15 USDC | 15-20 min |
| Economy | 0.05 USDC | 0.08 USDC | 30+ min |
Implementation
// Let user choose speed
const speedOptions = {
fast: {
indexerFee: parseUnits('0.2', 6),
resolverFee: parseUnits('0.3', 6),
estimatedTime: '5-10 min',
},
standard: {
indexerFee: parseUnits('0.1', 6),
resolverFee: parseUnits('0.15', 6),
estimatedTime: '15-20 min',
},
economy: {
indexerFee: parseUnits('0.05', 6),
resolverFee: parseUnits('0.08', 6),
estimatedTime: '30+ min',
},
};
// User selects 'fast'
const selectedSpeed = 'fast';
const fees = calculateFeesSync(amount, speedOptions[selectedSpeed]);
Market Dynamics
- Operators compete for transactions based on fees
- Users choose speed vs cost tradeoff
- Market equilibrium finds fair prices
- No fixed fees - adapts to network demand
Error Handling
Common Errors
Insufficient Balance
try {
await pons.execute(params, walletClient);
} catch (error) {
if (error.message.includes('Insufficient USDC balance')) {
showError('Not enough USDC in your wallet');
}
}
Pre-validate Before Signing
import { validateActionFeasibility } from '@pons-network/pons.js';
const validation = validateActionFeasibility(sendAmount, actionCost);
if (!validation.feasible) {
showError(`Need at least ${formatUnits(validation.minimumBurn, 6)} USDC`);
return;
}
Production Deployment
Environment Variables
# Required
SOURCE_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY
DEST_RPC_URL=https://rpc.arc.network
# Optional
PONS_GATEWAY_URL=https://gateway.pons.sh
Security Checklist
- Never expose private keys in client code
- Validate all user inputs before building actions
- Use
validateActionFeasibility()before signing - Set appropriate deadlines (not too long)
- Handle all error cases gracefully
FAQ
Q: How long does a cross-chain transfer take?
A: Depends on the fees you pay.
- Fast (higher fees): ~5-10 minutes
- Standard: ~15-20 minutes
- Economy (lower fees): ~30+ minutes
Q: Can I run my own indexer/resolver?
A: Yes! Pons Network is permissionless. Anyone can run an indexer or resolver and earn fees. See the Operators Guide.
Q: Why are fees dynamic?
A: Like Ethereum gas, dynamic fees create a competitive market:
- Users who need speed pay more
- Users who can wait pay less
- Operators prioritize profitable transactions
- Market finds equilibrium