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.
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
| Module | Highlights |
|---|---|
addresses | CHAIN_IDS, BSC_MAINNET, BSC_TESTNET, LOCAL, getChainAddresses(chainId), registerChainAddresses(chainId, patch), findQuote(chainId, addressOrSymbol), settlementAsset(chainId) |
ids | ID_TAG, KIND_STEALTH, KIND_HANDLE, MIN_FALLBACK_DELAY, NO_FALLBACK, PLATFORM, computeId, stealthKeyHash, stealthId, userIdHash, handleCommitment, handleId, assertFallback |
stealth | generateStealthBeneficiary, handleClaimKit, encodeClaimLink, parseClaimLink, redactClaimKit, ClaimLinkError |
eip712 | FEE_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) |
hints | hintFromCommitment, encryptHint, decryptHint, parseHintEnvelope, decryptHintShare, combineHintShares, hintPublicKey, hintKeyId, shamirSplit, shamirCombine |
launch | constants (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 |
railgun | decodeRailgunAddress, encodeRailgunAddress, isRailgunAddress, buildShieldRequest, encodeRailgunAdapterData, buildRailgunClaimData, openShieldRequest, shieldTemplatesHash, buildShieldTemplates, RAILGUN_PPOI_STANDBY_SECONDS |
consolidation | readQuoteRegistry (quoteTokens + quoteConfig), readCreatorMinOut, readOracleQuote, CONSOLIDATE_ALL_FLAG, consolidateAllAmountIn, effectiveConsolidateMinOut, buildConsolidate, buildRegisterShieldTemplates, buildClearShieldTemplates, estimateMinOut, readChainlinkUsd, quotePcsV3ExactIn, applySlippage |
deployments | deploymentJsonSchema, registerDeployment, deploymentAddresses |
vault | calldata 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 |
relayerClient | RelayerClient (config, quote, claim, consolidate, registerShieldTemplates, rotateOwner, ping, cancelBind, proposeBind, finalizeBind, txStatus, accounts, findAccount, accountNonces), ServiceError, claimToJson, claimFromJson, actionFeeToJson |
attestorClient | AttestorClient (info, commitment, commitmentWithReceipt, oauthStart, deprecated oauthStartUrl, telegramVerify, escrows, bind), collectBindSignatures, chooseBindDeadline, attestorBindDeadline, BIND_DEADLINE_*, decodePaymentResponse, formatLookupPrice |
api | zod schemas and types for every HTTP request and response (normative) |
abis | Generated 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.
Claim-link format
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. UseredactClaimKitbefore logging. - Never send the claim link to a server. Parse it from
location.hashin the browser. - Fetch account data and nonces with
accounts()/findAccount()/accountNonces(), which download everything. Do not readnonces(id),accountOf(id)orbalanceOf(id, …)from a third-party RPC on the beneficiary side; it links your IP to the id. Price consolidations with unitreadOracleQuotecalls over the whole quote list, not with the account's balance. - Send nothing account-specific in URLs:
quote()takes no amount,txStatus()is aPOST, andoauthStart()sends the intent in the body (avoid the deprecatedoauthStartUrl).