Skip to main content

SDK reference (@zk-pad/sdk)

TypeScript (ESM, strict), built on viem and the noble libraries. Source: packages/sdk. This page summarises the public API; the TSDoc comments in the source are authoritative.

Status

v0.1.0. Every ABI is generated from the real contracts (pnpm -C packages/sdk gen:abis, which can reuse an existing forge build via ZKPAD_FORGE_OUT). Deployment addresses come from contracts/deployments/<chainId>.json, either through registerDeployment(json) at runtime or embedded at build time by pnpm -C packages/sdk gen:deployments (chains 56 and 97).

pnpm add @zk-pad/sdk viem

Modules​

ModuleHighlights
addressesCHAIN_IDS, BSC_MAINNET, BSC_TESTNET, LOCAL, getChainAddresses(chainId), registerChainAddresses(chainId, patch), findQuote(chainId, addressOrSymbol), settlementAsset(chainId)
idsID_TAG, KIND_STEALTH, KIND_HANDLE, MIN_FALLBACK_DELAY, NO_FALLBACK, PLATFORM, computeId, stealthKeyHash, stealthId, userIdHash, handleCommitment, handleId, assertFallback
stealthgenerateStealthBeneficiary, handleClaimKit, encodeClaimLink, parseClaimLink, redactClaimKit, ClaimLinkError
eip712FEE_VAULT_DOMAIN_NAME, FEE_VAULT_DOMAIN_VERSION, TYPE_STRINGS, FEE_VAULT_TYPES, feeVaultDomain, feeVaultTypedData, hashFeeVaultMessage, signFeeVaultMessage, recoverFeeVaultSigner, DIRECT_ADAPTER, encodeDirectData, dataHash, buildClaim, FEE_CLAIM_DATA, buildFeeClaim (fee-only claims for paid relayer actions)
hintshintFromCommitment, encryptHint, decryptHint, parseHintEnvelope, decryptHintShare, combineHintShares, hintPublicKey, hintKeyId, shamirSplit, shamirCombine
launchconstants (TOKEN_SUPPLY, TICK_SPACING, DYNAMIC_FEE_FLAG, fee bounds, ZKPAD_HOOK_BITMAP), tick math (getSqrtRatioAtTick, priceAtTick, fdvAtTick, tickForFdv, clampStartingTick), presets (PRESET_OFFSETS, presetPositions, validatePositions), fees (percentToPips, pipsToPercent, assertFeePips, lpFeeFor, feeSplitPreview), pool keys (encodePoolParameters, sortCurrencies, zkPadPoolKey, poolId), FEE_IN, buildLaunchParams (feeIn: Both / Token requires allowSelfFundedTokenFees: true), parseQuoteAmount
railgundecodeRailgunAddress, encodeRailgunAddress, isRailgunAddress, buildShieldRequest, encodeRailgunAdapterData, buildRailgunClaimData, openShieldRequest, shieldTemplatesHash, buildShieldTemplates, RAILGUN_PPOI_STANDBY_SECONDS
consolidationreadQuoteRegistry (quoteTokens + quoteConfig), readCreatorMinOut, readOracleQuote, CONSOLIDATE_ALL_FLAG, consolidateAllAmountIn, effectiveConsolidateMinOut, buildConsolidate, buildRegisterShieldTemplates, buildClearShieldTemplates, estimateMinOut, readChainlinkUsd, quotePcsV3ExactIn, applySlippage
deploymentsdeploymentJsonSchema, registerDeployment, deploymentAddresses
vaultcalldata encoders (encodeRegister, encodeClaimCall, encodeConsolidateCall, encodeRegisterShieldTemplatesCall, encodeClearShieldTemplatesCall, encodeCreatorConsolidateCall, encodeCreatorConsolidateAndShieldCall, encodeRotateOwnerCall, encodePingCall, encodeCancelBindCall, encodeProposeBindCall, encodeFinalizeBindCall, encodeVaultMulticall), reads (readVaultAccount, readVaultBalances, readShieldTemplateCount, readLaunchCredit, readMinCreatorShield). The per-id reads are for creators and operators; beneficiary clients take their state from the relayer index instead
relayerClientRelayerClient (config, quote, claim, consolidate, registerShieldTemplates, rotateOwner, ping, cancelBind, proposeBind, finalizeBind, txStatus, accounts, findAccount, accountNonces), ServiceError, claimToJson, claimFromJson, actionFeeToJson
attestorClientAttestorClient (info, commitment, commitmentWithReceipt, oauthStart, deprecated oauthStartUrl, telegramVerify, escrows, bind), collectBindSignatures, chooseBindDeadline, attestorBindDeadline, BIND_DEADLINE_*, decodePaymentResponse, formatLookupPrice
apizod schemas and types for every HTTP request and response (normative)
abisGenerated ABIs: factory, hook, locker, MEV module, token (implementation, not the interface), router, FeeVault (includes the consolidation surface), IFeeVaultConsolidation, QuoteRegistry, IRouteExecutor, RailgunShieldAdapter, ShieldSender, IWithdrawalAdapter

Recipes​

Create a private beneficiary and launch​

import {
generateStealthBeneficiary, buildLaunchParams, findQuote, getChainAddresses,
percentToPips, zkPadFactoryAbi,
} from '@zk-pad/sdk';

const chain = getChainAddresses(56);
const kit = generateStealthBeneficiary({
chainId: 56,
fallbackRecipient: ngoAddress, // optional
fallbackDelay: 365n * 24n * 3600n, // >= 180 days
appUrl: 'https://zkpad.app',
});
// kit.claimLink holds the secret in its fragment. Never log it.

