deviant/Parameters/WalletConnect/examples/spike-readme.md
walerikus 35076606c7 chore: checkpoint in-flight work across shell, contracts, and docs
Snapshot of the shared working tree's outstanding changes (multiple
concurrent workstreams), excluding generated runtime state files
(.football-tote-state.json, .tote-platform-state.json, .usdm-historical-chart.html).

Spans: wallet shell (connect/, keystore MultiKeyStore+vault, screens,
currency picker, sign-message), verify artifacts, new Potidaea/DRA/tote
contracts + regtest suites, cross-chain/T6 + WalletConnect Parameters,
a telegram-orderbook subproject, design mockups, and docs. See
shells/app/WALLET-COORDINATION.md for the per-file ownership split.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 02:02:05 +02:00

7.6 KiB

Deviant tote — WalletConnect connector (chipnet test)

Standalone WalletConnect v2 "dApp" that pairs with a BCH wallet (Cashonize) and proves a chipnet address can connect and sign — the first milestone toward non-custodial tote staking (wc2-bch-bcr spec: namespace bch, chain bch:bchtest, methods bch_getAddresses / bch_signMessage / bch_signTransaction).

It is separate from the main project (its own node_modules) so it never touches Deviant's pinned dependencies.

What it does (milestone 1)

  1. Opens a WalletConnect session and prints a pairing URI + QR.
  2. After you approve in the wallet: bch_getAddresses (shows the connected address) and bch_signMessage (proves the wallet can sign with that key).

The tote bet co-sign (bch_signTransaction of an unsigned tote tx the wallet signs its own stake input on) is milestone 2 — added once this handshake works on your device.

Steps

  1. Get a free WalletConnect/Reown projectId at https://cloud.reown.com (create a project → copy the Project ID).

  2. Put a chipnet address in Cashonize. Import a chipnet WIF — e.g. wallet D from ../../.chipnet-keys-d.json (the wif field) — into Cashonize, and switch Cashonize to chipnet if it exposes a network toggle.

    • If your Cashonize build only does mainnet for WalletConnect, the handshake + signMessage still validate the wiring (they need no funds). Set WC_CHAIN=bch:bitcoincash in that case and we handle chipnet tx-signing at milestone 2.
  3. Run the connector (from this folder, or with the prefix from the repo root):

    WC_PROJECT_ID=<your-project-id> npm run connect
    # optional: WC_CHAIN=bch:bitcoincash WC_PROJECT_ID=... npm run connect
    
  4. Pair: copy the printed wc: URI (or scan the QR) into Cashonize → WalletConnect → approve the session.

  5. Approve the getAddresses and signMessage prompts. The connector prints the address + signature and exits with HANDSHAKE OK.

Verified so far

  • The connector loads, initializes SignClient, and reaches the WalletConnect relay (it errors only on a missing/invalid projectId — confirming the dApp side is wired). The pairing + signing half runs on your device and is what these steps drive.

Multi-protocol requirement (WalletConnect + CashConnect + WizardConnect)

BCH has multiple wallet-connection standards, and the tote must support them so bettors on any wallet ecosystem can stake non-custodially:

  • WalletConnect (wc2-bch-bcr) — WalletConnect v2 + the bch namespace. connect.mjs / npm run connect:wc. Cashonize speaks it.
  • CashConnect — BCH-native protocol by developers.cash (@jimtendo) built on CashRPC (WC2 is its transport today; HTTP/LibP2P planned). Merged into mainline Cashonize. Package cashconnect@0.0.25 (dApp CashConnectDapp, wallet CashConnectWallet); reference impl developers-cash/cashonize cash-connect.js. cashconnect.mjs / npm run connect:cc — rewritten against the installed dApp API (CONFIRM markers resolved from dist/index.d.ts); handshake (getBalance + signMessage) ready for on-device pairing. The CashConnect wire API is NOT the same as WalletConnect's — see the diagram note below.
  • WizardConnect — a third BCH connector (Riften Labs / NOSTR-based), used by the ParionUSD stablecoin. To add as a third adapter once its SDK/flow is confirmed.

