PSBT encoding service for the XChain Platform. Takes an ACTION string, a set of UTXOs, and a public key, and returns an unsigned Partially Signed Bitcoin Transaction (PSBT) ready for the caller to sign and broadcast. The encoder is fully stateless: no database, no persistent connections, every call is independent.
- Four encoding formats: OP_RETURN (76B), P2SH (476B), P2WSH (476B), and multisig (~61B/key); auto-selected by payload size
- AES-128-CTR obfuscation: derives key and IV from the first input's txid;
XCHNmagic prefix on all payloads - Two-transaction P2SH/P2WSH: automatic tx1 (fund) -> tx2 (spend/reveal) orchestration with marker OP_RETURN
- UTXO selection: largest-first selection, duplicate removal, optional unconfirmed filtering, automatic change output
- Fee estimation: byte-accurate transaction size estimation per format via
TxSizeEstimator; dust floor enforcement - Fee rate caps: caller-supplied
fee/feePerKbis capped atMAX_FEE_RATE_MULTIPLIERx the node's own fee estimate (default 100x), so a hostile or buggy request cannot drain inputs into miner fee;MAX_FEE_RATE_KBadds an optional absolute cap - Input validation: centralized parameter validation (
validator.js) with typed errors for all 15createTransactionparameters - Multi-chain support: Bitcoin, Litecoin, and Dogecoin on mainnet, testnet, and regtest (9 network configs)
- Replace-By-Fee: optional RBF signaling via sequence number
- Custom outputs: arbitrary address/value outputs (e.g., COINPay native coin payments)
- Token-gated content support: encodes FILE v1 gated files and
BATCH(FILE, MESSAGE)issuer-publish flows; ciphertext travels asrawDatavia P2WSH alongside the action string - JSON-RPC API: Express server with Helmet security headers, optional API key auth, configurable rate limiting, CORS
- Browser bundle: Browserify build for client-side PSBT generation without a server
- Single-instance guard: refuses to boot when
ENCODER_REPLICASdeclares more than one replica, and takes an exclusive PID lockfile against a second local process; the UTXO reservation guard and rate limiter are in-process only until a shared store exists - 1330+ tests: unit, integration, e2e, boundary, security, fuzz, chaos, mutation, regression, performance, smoke
Full encoder documentation is available in the xchain-documentation repository:
| Document | Description |
|---|---|
| README | Overview, encoding process, format details, API, testing, configuration |
| Format Selection | Decision guide for encoding formats with size limits and trade-offs |
git clone https://github.com/XChain-Platform/xchain-encoder.git
cd xchain-encoder
npm installCreate a .env file:
NETWORK=bitcoin-regtest
NODE_URL=127.0.0.1
NODE_PORT=8332
NODE_USER=rpcuser
NODE_PASSWORD=rpcpass
ENCODER_API_PORT=3000Start the encoder:
npm run api| Variable | Required | Default | Description |
|---|---|---|---|
NETWORK |
Yes | (none) | Coin and network (bitcoin-mainnet, dogecoin-testnet, litecoin-regtest, etc.) |
NODE_URL |
Yes | (none) | Coin node RPC host (e.g., 127.0.0.1) |
NODE_PORT |
Yes | (none) | Coin node RPC port |
NODE_USER |
Yes | (none) | RPC username |
NODE_PASSWORD |
Yes | (none) | RPC password |
ENCODER_API_PORT |
No | 3000 |
JSON-RPC API port |
NODE_RPC_TIMEOUT |
No | 30000 |
Coin-node RPC call timeout in milliseconds |
UTXO_TRACKER_URL |
No | (none) | xchain-utxo-tracker service host |
UTXO_TRACKER_API_PORT |
No | (none) | xchain-utxo-tracker service port |
UTXO_TRACKER_MAX_LAG_BLOCKS |
No | 2 |
Max blocks the utxo-tracker's reported sync lag may be before create_tx refuses to select UTXOs from it |
MAX_FEE_RATE_KB |
No | Uncapped | Absolute maximum fee rate in sat/kB |
MAX_FEE_RATE_MULTIPLIER |
No | 100 |
Caps caller-supplied fee/feePerKb at this multiple of the node's fee estimate (0 disables) |
XCHAIN_COMPRESSION_DEFAULT |
No | Enabled | Deployment default for transparent FILE compression; set 0, false, or off to disable |
ENCODER_REPLICAS |
No | 1 |
Deploy-manifest declared replica count; boot refuses above 1 until a shared UTXO-reservation store exists |
API_KEY |
No | Disabled | API key for x-api-key header authentication |
ENCODER_RATE_LIMIT_RPM |
No | 60 |
Maximum requests per minute per IP |
ENCODER_MAX_RPC_BATCH |
No | 20 |
Maximum JSON-RPC batch array length per request |
ENCODER_MAX_CONCURRENT_REQUESTS |
No | 50 |
Global cap on requests served at once across all client IPs; excess gets an immediate 429 + Retry-After instead of queueing. GET /status and GET /openrpc.json are exempt; 0 disables |
ENCODER_MAX_CONCURRENT_PROBES |
No | 16 |
Private concurrency reserve for the two exempt probe routes, so healthchecks stay answerable while the cap above sheds without becoming an uncapped bypass; 0 disables |
ENCODER_TRUST_PROXY |
No | loopback, uniquelocal |
Express trust proxy setting; controls which hop the per-IP rate limiter keys the client IP on. false, a hop count, or an address/CIDR list per the Express docs |
CORS_ORIGIN |
No | Disabled | Allowed CORS origin(s): * for any, one origin, or a comma-separated allowlist matched per-origin (browser wallet shells each send a different origin). A stray * inside a list is not a wildcard, so the grant fails closed |
A Prometheus /metrics endpoint and a structured log shim ship with this
service and stay inert unless switched on: with no env set, no route is
registered, no timer starts and no socket opens. Turn the endpoint on with
METRICS_ENABLED=1 (add METRICS_TOKEN to gate the scrape on a reachable
box), and ship logs with LOG_SHIP_ENABLED=1 plus LOG_SHIP_URL. Full
variable list and the exported metric names are in
src/observability/README.md.
The module is vendored byte-identically from xchain-hub. Edit it there
and re-run xchain-hub/bin/sync-observability.sh; a local edit fails the
parity check CI runs across the vendored copies.
| Command | Description |
|---|---|
npm run api |
Start the JSON-RPC API server |
npm run build |
Production browser bundle (minified) -> dist/xchain_encoder.min.js |
npm run build:dev |
Development browser bundle (unminified) |
npm run smoke-test |
Smoke tests (~52 tests, <1s) |
npm run test:unit |
Unit tests (541 tests) |
npm run test:integration |
Integration tests (112 tests) |
npm run test:boundary |
Boundary condition tests (~100 tests) |
npm run test:security |
Security tests (57 tests) |
npm run test:fuzz |
Property-based fuzz tests (6 suites) |
npm run test:chaos |
Chaos engineering tests (62 tests) |
npm run test:e2e |
End-to-end tests (~158 tests) |
npm run test:regression |
Regression tests (264 tests) |
npm run mutate |
Full mutation testing via StrykerJS |
npm run mutate:quick |
Quick mutation check (XChainEncoder.js only) |
npm run bench |
Performance benchmarks |
npm run bench:full |
Extended benchmarks with JSON output |
npm run bench:soak |
Soak test (sustained load) |
npm test |
Unit tests (hermetic, no external services) |
npm run test:regtest |
Regtest integration tests (requires local bitcoind) |
| Type | Tests | Description |
|---|---|---|
| Unit | 541 | XChainEncoder.createTransaction, prepareData, obfuscate, dataToPubkey, isSegwitUTXO, TxSizeEstimator, CryptoNetworks |
| Integration | 112 | ACTION encoding fidelity, encoding type selection, obfuscation round-trip, UTXO/fee interaction, multi-chain, custom outputs, error handling |
| E2E | ~158 | Full pipeline: API layer, P2SH/P2WSH two-tx orchestration, round-trip encode/decode, multi-chain, edge cases |
| Smoke | ~52 | Module loading, instantiation, network configs, basic PSBT creation, API startup |
| Boundary | ~100 | Payload size limits, chunk boundaries, fee calculation edges, UTXO values, change address, custom outputs, obfuscation |
| Security | 57 | Concurrency/reservation gates, payload-size caps, obfuscation-key handling, input validation |
| Fuzz | 6 | Property-based generative tests over prepareData, dataToPubkey, obfuscate |
| Chaos | 62 | Network failures, input corruption, library monkey-patching, arithmetic edge cases, resource exhaustion, API resilience |
| Mutation | StrykerJS | 896 mutants across XChainEncoder.js, validator.js, TxSizeEstimator.js, CryptoNetworks.js |
| Regression | 264 | Curated critical-path suite: encoding types, obfuscation, fee/UTXO, validator, multi-chain, P2SH/P2WSH, ACTION pipeline, API contract |
| Performance | 3 suites | Baseline benchmarks, full benchmarks with JSON, sustained soak tests |
| Total | 1330+ |
Copyright © 2025-2026 Dankest, LLC
Based on XChain Platform by Dankest, LLC – https://dankest.llc
Licensed under the GNU Affero General Public License v3.0 (AGPL-3.0-or-later) with a commercial license available for proprietary use.
You may use, modify, and distribute this material under the terms of the License. See LICENSE and NOTICE for full terms. See the licensing overview.