Boing SDK
🛠️ Developers: this is the TypeScript client for Boing JSON-RPC, QA errors, native DEX calldata, and wallet glue.
👋 Everyday users: you use this indirectly through boing.express, boing.observer, and boing.finance.
🛰️ Operators: after SDK source changes, runnpm run buildhere and commitdist/. Tutorialfile:consumers need a freshdist/.
TypeScript/JavaScript client for Boing Network: typed RPC client, hex utilities, and structured errors (including QA rejection feedback).
flowchart LR
App[dApp / finance / explorer] --> SDK["@boing-network/boing-sdk"]
SDK --> RPC[boing_chainHeight / getAccount / qaCheck / listDex*]
SDK --> Wallet[window.boing helpers]
Install
Consumers (npm):
npm install @boing-network/boing-sdk
Current registry latest: see the npm package page (this repo's package.json is 0.5.39). Package page: npmjs.com/package/@boing-network/boing-sdk. Do not install unscoped boing-sdk — that name is abandoned/locked and is not updated by this repo.
Monorepo / contributors (this directory):
npm install
npm run build
Consuming via file: (e.g. boing.finance)
The compiled output lives in dist/, which is committed in Boing-Network/boing.network (repo root .gitignore ignores generic dist/ but not boing-sdk/dist/). Clones and CI siblings therefore get a usable dist/index.js without a separate SDK build step.
When you change SDK source (src/), run npm run build in boing-sdk and commit the updated dist/. The boing-sdk workflow fails if dist/ drifts from the TypeScript build output.
boing.finance checks out boing.network only so the file: path resolves; it does not build the SDK first. boing.finance’s frontend/scripts/postinstall-boing-sdk.mjs only runs npm run build in the linked package when dist/index.js is missing (e.g. local SDK development or a broken checkout) and fails the install if that build does not succeed.
Publish to npm (CI)
Package name on the registry: @boing-network/boing-sdk (scoped). Unscoped boing-sdk remains abandoned/locked and is not published from this workflow.
Scoped publish is live. Routine releases use the Publish boing-sdk GitHub Action (.github/workflows/boing-sdk-publish.yml) on tags boing-sdk/v* with repository secret NPM_TOKEN.
Required GitHub secret (ENEEDAUTH if missing):
- On npmjs.com as a user that can publish
@boing-network/boing-sdk, create an Automation token (or granular token with publish on that package; Automation avoids interactive OTP in CI). - In the
Boing-Network/boing.networkrepo: Settings → Secrets and variables → Actions → New repository secret. - Name:
NPM_TOKEN. Value: the npm token (npm_…). - Smoke-check without publishing: Actions → Publish boing-sdk → Run workflow with auth_only = true (expects
npm whoamito succeed).
Historical tag publishes failed with ENEEDAUTH when this secret was absent (empty NODE_AUTH_TOKEN in CI). The workflow now fails fast with a clear error if the secret is missing.
Each release (after package.json version + CHANGELOG + committed dist/ are on main):
git checkout main && git pull origin main
node -p "require('./boing-sdk/package.json').version" # e.g. 0.5.39
git tag -a "boing-sdk/v$(node -p "require('./boing-sdk/package.json').version")" -m "@boing-network/boing-sdk $(node -p "require('./boing-sdk/package.json').version")"
git push origin "boing-sdk/v$(node -p "require('./boing-sdk/package.json').version")"
Watch Actions → Publish boing-sdk. Then: npm view @boing-network/boing-sdk version. Prefer registry installs for FreshMint and other apps (npm install @boing-network/boing-sdk); GitHub path:/boing-sdk pins are not the preferred consumer path.
Tests
- Unit / offline:
npm test— Vitest; does not require a running Boing node. - Optional RPC integration:
tests/rpcIntegration.test.tsruns only whenBOING_INTEGRATION_RPC_URLis set (e.g.http://127.0.0.1:8545). If it is unset, Vitest skips that whole suite so default runs stay offline-friendly. Core cases: sync state (orgetBlockByHeightfallback), blocks + receipts,getLogsChunked,fetchBlocksWithReceiptsForHeightRange,getTransactionReceipt(unknown tx →null). Endpoints that return -32601 forboing_getSyncStateorboing_getLogsstill pass where noted. WithBOING_EXPECT_FULL_RPC=1(see.github/workflows/boing-sdk-rpc-integration.yml), an extra test assertsboing_clientVersion,boing_rpcSupportedMethods, andprobeBoingRpcCapabilities(6/6 core methods, includingboing_getNetworkInfo/boing_getBlockByHeight/boing_getTransactionReceipt). - Operator / tutorial smoke (separate from Vitest): in
examples/native-boing-tutorial,npm run preflight-rpc(orcheck-testnet-rpconly) withBOING_RPC_URL— same scripts CI runs after starting a local node; see NODE-OPERATOR-COMMANDS.md. From the monorepo root, the samenpm run <script>names delegate to the tutorial package (see rootpackage.json). npm run verify— same asnpm test, then prints a short note explaining the skip when the env var is missing.
Quick start
import { createClient, BoingRpcError, explainBoingRpcError } from '@boing-network/boing-sdk';
// Local node (no retries needed):
const local = createClient('http://localhost:8545');
// Public testnet RPC — recommend maxRetries (default is 0):
const client = createClient({
baseUrl: 'https://testnet-rpc.boing.network/',
maxRetries: 3,
});
// Read chain and account state
const height = await client.chainHeight();
const account = await client.getAccount('0x' + '00'.repeat(32)); // 32-byte hex
console.log(account.balance, account.nonce, account.stake);
// Pre-flight QA check before deploying a contract
const qa = await client.qaCheck('0x600160005260206000f3'); // hex bytecode
if (qa.result === 'reject') {
console.error('QA rejected:', qa.rule_id, qa.message);
} else if (qa.result === 'allow') {
// Submit signed tx (hex from Rust CLI or future signer)
await client.submitTransaction(hexSignedTx);
}
// Handle structured QA / rate-limit errors on submit
try {
await client.submitTransaction(hexSignedTx);
} catch (e) {
if (e instanceof BoingRpcError && e.isQaRejected) {
const { rule_id, message } = e.qaData ?? {};
console.error('Deployment rejected:', rule_id, message);
}
if (e instanceof BoingRpcError) {
console.error(explainBoingRpcError(e)); // HTTP 429 / -32016 / gateway limits
}
throw e;
}
Public RPC / 429: Cloudflare in front of testnet-rpc.boing.network may return HTTP 429. Default maxRetries is 0; for public RPC pass maxRetries: 2–3 so transient limits and faucet -32016 can back off (honors Retry-After). See BOING-RPC-ERROR-CODES-FOR-DAPPS.md.
API
- createClient(config) —
configcan be a URL string or{ baseUrl, fetch?, timeoutMs?, maxRetries?, retryBaseDelayMs? }. Default timeout 30s; settimeoutMs: 0to disable. DefaultmaxRetries: 0(no retries); use2–3against public testnet RPC. - BoingClient — typed methods for all RPCs (32-byte account/hash params are validated locally before sending):
- RPC probe —
probeBoingRpcCapabilitiesreturns{ clientVersion, supportedMethods, methods }pluscountAvailableBoingRpcMethods,explainBoingRpcProbeGaps(rpcCapabilities.ts): detect -32601 missing methods; CLI:npm run buildthennpm run probe-rpc(ornpm run probe-rpcfrom repo root);diagnosisin JSON explains stale node vs current repo; node discovery:boing_clientVersion,boing_rpcSupportedMethods chainHeight(),getBalance(hexAccountId),getAccount(hexAccountId)getBlockByHeight(height, includeReceipts?),getBlockByHash(hexHash, includeReceipts?)getTransactionReceipt(hexTxId),getLogs(filter)— bounded log query (RPC-API-SPEC.md)- Receipt/log helpers:
normalizeTopicWord,iterBlockReceiptLogs,filterReceiptLogsByTopic0, … (receiptLogs.ts; see INDEXER-RECEIPT-AND-LOG-INGESTION.md) getAccountProof(hexAccountId),verifyAccountProof(hexProof, hexStateRoot)simulateTransaction(hexSignedTx),submitTransaction(hexSignedTx)- High-level flows:
submitTransferWithSimulationRetry,submitContractCallWithSimulationRetry,submitDeployWithPurposeFlow(see Simulate → access list → submit below) - Linked NFT ↔ token (on-chain registry) — live public testnet registry
CANONICAL_BOING_TESTNET_LINKED_NFT_TOKEN_REGISTRY_HEX(0xf704485a…655ddonhttps://testnet-rpc.boing.network/, saltBOING_NFT_TOKEN_LINK_REG_V2, selectors0xE0–0xE8); v10xebf9f019…3dd8is history-only asCANONICAL_BOING_TESTNET_LINKED_NFT_TOKEN_REGISTRY_V1_HEX; dAppbuildLinkedNftTokenProjectPack(joint deploy + claim×2 + register),buildLinkedNftTokenPairDeploys,buildLinkedNftTokenRegisterFlowTxs, registry deploy/encoders; optional cache schemaboing.linked_nft_token.v1(linkedNftTokenProjectPack.ts,linkedNftTokenRegistry.ts,linkedNftToken.ts; BOING-LINKED-NFT-TOKEN.md) - Builder attestation (on-chain registry) — live public testnet registry
CANONICAL_BOING_TESTNET_BUILDER_ATTESTATION_REGISTRY_HEX(0x734077a9…1816onhttps://testnet-rpc.boing.network/, saltBOING_BUILDER_ATTEST_REG_V1, selectors0xE0–0xE6); dual asset claimer separate from companions;buildBuilderAttestationAttestFlowTxs, deploy/encoders (builderAttestationRegistry.ts; BOING-BUILDER-ATTESTATION.md). Soft wallet proofs are not authority. - Pinned native deploy (Boing Express tx objects) —
buildContractDeployMetaTx(optionalcreate2SaltHex/create2_salt),buildReferenceFungibleDeployMetaTx/buildReferenceFungibleSecuredDeployMetaTx/buildReferenceNftCollectionDeployMetaTx(wizard-friendly one-call builders),resolveReferenceFungibleTemplateBytecodeHex,resolveReferenceFungibleSecuredTemplateBytecodeHex,resolveReferenceNftCollectionTemplateBytecodeHex,ensure0xHex,DEFAULT_REFERENCE_FUNGIBLE_SECURED_TEMPLATE_BYTECODE_HEX,REFERENCE_FUNGIBLE_TEMPLATE_VERSION,REFERENCE_FUNGIBLE_SECURED_TEMPLATE_VERSION,REFERENCE_NFT_COLLECTION_TEMPLATE_VERSION(canonicalDeployArtifacts.ts; BOING-CANONICAL-DEPLOY-ARTIFACTS.md); multi-network wizards:isBoingTestnetChainId,normalizeBoingChainIdHex,BOING_TESTNET_CHAIN_ID_*(chainIds.ts); review-step QA (dappDeploy.ts; BOING-DAPP-INTEGRATION.md § One wizard, two backends):buildAndPreflightReferenceFungibleDeploy,buildAndPreflightReferenceNftCollectionDeployMeta,preflightContractDeployMetaWithUi,describeContractDeployMetaQaResponse,preflightContractDeployMetaQa,BOING_QA_PLACEHOLDER_DESCRIPTION_HASH_HEX - Generic calldata words —
calldataSelectorLastByte,calldataU128BeWord,calldataAccountIdWord,calldataFixedWord32,concatCalldata(same layout as reference token/NFT encoders; use for custom contracts) - Typed call builder —
BoingCalldataWord,assertBoingCalldataWord,boingWordU128/boingWordAccount/boingWordFixed/boingWordSelector(32-byte argument words),encodeBoingCall(selectorLowByte, args)(reference-style first word + argument words) - Minimal call ABI (Boing-native) —
encodeBoingCallTyped(selector, ['u128','account',…], values),encodeBoingCallFromAbiArgs,BoingReferenceCallDescriptors(fixed-width reference token / NFT / native AMM layouts) +encodeBoingCallFromDescriptor— not Solidity ABI / keccak4; matchesdocs/BOING-REFERENCE-TOKEN.md,BOING-REFERENCE-NFT.md,NATIVE-AMM-CALLDATA.md - Reference NFT —
SELECTOR_MINT_BATCH(0x06),MAX_REFERENCE_NFT_MINT_BATCH(500),encodeReferenceMintBatchCalldata/encodeReferenceMintBatchCalldataHex(layout96+64n; zero hash skips metadataSSTORE), plus 96-byteencodeReferenceOwnerOf*/TransferNft*/SetMetadataHash*; storage key helpers (referenceNft.ts). Do not encodemint_batchviaBoingReferenceCallDescriptors. Template version"3", artifact idboing.reference_nft_collection.v0; dump line 3 /embed-reference-nft-collection-template-hex.mjs. NeedsGAS_PER_CONTRACT_CALL = 40_000_000on the node for full n=500 (Fly public testnet redeploy after merge). For 10k mints: chunk 20×500 — OpenSea-scale media upload ≠ one Boing consensus tx. - Native CP pool (MVP) —
buildNativeConstantProductPoolAccessList,buildNativeConstantProductContractCallTx,mergeNativePoolAccessListWithSimulation;fetchNativeConstantProductReserves,fetchNativeConstantProductPoolSnapshot(batched reserves + total LP ± signer LP),fetchNativeConstantProductTotalLpSupply,fetchNativeConstantProductSwapFeeBps(v3/v4),fetchNativeAmmSignerLpBalance,decodeBoingStorageWordU128,decodeNativeAmmAddLiquidityReturnLpMinted,decodeNativeAmmLogDataU128Triple, reserve / total-LP / token / swap-fee-bps key helpers (nativeAmmPool.ts);tryParseNativeAmmLog2,filterMapNativeAmmRpcLogs(nativeAmmLogs.ts);constantProductAmountOut/constantProductAmountOutWithFeeBps/constantProductAmountOutNoFee/encodeNativeAmmSetTokensCalldata/encodeNativeAmmSetSwapFeeBpsCalldata/NATIVE_CP_SWAP_FEE_BPS/NATIVE_AMM_TOPIC_*_HEX/nativeAmmLogTopic0Utf8(nativeAmm.ts; NATIVE-AMM-CALLDATA.md); LP vault —encodeNativeAmmLpVaultConfigureCalldata,buildNativeAmmLpVaultDepositAddAccessList,buildNativeAmmLpVaultDepositAddContractCallTx, merge helpers (nativeAmmLpVault.ts; NATIVE-AMM-LP-VAULT.md); LP share token — encoders +buildLpShareTokenAccessList,buildLpShareTokenContractCallTx,mergeLpShareTokenAccessListWithSimulation(nativeLpShareToken.ts; NATIVE-LP-SHARE-TOKEN.md);CANONICAL_BOING_TESTNET_NATIVE_CP_POOL_HEX(canonicalTestnet.ts) — convenience mirror of RPC-API-SPEC.md § Native constant-product AMM (bump when the on-chain canonical pool changes) - Native DEX (pair directory + routers) — BOING-NATIVE-DEX-CAPABILITY.md: factory calldata + access lists (
nativeDexFactory.ts),fetchNativeDexFactoryPairsCount,findNativeDexFactoryPoolByTokens,Log3parsers (nativeDexFactoryPool.ts,nativeDexFactoryLogs.ts; NATIVE-DEX-FACTORY.md); ledger-router forward calldata + tx builders v1–v3 (nativeDexLedgerRouter.ts; NATIVE-DEX-LEDGER-ROUTER.md); swap2 + multihop encoders (nativeDexSwap2Router.ts; NATIVE-DEX-SWAP2-ROUTER.md, NATIVE-DEX-MULTIHOP-SWAP-ROUTER.md); CREATE2 predictors + salts (create2.ts); tutorialdeploy-native-dex-directory(examples/native-boing-tutorial README §7c2) - Seamless defaults + wallet glue —
fetchNativeDexIntegrationDefaults/mergeNativeDexIntegrationDefaults(dexIntegration.ts): mergeboing_getNetworkInfo.end_user(BOING_CANONICAL_NATIVE_CP_POOL/BOING_CANONICAL_NATIVE_DEX_FACTORY) with overrides and 6913 embedded pool fallback;fetchNativeDexFactoryRegisterLogsfor chunkedregister_pairscans;getInjectedEip1193Provider,boingSendTransaction,requestAccounts,readChainIdHex,providerSupportsBoingNativeRpc(walletProvider.ts; BOING-DAPP-INTEGRATION.md) - Directory snapshot (Boing-only) —
fetchNativeDexDirectorySnapshot(defaults +pairs_count+ optional log backfill),pickNativeDexPoolFromRegisterLogs,resolveNativeDexPoolForTokens(logs|simulate|auto),nativeDexPairKey,suggestNativeDexRegisterLogCatchUpRange(nativeDexDirectory.ts; no external chain RPC) - DEX onboarding + preflight —
formatBoingNativeDexNotEvmDisclaimer,describeNativeDexDefaultGaps,assertBoingNativeDexToolkitRpc,formatNativeDexToolkitPreflightForUi,buildNativeCpPoolSwapExpressTx,buildNativeDexMultihopSwapExpressTxFromRoute128/160,applyNativeDexMultihopSimulationToContractCallTx(nativeDexSeamless.ts); wallet copyBOING_WALLET_RPC_METHODS_NATIVE_DAPP,explainEthSendTransactionInsufficientForBoingNativeCall,connectInjectedBoingWallet,mapInjectedProviderErrorToUiMessage(walletProvider.ts) - CP routing + aggregation (off-chain) —
quoteCpPoolSwap,rankDirectCpPools,findBestCpRoutes,pickFirstMultihopCpRoute,uniqueSortedTokenHex32FromCpRoute,minOutFloorAfterSlippageBps,encodeNativeDexMultihopRouterCalldata*FromRoute,quoteCpEvenSplitAcrossDirectPools,hydrateCpPoolVenuesFromRpc,fetchCpRoutingFromDirectoryLogs(nativeDexRouting.ts);buildNativeDexMultihopRouterAccessList,mergeNativeDexMultihopRouterAccessListWithSimulation(nativeAmmPool.ts) - CREATE2 address prediction —
predictCreate2ContractAddress,predictNativeCpPoolCreate2Address…predictNativeCpPoolV5Create2Address,nativeCpPoolCreate2SaltV1Hex…nativeCpPoolCreate2SaltV5Hex,NATIVE_CP_POOL_CREATE2_SALT_V1…V5(create2.ts; matchesboing_primitives::create2_contract_address/native_amm.rs; see NATIVE-AMM-CALLDATA.md § CREATE2) - Indexer-scale reads —
getIndexerChainTips/clampIndexerHeightRange/planIndexerChainTipsWithFallback/planIndexerCatchUp(indexerSync.ts; fallback whenboing_getSyncStateis -32601);fetchBlocksWithReceiptsForHeightRange(replay path: full blocks + receipts; demos: fetch-blocks-range.mjs, indexer-ingest-tick.mjs);summarizeIndexerFetchGaps,mergeInclusiveHeightRanges,unionInclusiveHeightRanges,subtractInclusiveRangeFromRanges,blockHeightGapRowsForInsert,nextContiguousIndexedHeightAfterOmittedFetch(indexerBatch.ts,indexerGaps.ts; pruned gaps, D1 row shapes, archive backfill subtraction);getLogsChunked/planLogBlockChunks(≤128-block spans per RPC-API-SPEC.md); optionalmaxConcurrenton chunked logs and height-range fetches;mapWithConcurrencyLimit;flattenReceiptsFromBundles;fetchReceiptsForBlockHeight;fetchReceiptsForHeightRange(optionalonMissingBlock: 'omit') — ingestion pseudo-flow: INDEXER-RECEIPT-AND-LOG-INGESTION.md; example DDL: tools/observer-indexer-schema.sql; JSON + SQLite ingest: examples/observer-ingest-reference (ingest-tick,ingest-sqlite-tick+BOING_SQLITE_PATH); D1 cron sample: examples/observer-d1-worker; backlog: NEXT-STEPS-FUTURE-WORK.md - Universal contract deploy index —
rpcTransactionJsonToTransactionInput,transactionIdFromUnsignedRpcTransaction,extractUniversalContractDeploymentsFromBlock/extractUniversalContractDeploymentsFromBlockJson,CONTRACT_DEPLOY_INIT_CODE_MARKER, row types (universalContractDeployIndex.ts; shipped 0.3.1+). Optional hosted materialization + REST/SSE: HANDOFF_Universal_Contract_Deploy_Indexer.md,workers/deploy-registry-indexer. - Fixed contract submitter —
createNativeContractSubmitter({ client, secretKey32, senderHex, contractHex, accessList? })→.submitCalldata(bytes) registerDappMetrics(hexContract, hexOwner),submitIntent(hexSignedIntent)qaCheck(hexBytecode, purposeCategory?, descriptionHash?, assetName?, assetSymbol?)— pre-flight QA without submitting (same param order as nodeboing_qaCheck)qaPoolList(),qaPoolConfig(),qaPoolVote(txHashHex, voterHex, vote, signedVoteTxHex?)— QA pool for Unsure deploys (public_membershiprequires a signedQaPoolVotetx)getNetworkFeePolicy(),operatorApplyFeePolicy(json)— native fee split / treasury leviesfaucetRequest(hexAccountId)— testnet only
- RPC probe —
- BoingRpcError —
code,message,data,method;isQaRejected,isQaPendingPool,pendingPoolTxHash,isQaPoolDisabled,isQaPoolFull,isQaPoolDeployerCap,qaData;toString()for logging. - Hex helpers —
ensureHex,bytesToHex,hexToBytes,accountIdToHex,hexToAccountId,validateHex32,isBoingNativeAccountIdHex(32-byte account id check for multi-wallet wizards).
All 32-byte IDs (account, hash) are hex strings with or without 0x prefix. Invalid hex or wrong length throws before the request.
Tokens (mirrors boing token)
Deploy, mint, transfer and read the canonical reference fungible token. Output is byte-identical to the Rust CLI (see BOING-REFERENCE-TOKEN.md).
import { createClient, deployBoingToken, transferBoingToken, getBoingTokenBalance, hexToBytes } from '@boing-network/boing-sdk';
const client = createClient('https://testnet-rpc.boing.network');
const seed = hexToBytes(process.env.BOING_SECRET_HEX!); // 32-byte Ed25519 seed; never hard-code or commit it
const { tokenHex, deployerHex } = await deployBoingToken(client, seed, {
symbol: 'TKN',
name: 'My Token',
initialSupply: 1_000_000n, // one-time mint_first at nonce + 1 (fixed supply)
});
await transferBoingToken(client, seed, { tokenHex, toHex: '0x…', amount: 250_000n });
console.log(await getBoingTokenBalance(client, tokenHex, deployerHex));
Pass dryRun: true (and nonce) to get { txId, signedHex } without submitting. Unsigned builders (buildBoingTokenDeployTransaction, buildBoingTokenMintTransaction, buildBoingTokenTransferTransaction) work with signTransactionInputWithSigner or a wallet.
Native AMM pools (mirrors boing pool)
Create, fund, swap with slippage protection, and read native constant-product pools (templates v1..v7). Byte-identical to the Rust CLI; see NATIVE-AMM-CLI.md, including the custody caveat (deposits and swap inputs are ledger-only in v1..v7).
import { createClient, createBoingPool, addBoingPoolLiquidity, swapBoingPool, getBoingPoolState, hexToBytes } from '@boing-network/boing-sdk';
const client = createClient('http://127.0.0.1:8545');
const seed = hexToBytes(process.env.BOING_SECRET_HEX!);
const { poolHex } = await createBoingPool(client, seed, { version: 7, feeBps: 30n });
await addBoingPoolLiquidity(client, seed, { poolHex, amountA: 10_000n, amountB: 40_000n });
await swapBoingPool(client, seed, { poolHex, direction: 'a-to-b', amountIn: 1_000n, slippageBps: 50n });
console.log(await getBoingPoolState(client, poolHex));
Submitting transactions
The node expects hex-encoded bincode-serialized SignedTransaction. Encoding matches Rust boing-primitives (bincode 1.3); see BOING-SIGNED-TRANSACTION-ENCODING.md.
Options:
- TypeScript (Node or bundler) — build + sign with a 32-byte Ed25519 secret key:
import {
createClient,
fetchNextNonce,
buildTransferTransaction,
signTransactionInput,
} from '@boing-network/boing-sdk';
const client = createClient('http://localhost:8545');
const senderHex = '0x' + '<64-hex of public key>';
const secret32 = new Uint8Array(32); // your signing seed / secret key bytes
const nonce = await fetchNextNonce(client, senderHex);
const tx = buildTransferTransaction({
nonce,
senderHex,
toHex: '0x' + '<64-hex recipient>',
amount: 1n,
});
const signedHex = await signTransactionInput(tx, secret32);
await client.simulateTransaction(signedHex);
await client.submitTransaction(signedHex);
-
Injected wallet —
boing_sendTransaction/boing_signTransactionin Boing Express (BOING-EXPRESS-WALLET.md). -
Rust CLI —
cargo run -p boing-cli -- …for local dev. -
Custom signer —
signTransactionInputWithSigner(tx, async (hash) => …)(must return 64-byte Ed25519 signature overhash).
Protocol QA: Contract deploys are checked in the mempool (boing_qa). Use qaCheck before submit and purpose-bearing deploy types from the wallet. See QUALITY-ASSURANCE-NETWORK.md.
dApp checklist: BOING-DAPP-INTEGRATION.md.
See RPC-API-SPEC.md, BUILD-ROADMAP.md, and BOING-VM-CAPABILITY-PARITY-ROADMAP.md.
Simulate → access list → submit (P4)
Use submitTransferWithSimulationRetry, submitContractCallWithSimulationRetry, or submitDeployWithPurposeFlow so the node can widen access_list when boing_simulateTransaction returns access_list_covers_suggestion: false (merges suggested_access_list via mergeAccessListWithSimulation).
import {
createClient,
submitTransferWithSimulationRetry,
explainBoingRpcError,
senderHexFromSecretKey,
hexToBytes,
} from '@boing-network/boing-sdk';
const client = createClient(process.env.BOING_RPC_URL ?? 'http://127.0.0.1:8545');
const secret = hexToBytes(process.env.BOING_SECRET_HEX!); // 0x + 64 hex
const senderHex = await senderHexFromSecretKey(secret);
try {
const { tx_hash, lastSimulation, attempts } = await submitTransferWithSimulationRetry({
client,
secretKey32: secret,
senderHex,
toHex: process.env.BOING_TO_HEX!,
amount: 10n,
});
console.log({ tx_hash, gas: lastSimulation.gas_used, attempts });
} catch (e) {
console.error(explainBoingRpcError(e));
}
Deploy path runs boing_qaCheck first; reject throws BoingRpcError with isQaRejected. On submitTransaction, catch BoingRpcError for mempool QA (-32050), pool (-32051), and pool caps (-32054..-32056). Code reference: BOING-RPC-ERROR-CODES-FOR-DAPPS.md.
Canonical scripts: examples/native-boing-tutorial (transfer, contract-call, deploy-minimal).
API additions (transaction flows)
senderHexFromSecretKey(secret32)— deriveAccountIdhex from signing seed.submitTransferWithSimulationRetry,submitContractCallWithSimulationRetry,submitDeployWithPurposeFlow— seesubmitFlow.ts.SimulationFailedError— simulation failed without an access-list retry path.explainBoingRpcError(e)— human-readable string forBoingRpcErrorand QA pool codes.
Tests
cd boing-sdk && npm install && npm test
Golden vectors are tied to cargo run -p boing-primitives --example dump_bincode.
Planned
- CLI auto-completion, contract templates. See DEVELOPMENT-AND-ENHANCEMENTS.md.