Skip to content

feat: accept optional walletProvider to unblock third-party OPNet wallets#128

Open
bitbragi wants to merge 1 commit into
btc-vision:mainfrom
MyScribe-Ecosystem:feat/pluggable-wallet-provider
Open

feat: accept optional walletProvider to unblock third-party OPNet wallets#128
bitbragi wants to merge 1 commit into
btc-vision:mainfrom
MyScribe-Ecosystem:feat/pluggable-wallet-provider

Conversation

@bitbragi
Copy link
Copy Markdown

@bitbragi bitbragi commented Apr 14, 2026

Closes #127.

Summary

TransactionFactory and MessageSigner currently auto-detect window.opnet.web3 as the sole browser signing provider. This blocks any third-party OPNet-compatible wallet (injected at other window globals, such as window.myscribe) from being used through the documented signer: null browser flow — even when the caller has explicitly connected a different wallet via @btc-vision/walletconnect.

This PR adds an optional walletProvider?: Web3Provider parameter to every public signing entry point. When provided, it is used directly instead of auto-detecting window.opnet.web3. When omitted, all methods fall through to the existing auto-detect path — preserving full backward compatibility.

Changes

src/transaction/TransactionFactory.ts

  • New optional walletProvider?: Web3Provider parameter on the four public signing entry points:
    • createCancellableTransaction(params, walletProvider?)
    • signInteraction(params, walletProvider?)
    • signDeployment(params, walletProvider?)
    • createBTCTransfer(params, walletProvider?)
  • The four private detect*OPWallet helpers now accept and use the same parameter.
  • Introduced a private helper getDefaultBrowserWallet(): Web3Provider | null that wraps the existing window.opnet.web3 auto-detect logic. Replaces the four duplicated null-check blocks.

src/keypair/MessageSigner.ts

  • Same parameter added to:
    • signMessageAuto(message, keypair?, walletProvider?)
    • tweakAndSignMessageAuto(message, keypair?, network?, walletProvider?)
    • signMLDSAMessageAuto(message, mldsaKeypair?, walletProvider?)
    • trySignSchnorrWithOPWallet(message, walletProvider?)
    • trySignMLDSAWithOPWallet(message, walletProvider?)
    • verifyMLDSAWithOPWallet(message, signature, walletProvider?)
    • getMLDSAPublicKeyFromOPWallet(walletProvider?)
  • trySignSchnorrWithOPWallet: when an explicit Web3Provider is passed, it uses walletProvider.signSchnorr(messageHex) (part of the Web3Provider interface). The legacy auto-detect path continues to use OPWallet.signData(...), unchanged.

Not modified:

  • signMessageECDSAAuto / trySignECDSAWithOPWalletWeb3Provider has no ECDSA method, so these remain OP_WALLET-only. Can be extended later if Web3Provider ever adds an ECDSA method.
  • signConsolidatedInteraction — does not call any detect*OPWallet path; requires an explicit signer already.

Backward compatibility

  • All existing callers continue to work unchanged. The new parameter is optional everywhere.
  • When no walletProvider is passed, behavior is identical to today (auto-detect window.opnet.web3).
  • No changes to return types, error messages remain semantically equivalent (slight wording tweak: "OP_WALLET is not available" → "no browser wallet is available for signing" — happy to revert if preferred).
  • No changes to WindowWithWallets interface, the OPWallet type, or any exported public types.

Testing

  • npm run build — clean, no ESLint or TypeScript errors
  • npm run check:circular — no circular dependencies
  • npm test838 tests pass, 6 skipped, 0 failed (verified no regressions)

How this unblocks multi-wallet OPNet dApps

With this change + the companion PR btc-vision/walletconnect#23, a dApp can route signing through the currently-connected wallet regardless of which OPNet-compatible wallet the user selected:

// dApp code using @btc-vision/walletconnect (after #23 lands)
const { walletInstance } = useWalletConnect();
const web3 = (walletInstance as any)?.web3 as Web3Provider | undefined;
// The cast goes away once @btc-vision/walletconnect exposes `web3` typed
// on the hook return — happy to follow up with that small PR after this lands.

const factory = new TransactionFactory();
const result = await factory.signDeployment(
    { ...params, signer: null, mldsaSigner: null },
    web3,  // routes to whichever wallet the user connected (or undefined → today's auto-detect)
);

If the dApp doesn't care about multi-wallet support, omitting the second argument preserves today's OP_WALLET auto-detect behavior exactly.

Documentation

A handful of documentation/**/*.md files reference window.opnet.web3 as the sole signing path. Happy to follow up with a docs PR once this API shape is locked in — kept it out of this diff to keep the change surgical and easy to review.

…lets

Closes btc-vision#127.

Currently, TransactionFactory and MessageSigner auto-detect
window.opnet.web3 as the sole browser signing provider. This blocks any
third-party OPNet-compatible wallet (injected at other window globals
such as window.myscribe) from being used through the documented
`signer: null` browser flow — even when the caller has explicitly
connected a different wallet via @btc-vision/walletconnect.

