SDK Reference
Contracts SDK
@pennysia/contracts-sdk is the TypeScript boundary between your app and Pennysia Settlement.
Version: 0.4.2
Chain: Ethereum Mainnet (chain ID 1)
What it provides
- Canonical
PennysiaSettlementABI (synced from the Settlement repo) - Deployed addresses (
pennysiaAddresses) - Typed route builders for SYNC and SODAX
- Calldata encoders for
executeSwapand SODAX cancel - ERC-20 approval request builders
- Framework-neutral execution service (inject your own viem clients)
- SODAX open/cancel encoding helpers
- Surplus fee constants (
MAX_FEE_PPM,computeSurplusFee)
Installation
npm install @pennysia/contracts-sdk@0.4.2Main exports
import {
pennysiaSettlementAbi,
pennysiaAddresses,
getSettlementDeployment,
buildSyncRoute,
buildSodaxRoute,
buildExecuteSwapRequest,
buildCancelAndRecoverSodaxRequest,
buildOpenSodaxPayload,
encodeExecuteSwapCalldata,
ROUTE_KIND_SYNC,
ROUTE_KIND_SODAX,
MAX_FEE_PPM,
computeSurplusFee,
} from "@pennysia/contracts-sdk";Execution helpers are a separate import:
import {
createSettlementExecutor,
ensureErc20Allowance,
buildErc20ApproveRequest,
} from "@pennysia/contracts-sdk/execution";Settlement address
pennysiaAddresses.ethereum.settlement
// 0x3Aad97E5a91b8e43b7Dc830aCEb004307678795EBuilding a SYNC route
A SYNC route wraps aggregator calldata for Settlement:
const route = buildSyncRoute({
spendAs: sellToken, // or WETH for ETH sells
quotedReturn: quoteAmount,
value: 0n, // native value for the venue call
venue: quote.to, // contract to call
allowanceTarget: quote.spender,
approvalMode: ROUTE_DIRECT_ALLOWANCE,
callData: quote.data,
});Map from your quote:
| Route field | Quote field |
|---|---|
venue | tx.to |
allowanceTarget | spender |
callData | tx.data |
value | tx.value |
quotedReturn | expected output amount |
Settlement must be the receiver/taker in the calldata.
Executing a swap
The execution module enforces this order:
- Validate routes and calldata
- Check ERC-20 allowance (spender = Settlement; skip for native input)
- Reset allowance to zero if the token requires it (e.g. USDT)
- Submit maximum allowance to Settlement
- Simulate
executeSwap - Submit
executeSwap - Wait for receipt and verify success
You inject viem-compatible read, write, simulate, and receipt functions. The SDK never creates a wallet or reads environment variables.
const executor = createSettlementExecutor({
readAllowance: (args) => publicClient.readContract(args),
writeApproval: (args) => walletClient.writeContract(args),
simulateExecuteSwap: (args) => publicClient.simulateContract(args),
writeExecuteSwap: (args) => walletClient.writeContract(args),
waitForReceipt: (hash) => publicClient.waitForTransactionReceipt({ hash }),
});
await executor.executeSwap({
routes: [route1, route2], // ordered fallback
sellToken,
buyToken,
amountIn,
minTotalReturn,
deadline,
});SODAX helpers
For cross-chain intents:
import {
buildSodaxRoute,
buildOpenSodaxPayload,
encodeCancelAndRecoverSendMessage,
computeIntentDigest,
} from "@pennysia/contracts-sdk";SODAX routes use ROUTE_KIND_SODAX. Open calldata is built with buildOpenSodaxPayload. Cancel encoding uses encodeCancelAndRecoverSendMessage.
Fees
import { computeSurplusFee, MAX_FEE_PPM } from "@pennysia/contracts-sdk";
// grossOutput and quotedReturn are bigint
const fee = computeSurplusFee(grossOutput, quotedReturn);
// MAX_FEE_PPM = 10_000 → 10% cap on grossSyncing contract changes
When Settlement Solidity changes:
- Build the Settlement repository with Foundry
- Run the sync script in the SDK repo
- Run
npm run check:generatedand tests - Publish a new contracts-sdk version
See the SDK repository guide for the full workflow.
What this SDK does not do
- Create providers or wallets
- Read environment variables
- Select quote sources
- Handle HTTP or API routes
- Own UI state
Those belong in your application layer.
Backend SDK
@pennysia/backend-sdk provides framework-neutral quote orchestration and exchange domain types.
Version: 0.1.9
This is not an HTTP client. It does not call Pennysia API endpoints directly. You implement LiquidityAdapter for each exchange and pass them to the fanout engine.
What it provides
- Quote, route, and execution-capability types
LiquidityAdapterinterface and adapter registry- Canonical source identities (
LAUNCH_SOURCES) - Bounded quote fanout with deadlines
- Median consensus and high-deviation rejection
- Quote ranking by net output
- Settlement compatibility helpers
- Native token and quote-freshness primitives
- Hard-intent planning helpers (CoW, Velora Delta, UniswapX)
Installation
npm install @pennysia/backend-sdk@0.1.9Core usage
import {
runQuoteFanout,
rankQuotes,
type LiquidityAdapter,
type QuoteParams,
} from "@pennysia/backend-sdk";Implement an adapter
Each exchange implements LiquidityAdapter:
const myAdapter: LiquidityAdapter = {
id: "my-exchange",
async quote(params: QuoteParams) {
// Call your exchange API
// Return a NormalizedQuote or null
},
};Run fanout
const result = await runQuoteFanout({
quoteParams: {
sellToken: "0x...",
buyToken: "0x...",
sellAmount: 1_000_000n,
chainId: 1,
},
adapters: [uniswapAdapter, zeroxAdapter, /* ... */],
deadlineMs: 8_000,
});
// result.quotes — all quotes received before deadline
// result.errors — per-adapter failuresRank quotes
import { rankQuotes } from "@pennysia/backend-sdk";
const ranked = rankQuotes(result.quotes);
// Best quote first, net output after gasSource manifest
LAUNCH_SOURCES defines every integrated exchange:
import {
LAUNCH_SOURCES,
normalizeSourceId,
getLaunchSource,
} from "@pennysia/backend-sdk";Each source has an ID, display name, capabilities, and enablement flags. The Pennysia web app uses the same manifest.
Current source IDs include: uniswap, uniswapx, zerox, cowswap, velora, velora-delta, bebop, kyberswap, openocean, rango, dodo, portals, lifi, squid, enso, nordstern, liquidmesh, okx_dex, bitget_swap, rubic, sushi, sodax, fabric, and others.
Enablement helpers
import {
isQuoteFanoutEnabled,
getQuoteDisableReason,
isLaunchIntentBlocked,
} from "@pennysia/backend-sdk";These check whether a source is live for quoting or blocked at launch. The SDK never reads environment variables. You pass ops flags as options.
Consensus
import { validateQuoteConsensus } from "@pennysia/backend-sdk";
const check = validateQuoteConsensus(quotes, { maxDeviationBps: 500 });
// Rejects outlier quotes that deviate too far from the medianSettlement compatibility
import {
resolveSettlementRoutes,
isSettlementCompatible,
} from "@pennysia/backend-sdk";These helpers determine whether a quote can be wrapped as a Settlement SYNC route and build route inputs from resolved quote data.
Hard intents
CoW Protocol, Velora Delta, and UniswapX are wallet-side routes. The backend SDK provides planning helpers but does not submit transactions. Settlement's executeSwap does not handle these.
What this SDK does not do
- HTTP routes or response serialization
- Wallet discovery, signing, or provider transport
- Environment variable loading or API keys
- Smart contract ABI or transaction submission (use contracts-sdk)
- Concrete aggregator implementations (you provide adapters)
Relationship to contracts-sdk
| Concern | Backend SDK | Contracts SDK |
|---|---|---|
| Quote fetching | Yes (via your adapters) | No |
| Quote ranking | Yes | No |
| Route building | Types and helpers | Full calldata builders |
| On-chain execution | No | Yes |
| ABI and addresses | No | Yes |
Your app composes both: backend-sdk for quotes, contracts-sdk for execution.