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