Third-Party STP Client Integration Guide
This guide is for wallets, SDKs, PWA adapters, CLIs, backend services, and AI Agent tools that want to implement an STP client. The goal is language-independent interoperability with a compatible Core Node.
This guide only covers STP client interoperability. Wallet creation, mnemonic import/export, password changes, and ordinary asset sends belong to the SAT20 Wallet or SAT20 Agent Wallet adapter layer. See SAT20 Agent Wallet.
Integration Goals
An STP client must be able to:
Discover and verify a Core Node.
Manage the user's channel identity, signatures, and local channel state.
Construct, sign, send, and verify STP protocol messages.
Query the Bitcoin L1 indexer and SatoshiNet L2 indexer.
Drive open, splicing-in, unlock, lock, lock-with-expand, splicing-out, close, force close, and punish flows.
Recover pending operations after network errors, indexer delay, peer offline events, or local restarts.
Every value movement should be brought back to verifiable evidence: transactions, UTXOs, commitment height, commitments, ascend/descend records, and punishment coverage. A Core Node response, balance display, or single indexer response is not enough by itself.
Recommended Architecture
Wallet boundary
Stores keys, mnemonic, and user authorization. This can be a PWA, hardware wallet, mobile wallet, or local secure wallet
STP engine
Implements STP messages, commitment state, channel actions, and recovery
Transaction engine
Builds and verifies Bitcoin L1, SatoshiNet, commitment, punish, and sweep transactions
Asset engine
Parses BTC, ORDX, Runes, BRC20, and amount precision
State store
Persists channel data, commitment height, commitments, revocation material, and pending operations
Chain query
Queries L1/L2 UTXOs, assets, transaction visibility, confirmations, ascend/descend, and channel state
Safety monitor
Produces safety snapshots, commitment exports, punish status, force-close plans, and sweep plans
For AI Agents, the recommended path is SAT20 PWA Wallet Adapter: the PWA stores keys and database state, while the Agent sends authorized JSON operations and reads safety evidence.
Core Node Discovery
Before connecting to a Core Node, the client must verify that:
Wallet, Core Node, Bitcoin L1 indexer, and SatoshiNet L2 indexer are on the same network.
The Core Node public key comes from trusted discovery or explicit user configuration.
Protocol version, CSV parameters, service fees, and feature list are acceptable.
The client can independently query L1/L2 state and does not rely only on the Core Node's response.
A normal user connecting to a Core Node does not stake assets. Staking is only relevant when a node connects to a Bootstrap Node and prepares to become a Core Node.
Example discovery response:
Message Envelope
Authenticated STP messages should use a deterministic envelope:
Signing rules:
All fields except
signatureare signed.Serialization must be deterministic, such as canonical JSON or an equivalent fixed encoding.
The receiver verifies sender key, channel identity, chain ID, and message type.
Any message that changes commitment state must include the current
commit_height.The client rejects height rollback, chain mismatch, invalid signatures, and asset amount inconsistency.
Operation Interface
The client should expose a language-independent JSON operation interface to wallets, CLIs, and Agents. It should hide asset UTXOs, fee UTXOs, and channel-internal inputs. Coin selection and transaction package construction are wallet responsibilities.
stp.status
stp.statusReturns Core Node status, channel list, commitment height, pending operations, and indexer synchronization state.
stp.open
stp.openOpens a private channel with a Core Node.
The client internally selects or constructs the L1 funding input. Normal user open does not require staking.
stp.splicing_in
stp.splicing_inBrings Bitcoin L1 assets into the channel.
The client selects asset inputs and ordinary BTC fee inputs internally. For BRC20, it can create a transfer inscription if no suitable transfer UTXO exists. The Agent passes asset and amount, not raw input lists.
stp.expand
stp.expandAdds assets already located at the channel address into channel management.
Expand is used after interrupted splicing-in, rebuild, or direct transfer into the channel address. The client must use L1/L2 indexer evidence to decide whether ascend is needed and must not double-issue SatoshiNet assets.
stp.unlock
stp.unlockReleases channel assets to the user's SatoshiNet address.
Unlock does not require user-provided fee rate or fee UTXO. SatoshiNet does not use Bitcoin L1 fee-rate semantics for this operation.
stp.lock
stp.lockLocks SatoshiNet personal-address assets back into the channel.
The client chooses spendable L2 UTXOs internally and verifies that the asset has moved from pending into spendable state.
stp.lock_with_expand
stp.lock_with_expandRestores channel control when capacity is insufficient.
This is a user-safety operation. It lets the client regain channel coverage without manually exiting to L1 and re-entering.
stp.splicing_out
stp.splicing_outExits channel assets to Bitcoin L1.
The adapter handles asset inputs, transfer inscriptions, de-anchor, and fee inputs internally. If ordinary BTC fee funds are insufficient, it should return a clear error and next action.
stp.close
stp.closeCooperatively closes or force-closes a channel.
If cooperative close is impossible, the client must expose a force-close plan before broadcasting.
Safety Interfaces
Every production-quality client should expose:
stp.safety_snapshot
Returns channel point, commitment height, commitment availability, balances, CSV, and punishment coverage
stp.commitment_export
Exports current commitment transactions and read-only verification data
stp.punish_status
Lists revoked remote commitments and punishment coverage
stp.punish_build
Builds and dry-runs a punishment transaction for a given old commitment
stp.punish_broadcast
Broadcasts a verified punishment transaction
stp.force_close_plan
Produces the local commitment, CSV delay, and sweep conditions
stp.sweep_build
Builds and verifies a sweep transaction after CSV
stp.transaction
Tracks pending reservation, txids, channel state, and next action
Agents must treat PUNISH_COVERAGE_UNKNOWN and PUNISH_COVERAGE_MISSING as blockers for value movement.
Successful Response Shape
Value-moving operations should return both user-facing status and verifiable evidence:
If the result is unknown, return a structured pending state instead of a generic failure.
Commitment Balance vs Spendable UTXO
Clients must separate commitment balances from spendable SatoshiNet UTXOs.
local_balance and remote_balance describe the current commitment allocation. l2_spendable_balance describes UTXOs the wallet can spend on SatoshiNet. A freshly spliced-in asset may already be part of the commitment, while its L2 anchor output is still pending. In that case, unlock or lock must wait.
Asset Rules
Plain sats
Bitcoin L1 has dust and fee constraints. SatoshiNet L2 does not have the same dust limit
ORDX
ORDX binds to sats. Ordinals NFT-like ordx:o objects are ignored by STP ascend
Runes
Runes can be carried by zero-sat L2 UTXOs. L1 still follows Bitcoin output constraints
BRC20
BRC20 can require transfer inscription packages on L1. The adapter chooses or creates transfer UTXOs internally
If a UTXO carries multiple assets, only the explicitly requested asset ascends. Other assets are excluded from the expected L2 balance.
Unknown-Result Recovery
Timeout, EOF, connection reset, service unavailability, and temporary indexer lag are unknown results, not definite failures.
Recovery sequence:
Stop resubmitting the same request and lock related inputs.
Persist request JSON, reservation, txids, channel id, asset, amount, and error text.
Query pending reservation.
Query related Bitcoin L1 and SatoshiNet txids.
Query Core Node channel status, commitment height, channel point, and pending status.
If any transaction is visible, any side advanced state, or the reservation exists, keep tracking the original operation.
Retry only after both sides remain at the same old safe state, no pending reservation exists, and all related transactions remain invisible.
Testnet Interoperability
Before connecting to mainnet, a third-party client should complete these testnet flows:
Open a private channel as a normal user without staking.
Read a safety snapshot after open.
Unlock and lock plain sats.
Splicing-in, unlock, and lock at least one Runes asset.
Splicing-in, unlock, and lock at least one BRC20 asset.
Splicing-out at least one protocol asset to Bitcoin L1.
Recover from unknown network results without double-spending inputs.
Prove punishment coverage after commitment height changes.
Generate a force-close plan when peer is unavailable.
Restart the client and recover pending reservations and safety state.
Mainnet release requires stricter long-running, reorg, indexer-delay, peer-offline, duplicate-request, database-recovery, and authorization-cancel tests.
Last updated