Two sides, both needed. A dApp side (the tote proposes txs — these scripts) and a wallet side (the Deviant wallet, under construction, will respond — host CashConnectWallet / a WC2 wallet responder so Deviant users can connect to the tote and to other dApps like ParionUSD). The scripts here are the dApp side; the wallet-side integration lands in the Deviant wallet.

Reusable proposer core (src/connect/proposer.ts, unit-tested). The dApp side is generalised into a protocol-agnostic module: a WalletSession interface (the seam every adapter implements) + buildCoSignRequest(tb, opts) (wraps CashScript generateWcTransactionObject) + proposeCoSign(session, tb, opts). Any Deviant dApp — tote, checkers, event-prediction — is written ONCE against WalletSession: build the tx (covenant inputs unlocked by the contract; each user input via placeholderP2PKHUnlocker), proposeCoSign, done. The WC / CashConnect / WizardConnect adapters (these scripts) are interchangeable behind it; the same request object works over either protocol, only the transport differs.

Architecture: shared tx-building, pluggable connection adapters. The unsigned tote transaction is built ONCE — CashScript's TransactionBuilder.generateWcTransactionObject({ broadcast, userPrompt }) produces the { transaction, sourceOutputs, broadcast, userPrompt } object, with the bettor's own stake input marked unsigned via placeholderP2PKHUnlocker(bettorAddress). Only the connect / session / sign-request layer differs per protocol:

buildToteBetTx()  ──>  TransactionBuilder ──┬─► WC adapter: WcTransactionObject ──► bch_signTransaction (verbatim, toWcParams)
                                            └─► CC adapter: map → SignTransactionV0Params[] ──► bch_signTransaction_V0

What is shared is the tx-building (the TransactionBuilder with the bettor's stake input marked unsigned via placeholderP2PKHUnlocker). The wire request is not: WalletConnect's bch_signTransaction consumes the WcTransactionObject verbatim, but CashConnect's bch_signTransaction_V0 takes Array<SignTransactionV0Params> (template-/wallet-scoped inputs+outputs) — a different shape that a CashConnect adapter must map the built tx into. (The cashconnect SDK applies its own libauth stringify, so a CC payload must NOT be pre-toWcParams-ed.) Both still return a signed-tx hex; the covenant inputs are signed by the dApp (CashScript), the wallet signs only the bettor's P2PKH input. This WcTransactionObject→CashRPC mapping is the open CashConnect co-sign milestone.

Milestones

  1. WC handshake — connect.mjs (bch_getAddresses + bch_signMessage). ✅ device-validated (2026-05-25, Cashonize on bch:bitcoincash): paired, bch_getAddresses returned the wallet address and bch_signMessage returned a valid signature. (chipnet bch:bchtest needs Cashonize switched to chipnet; the mainnet handshake proves the wiring with no funds.)
  2. CashConnect handshake — cashconnect.mjs. 🔧 rewritten against the installed cashconnect@0.0.25 dApp API (CashConnectDapp(options, chain, dialogs) → start() → connect(requiredNamespaces); CashRPC bch_getBalance_V0 + bch_signMessage_V0). Imports + exports verified to resolve; pairing runs on-device. Co-sign (bch_signTransaction_V0) needs the WcTransactionObject→CashRPC mapping (above) and is the next milestone.
  3. WizardConnect handshake — third adapter (ParionUSD's connector). ⏳ when its SDK/flow is confirmed.
  4. Co-sign (bch_signTransaction) — signtx.mjs (npm run connect:signtx): builds a self-send with the user input as a placeholder, requests the wallet co-sign. ✅ device-validated (2026-05-25, Cashonize/mainnet, broadcast:false): returned signed-tx hash 1bfda2ccafa7ae071cf16f04a04e83491aa4af238ff978690bb7e797acdc3bb9. This is the exact tote bet() / checkers GamePoolEscrow.fund() staking path, shared across all adapters (built once via CashScript). Serialization gotcha: WC params must use libauth's stringify format (<Uint8Array: 0x..> / <bigint: ..n>), not plain hex/JSON — captured in src/connect/proposer.ts:toWcParams.