This commit adds an optional `walletProvider?: Web3Provider` parameter
to the public signing entry points:

  TransactionFactory:
  - createCancellableTransaction(params, walletProvider?)
  - signInteraction(params, walletProvider?)
  - signDeployment(params, walletProvider?)
  - createBTCTransfer(params, walletProvider?)

  MessageSigner:
  - signMessageAuto(message, keypair?, walletProvider?)
  - tweakAndSignMessageAuto(message, keypair?, network?, walletProvider?)
  - signMLDSAMessageAuto(message, mldsaKeypair?, walletProvider?)
  - trySignSchnorrWithOPWallet(message, walletProvider?)
  - trySignMLDSAWithOPWallet(message, walletProvider?)
  - verifyMLDSAWithOPWallet(message, signature, walletProvider?)
  - getMLDSAPublicKeyFromOPWallet(walletProvider?)

Behavior:
- When `walletProvider` is provided, it is used directly instead of
  auto-detecting window.opnet.web3.
- When omitted, all methods fall through to the existing auto-detect
  path (via new TransactionFactory.getDefaultBrowserWallet() helper),
  preserving full backward compatibility.

TransactionFactory's four private detect*OPWallet helpers now route
through getDefaultBrowserWallet() for the auto-detect fallback,
eliminating the repeated window.opnet null checks.

For trySignSchnorrWithOPWallet, when an explicit Web3Provider is passed
it uses `walletProvider.signSchnorr(messageHex)` (part of the Web3Provider
interface). The legacy auto-detect path continues to use the OPWallet's
signData method, unchanged.

All 838 existing tests pass; no regressions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@bitbragi
Copy link
Copy Markdown
Author

End-to-end testing setup is ready if helpful for review.

The dApp-side change is one line — passing walletInstance.web3 (the connected wallet's Web3Provider reference, as returned by useWalletConnect()) as the second argument to factory.signDeployment():

const { walletInstance } = useWalletConnect();
const web3 = (walletInstance as any)?.web3 as Web3Provider | undefined;
const result = await factory.signDeployment(
    { ...params, signer: null, mldsaSigner: null },
    web3,  // routes to the connected wallet instead of window.opnet auto-detect
);

(The cast goes away once @btc-vision/walletconnect exposes web3 typed on the hook — happy to open that follow-up PR too.)

Happy to stand up a public demo on testnet.myscribe.org or answer any questions about the API shape. If you'd prefer a different approach (constructor injection, a registry pattern, something else), just say the word — the implementation is small enough that redesign is cheap.

bitbragi pushed a commit to MyScribe-Ecosystem/opnet-upstream-pr that referenced this pull request Apr 19, 2026
Closes the routing gap for third-party OPNet-compatible wallets when
invoking contract methods via the fluent API
(`contract.method(...).sendTransaction(...)`).

Currently, CallResult.signTransaction() calls
`factory.signInteraction(params)` without forwarding any wallet
provider. TransactionFactory then auto-detects window.opnet.web3,
which means all contract interactions route to OP_WALLET even when
the dApp has connected a different wallet via @btc-vision/walletconnect.

This change adds an optional `walletProvider?: Web3Provider` field to
the TransactionParameters interface and forwards it to
`factory.signInteraction()`. Behavior is unchanged when omitted
(current auto-detect path preserved).

Companion to btc-vision/transaction#128, which adds the corresponding
parameter to `TransactionFactory.signInteraction()`. This PR requires
that one to be merged first (or published as a prerelease) before it
can build against the published package.

## Usage

    const { walletInstance } = useWalletConnect();
    const web3 = (walletInstance as any)?.web3; // post-walletconnect hook typed exposure

    const result = contract.someMethod(args);
    await result.sendTransaction({
        ...existingParams,
        walletProvider: web3,  // routes to the connected wallet
    });

## Testing

- `npm run build` passes with no errors
- `npm test` — 300 existing tests pass. Two pre-existing failures in
  `test/utxos-manager.test.ts` (unrelated to this change — same
  failures exist on main).
- Manual end-to-end test: verified the combined fix (this PR +
  btc-vision/transaction#128 + btc-vision/walletconnect#23) correctly
  routes MyScribe Wallet contract interactions on testnet.myscribe.org.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@bitbragi
Copy link
Copy Markdown
Author

bitbragi commented Apr 20, 2026

End-to-end demo is live at https://testnet.myscribe.org with this PR + companion fixes (walletconnect #23, walletconnect #25, and the matching opnet #158 which closes the same gap in CallResult.signTransaction).

Re-verified end-to-end on rebuilt testnet (commit 5f6ae71, 2026-04-26) with OP_WALLET + MyScribe installed side-by-side:

MyScribe connected (mswallet_test_1):

OP_WALLET connected (opwallet_test_1):

No cross-wallet interception in either direction. All four txs signed via the correct wallet's extension popup.

@bitbragi bitbragi marked this pull request as ready for review April 25, 2026 21:05
@bitbragi bitbragi marked this pull request as draft April 26, 2026 02:02
@bitbragi bitbragi marked this pull request as ready for review April 26, 2026 20:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TransactionFactory.detect*OPWallet methods hardcode window.opnet, blocking third-party OPNet-compatible wallets

1 participant