Skip to content

Wallet/Application Integration Example (TypeScript)

This example uses the wallet/runtime façade exports and keeps protocol authority distinct from optional supplemental economics support. External adopters should import from package roots only: @bch-stealth/direct-spend-wallet and @bch-stealth/prover-runtime.

import {
  assembleDirectSpendTx,
  buildDirectSpendPlan,
  recoverDirectSpendOutputs,
  type DirectSpendWalletRuntimeExecutorV1,
} from "@bch-stealth/direct-spend-wallet";
import {
  prepareDirectSpendProofInput,
  proveDirectSpend,
  verifyDirectSpend,
} from "@bch-stealth/prover-runtime";

type ProtocolAuthorityInput = {
  outpointRef: string; // authoritative continuation source (txid:vout)
  valueSats: number;
};

type SupplementalInput = {
  outpointRef: string;
  valueSats: number;
} | null;

function runtimeExecutorFromProof(
  proofInput: ReturnType<typeof prepareDirectSpendProofInput>,
  proved: Awaited<ReturnType<typeof proveDirectSpend>>,
  verify: Awaited<ReturnType<typeof verifyDirectSpend>>,
): DirectSpendWalletRuntimeExecutorV1 {
  return async () => ({
    request: proofInput,
    estimate: {
      backendId: proved.prove.backendId,
      estimatedProofBytes: proved.prove.estimatedProofBytes,
      estimatedProofBlobBytes: proved.prove.pbv1Bytes.length,
    },
    prove: proved.prove,
    verify,
  });
}

export async function runDirectSpendHop(args: {
  piv1Bytes: Uint8Array;
  witnessBytes: Uint8Array;
  encryptedPayloadBytes?: Uint8Array | null;
  hintsBytes?: Uint8Array | null;
  protocolAuthority: ProtocolAuthorityInput;
  supplemental: SupplementalInput;
}) {
  const proofInput = prepareDirectSpendProofInput({
    piv1Bytes: args.piv1Bytes,
    witnessBytes: args.witnessBytes,
    encryptedPayloadBytes: args.encryptedPayloadBytes ?? null,
    hintsBytes: args.hintsBytes ?? null,
    determinism: { mode: "deterministic" },
  });
  const proved = await proveDirectSpend(proofInput);
  const verify = await verifyDirectSpend(proved);
  if (!verify.ok) throw new Error(verify.reason ?? "verifyDirectSpend failed");

  const plan = buildDirectSpendPlan({
    request: proofInput,
    runtimeExecutor: runtimeExecutorFromProof(proofInput, proved, verify),
    hostChecks: {
      transcriptBinding: async () => undefined,
      ofm2v2: async () => undefined,
      continuationShell: async () => undefined,
      malformedCarrier: async () => undefined,
      routingCompatibility: async () => undefined,
    },
    assembleTx: async (run) => {
      // Protocol authority input is mandatory.
      // Supplemental input is optional economics support only.
      return {
        authority: args.protocolAuthority,
        supplemental: args.supplemental,
        proofBytes: run.prove.proofBytes,
        pbv1Bytes: run.prove.pbv1Bytes,
      };
    },
    broadcastTx: async (txPlan) => {
      // Replace with your tx builder + BCH broadcast implementation.
      return {
        txidHex: "replace-with-broadcast-txid",
        rawTxHex: buildRawTxHexFromPlan(txPlan),
      };
    },
    persistState: async (broadcastResult) => {
      // Persist latest continuation outpoint + sender state.
      await persistContinuationState(broadcastResult.txidHex);
    },
  });

  const assembled = await assembleDirectSpendTx(plan);
  const recovery = recoverDirectSpendOutputs({
    identity: {
      scanKeyAHex: "11".repeat(32),
      spendKeyBHex: "22".repeat(32),
    },
    currentTipHeight: 0,
    chainCandidates: [],
  });

  return { assembled, recovery };
}

function buildRawTxHexFromPlan(_plan: unknown): string {
  throw new Error("Provide wallet-specific tx assembly/signing implementation");
}

async function persistContinuationState(_txidHex: string): Promise<void> {
  // Implement wallet state persistence.
}

Required Preservation Rules

  • The authoritative protocol source is the continuation input, not supplemental funding.
  • Supplemental transparent input (if used) must be explicitly classified as economics support.
  • Runtime proof/PBV1 bytes must remain identical through verify, carrier packing, and broadcast.
  • Host checks should fail closed before broadcast if boundary mismatches are detected.
  • packages/direct-spend-wallet/src/* and standalone/prover-runtime/src/* are internal implementation paths, not stable external integration entrypoints.