Skip to main content

Integration Guide

Complete step-by-step guide for integrating Pons Network into your dApp. Build cross-chain experiences with decentralized execution.

Table of Contents​

  1. Getting Started
  2. Integration Steps
  3. Building Actions
  4. Transfer Flow
  5. Dynamic Fees
  6. Error Handling
  7. 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?​

StepWhoFee
Send messageUserNetwork fee
Index messageIndexer (decentralized)Dynamic indexer fee
Execute actionResolver (decentralized)Dynamic resolver fee

Dynamic Fees​

Pons Network uses dynamic fees, just like Ethereum gas.

How It Works​

SpeedIndexer FeeResolver FeeEst. Time
Fast0.2 USDC0.3 USDC5-10 min
Standard0.1 USDC0.15 USDC15-20 min
Economy0.05 USDC0.08 USDC30+ 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