const quote = findQuote(56, 'WBNB')!;
const config = buildLaunchParams({
name: 'Save the Reef', symbol: 'REEF',
quote, // address, decimals, tick bounds
targetFdv: quote.defaultFdv, // or startingTick
preset: 'fair',
feePips: percentToPips(2), // 2%
beneficiaryId: kit.id,
antiSnipe: {startingFeePips: 500_000, secondsToDecay: 60},
devBuy: {amountIn: 10n ** 17n, minAmountOut: 0n, recipient: creator},
deployment: {hook: chain.zkpad.hook!, locker: chain.zkpad.locker!, mevModule: chain.zkpad.mevModule},
});
await walletClient.writeContract({
address: chain.zkpad.factory!, abi: zkPadFactoryAbi,
functionName: 'deployToken', args: [config], value: config.devBuyConfig.amountIn,
});

Claim to Railgun through a relayer​

import {
parseClaimLink, RelayerClient, buildRailgunClaimData, buildClaim, claimToJson,
signFeeVaultMessage, getChainAddresses,
} from '@zk-pad/sdk';
import {privateKeyToAccount} from 'viem/accounts';

const kit = parseClaimLink(window.location.href); // fragment only, never sent anywhere
const relayer = new RelayerClient({baseUrl: 'https://relayer.example'});
const cfg = await relayer.config();
const {nonce} = await relayer.accountNonces(kit.id); // fetch-everything, filtered locally: no RPC read
const usdt = getChainAddresses(kit.chainId).usdt;

const {data} = buildRailgunClaimData({zkAddress, beneficiaryId: kit.id, usdt, chainId: kit.chainId});
const q = await relayer.quote({asset: usdt, adapter: cfg.adapters.railgun!}); // no amount, by design
const {claim} = buildClaim({
beneficiaryId: kit.id, asset: usdt,
amount: 2n ** 256n - 1n, // the whole balance at execution
adapter: cfg.adapters.railgun!, data,
relayer: cfg.relayer, relayerFee: BigInt(q.relayerFee),
nonce,
deadline: BigInt(Math.floor(Date.now() / 1000) + 900),
});
const signature = await signFeeVaultMessage(privateKeyToAccount(kit.privateKey), 'Claim', claim, kit.chainId, cfg.feeVault);
const {txHash} = await relayer.claim({claim: claimToJson(claim), adapterData: data, signature /*, register */});
const status = await relayer.txStatus(txHash); // POST /v1/tx-status

(Field shapes such as q.relayerFee follow src/api.ts; amounts are decimal strings in JSON.) Other owner actions (consolidate, registerShieldTemplates, rotateOwner, ping) attach a fee: quote it with relayer.quote({action, asset, adapter: DIRECT_ADAPTER}), build it with buildFeeClaim at nonce nonce + 1 (signed by the new owner for a rotation) and send it as fee: actionFeeToJson(feeClaim, feeSignature). To consolidate a whole balance, sign amountIn = consolidateAllAmountIn(quotedAmount).

Encrypt a social-escrow hint​

import {AttestorClient, encryptHint, handleId, hintFromCommitment} from '@zk-pad/sdk';

const att = new AttestorClient({baseUrl: 'https://attestor-1.example'});
const c = await att.commitment({platform: 'x', userId: '783214'}); // free; a handle needs an x402 paying fetch
const id = handleId(c.handleCommitment, ngoAddress, fallbackDelay);
const committee = await Promise.all(attestorUrls.map((u) => new AttestorClient({baseUrl: u}).info()));
const hint = encryptHint(
hintFromCommitment(c, {fallbackRecipient: ngoAddress, fallbackDelay}), // includes attestorSalt
committee.map((i) => i.hintPublicKey), // every attestor of the committee
);
// pass `id` as lockerConfig.beneficiaryId and `hint` as beneficiaryHint

The hint must carry attestorSalt (hintFromCommitment adds it): attestors have independent peppers, so the rest of the committee could not verify the escrow without it. encryptHint only produces threshold-1 envelopes (each recipient opens it alone) and refuses threshold > 1. CommitmentResponse is defined in src/api.ts and HintPlaintext in src/hints.ts.

https://<app>/claim#v1.<base64url(json)> with short keys: c chainId, t kind (1 stealth / 2 handle), k private key, s salt (stealth), f / d fallback recipient / delay, i id, v FeeVault (optional), l label (optional, not secret, not verified). parseClaimLink re-derives and checks the id for stealth kits.

Hint envelope​

0x01 | n | t | ephPub(33) | nonce(24) | XChaCha20-Poly1305(K, pad320(json)) | n × (keyId(4) | ECIES-share(49))

Shares of K are wrapped with secp256k1 ECDH + HKDF-SHA256. The format has a Shamir threshold field (t-of-n over GF(256)), but only t = 1 is produced and opened: every share is K itself, so any one recipient decrypts alone. There is no share-exchange protocol between attestors. The plaintext is padded to 320 bytes so ciphertext length reveals nothing.

Security rules for integrators​

  • Never log, persist or transmit privateKey, salt, a claim link or a Railgun shield key. Use redactClaimKit before logging.
  • Never send the claim link to a server. Parse it from location.hash in the browser.
  • Fetch account data and nonces with accounts() / findAccount() / accountNonces(), which download everything. Do not read nonces(id), accountOf(id) or balanceOf(id, …) from a third-party RPC on the beneficiary side; it links your IP to the id. Price consolidations with unit readOracleQuote calls over the whole quote list, not with the account's balance.
  • Send nothing account-specific in URLs: quote() takes no amount, txStatus() is a POST, and oauthStart() sends the intent in the body (avoid the deprecated oauthStartUrl).