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>
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)
- Opens a WalletConnect session and prints a pairing URI + QR.
- After you approve in the wallet:
bch_getAddresses(shows the connected address) andbch_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
-
Get a free WalletConnect/Reown projectId at https://cloud.reown.com (create a project → copy the Project ID).
-
Put a chipnet address in Cashonize. Import a chipnet WIF — e.g. wallet D from
../../.chipnet-keys-d.json(thewiffield) — into Cashonize, and switch Cashonize to chipnet if it exposes a network toggle.- If your Cashonize build only does mainnet for WalletConnect, the handshake +
signMessagestill validate the wiring (they need no funds). SetWC_CHAIN=bch:bitcoincashin that case and we handle chipnet tx-signing at milestone 2.
- If your Cashonize build only does mainnet for WalletConnect, the handshake +
-
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 -
Pair: copy the printed
wc:URI (or scan the QR) into Cashonize → WalletConnect → approve the session. -
Approve the
getAddressesandsignMessageprompts. The connector prints the address + signature and exits withHANDSHAKE 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
bchnamespace.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(dAppCashConnectDapp, walletCashConnectWallet); reference impldevelopers-cash/cashonizecash-connect.js.cashconnect.mjs/npm run connect:cc— rewritten against the installed dApp API (CONFIRM markers resolved fromdist/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
- WC handshake —
connect.mjs(bch_getAddresses+bch_signMessage). ✅ device-validated (2026-05-25, Cashonize onbch:bitcoincash): paired,bch_getAddressesreturned the wallet address andbch_signMessagereturned a valid signature. (chipnetbch:bchtestneeds Cashonize switched to chipnet; the mainnet handshake proves the wiring with no funds.) - CashConnect handshake —
cashconnect.mjs. 🔧 rewritten against the installedcashconnect@0.0.25dApp API (CashConnectDapp(options, chain, dialogs)→start()→connect(requiredNamespaces); CashRPCbch_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. - WizardConnect handshake — third adapter (ParionUSD's connector). ⏳ when its SDK/flow is confirmed.
- 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 hash1bfda2ccafa7ae071cf16f04a04e83491aa4af238ff978690bb7e797acdc3bb9. This is the exact totebet()/ checkersGamePoolEscrow.fund()staking path, shared across all adapters (built once via CashScript). Serialization gotcha: WC params must use libauth'sstringifyformat (<Uint8Array: 0x..>/<bigint: ..n>), not plain hex/JSON — captured insrc/connect/proposer.ts:toWcParams.