dgb-wallet/packages/psbt/scripts/build-tx-demo.ts
Digibyte.X d50286a9a5 DigiByte.X Wallet — recovery-first web wallet monorepo
Packages: core (BIP39/32/44/49/84/86 derivation, WIF), keystore (PBKDF2 +
AES-GCM), psbt, p2p (BIP157/158 SPV daemon, Dandelion++ broadcast with
relay-echo verification), blockbook, rpc, electrum, net, bns, web-ui
(React/Vite), mobile (Expo). ops/ holds the nginx vhost, systemd unit and
push.sh deploy script for digibyte.x/web.
2026-09-20 20:16:52 +02:00

107 lines
4.2 KiB
TypeScript

// End-to-end demo:
// seed → derive key → build PSBT spending a mock UTXO
// → sign → finalize → print raw tx hex + txid
//
// The mock UTXO cannot be broadcast (its txid doesn't exist on-chain),
// but every other step is real: the signature is cryptographically
// valid for the constructed input/output pair. This proves the
// PSBT machinery works end-to-end against DGB network params.
import {
seedFromPhrase,
rootFromSeed,
accountNode,
addressNode,
p2wpkhAddress,
digibyte,
} from '@dgb-wallet/core';
import { payments } from 'bitcoinjs-lib';
import { ECPairFactory } from 'ecpair';
import * as ecc from '@bitcoinerlab/secp256k1';
import { buildPsbt, signAllInputs, finalizeAndExtract, feeSats, feeRateSatsPerByte, type Utxo } from '../src/index.js';
const ECPair = ECPairFactory(ecc);
// Public BIP39 test vector — safe to hardcode anywhere.
const TEST_SEED = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
console.log('\n=== DigiByte PSBT End-to-End Demo ===\n');
console.log(`Test seed (public BIP39 vector): "${TEST_SEED}"\n`);
const seed = await seedFromPhrase(TEST_SEED);
const root = rootFromSeed(seed);
// BIP84 native-SegWit account, first receive address = sender.
const account = accountNode(root, 84, 0);
const senderNode = addressNode(account, 0, 0);
const senderAddress = p2wpkhAddress(senderNode);
// Get the signing key. We need an ECPair with the WIF for signing;
// build one from the BIP32 private key.
if (!senderNode.privateKey) throw new Error('missing private key on derived node');
const keyPair = ECPair.fromPrivateKey(Buffer.from(senderNode.privateKey), { network: digibyte });
// Recipient — next receive address in the same account (chain=0, index=1).
const recipientNode = addressNode(account, 0, 1);
const recipientAddress = p2wpkhAddress(recipientNode);
// Change back to sender for simplicity in the demo (real wallet would
// use a fresh change address on chain=1).
const changeAddress = senderAddress;
// Compute the scriptPubKey for the sender address; needed as
// witnessUtxo.script for a SegWit input.
const senderP2wpkh = payments.p2wpkh({
pubkey: Buffer.from(senderNode.publicKey),
network: digibyte,
});
if (!senderP2wpkh.output) throw new Error('failed to derive sender scriptPubKey');
const senderScriptHex = senderP2wpkh.output.toString('hex');
// Mock UTXO: pretend the sender has a 1.0 DGB UTXO at txid ...42, vout 0.
const MOCK_TXID = '0000000000000000000000000000000000000000000000000000000000000042';
const utxo: Utxo = {
txid: MOCK_TXID,
vout: 0,
value: 100_000_000, // 1.0 DGB in sats
address: senderAddress,
scriptPubKey: senderScriptHex,
witness: { scriptHex: senderScriptHex, value: 100_000_000 },
};
// Spend plan: send 0.5 DGB to recipient, keep 0.4999 DGB as change
// (0.0001 DGB fee = 10,000 sats).
const SEND_AMOUNT = 50_000_000;
const CHANGE_AMOUNT = 49_990_000;
const FEE = utxo.value - SEND_AMOUNT - CHANGE_AMOUNT;
console.log('Inputs:');
console.log(` ${senderAddress}`);
console.log(` ${MOCK_TXID}:${utxo.vout} = ${utxo.value / 1e8} DGB (mock)`);
console.log('\nOutputs:');
console.log(` recipient ${recipientAddress} ${SEND_AMOUNT / 1e8} DGB`);
console.log(` change ${changeAddress} ${CHANGE_AMOUNT / 1e8} DGB`);
console.log(` fee ${FEE / 1e8} DGB\n`);
// Build → sign → finalize.
const psbt = buildPsbt({
inputs: [utxo],
outputs: [
{ address: recipientAddress, value: SEND_AMOUNT },
{ address: changeAddress, value: CHANGE_AMOUNT },
],
});
signAllInputs(psbt, keyPair);
const { hex, txid } = finalizeAndExtract(psbt);
console.log('Signed transaction:');
console.log(` txid: ${txid}`);
console.log(` fee: ${feeSats(psbt)} sats (${feeRateSatsPerByte(psbt).toFixed(2)} sat/vB)`);
console.log(` hex: ${hex}\n`);
console.log('Notes:');
console.log(' - The UTXO is synthetic; this tx cannot be broadcast.');
console.log(' - Signature IS cryptographically valid for the input/output pair.');
console.log(' - Feed a real UTXO (from scantxoutset, Blockbook, or Electrum-DGB)');
console.log(' and this exact code path produces a broadcastable transaction.\n');