@meshconnect/uwc-bitcoin 0.2.4-snapshot.ec6b95b → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,155 @@
1
+ import type {
2
+ BitcoinAccount,
3
+ BitcoinPsbtSignRequest,
4
+ BitcoinSigHash,
5
+ BitcoinSignMessageParams,
6
+ SignatureType
7
+ } from '@meshconnect/uwc-types'
8
+ import { WalletConnectorError } from '@meshconnect/uwc-types'
9
+ import type { BitcoinTransportAdapter } from '../adapter'
10
+ import {
11
+ base64ToBytes,
12
+ bytesToBase64,
13
+ bytesToHex,
14
+ hexToBytes
15
+ } from '../encoding'
16
+
17
+ /**
18
+ * The subset of OKX's injected Bitcoin provider this adapter drives
19
+ * (`window.okxwallet.bitcoin`). OKX does NOT expose Bitcoin through Wallet
20
+ * Standard — only this legacy injected provider — so it needs a dedicated
21
+ * adapter, not `WalletStandardBitcoinAdapter`.
22
+ *
23
+ * Wire conventions the provider follows:
24
+ * - `requestAccounts()` prompts + returns the authorized addresses; the caller
25
+ * pairs `getPublicKey()` for the active account's compressed key (hex).
26
+ * - `signMessage(msg, type)` returns a base64 signature.
27
+ * - `signPsbt(psbtHex, options)` takes AND returns PSBT as HEX — so this adapter
28
+ * converts base64 ↔ hex at the boundary (our domain is base64/BIP-174).
29
+ */
30
+ export interface OkxBitcoinProvider {
31
+ /** Prompt for connection; returns the authorized addresses. */
32
+ requestAccounts(): Promise<string[]>
33
+ /** Non-prompting: the already-authorized addresses ([] if not connected). */
34
+ getAccounts(): Promise<string[]>
35
+ /** The active account's compressed public key, hex-encoded. */
36
+ getPublicKey(): Promise<string>
37
+ /** Sign a message; returns a base64 signature. */
38
+ signMessage(
39
+ message: string,
40
+ type?: 'ecdsa' | 'bip322-simple'
41
+ ): Promise<string>
42
+ /**
43
+ * Sign a PSBT (hex in, signed PSBT hex out). `toSignInputs` scopes which
44
+ * inputs to sign (index + expected address, optional sighash), mirroring the
45
+ * Wallet Standard adapter's `inputsToSign`; omitted → the wallet signs all it
46
+ * can.
47
+ */
48
+ signPsbt(
49
+ psbtHex: string,
50
+ options?: {
51
+ autoFinalized?: boolean
52
+ toSignInputs?: {
53
+ index: number
54
+ address: string
55
+ sighashTypes?: number[]
56
+ }[]
57
+ }
58
+ ): Promise<string>
59
+ on(event: string, handler: (accounts: string[]) => void): void
60
+ removeListener(event: string, handler: (accounts: string[]) => void): void
61
+ }
62
+
63
+ /** Bitcoin SIGHASH flag → numeric value (Bitcoin Core encoding) for the
64
+ * provider's `toSignInputs.sighashTypes`. */
65
+ const SIGHASH_VALUES: Record<BitcoinSigHash, number> = {
66
+ ALL: 0x01,
67
+ NONE: 0x02,
68
+ SINGLE: 0x03,
69
+ 'ALL|ANYONECANPAY': 0x81,
70
+ 'NONE|ANYONECANPAY': 0x82,
71
+ 'SINGLE|ANYONECANPAY': 0x83
72
+ }
73
+
74
+ /**
75
+ * OKX (legacy injected) Bitcoin transport adapter — the non-Wallet-Standard
76
+ * injected path. The injected transport resolves `window.okxwallet.bitcoin` by
77
+ * its `injectedId` and hands it here; this adapter maps our domain shapes onto
78
+ * the provider's methods.
79
+ *
80
+ * `sendTransfer` is intentionally not implemented — that is the WalletConnect
81
+ * high-level path. (OKX has `sendBitcoin`, but broadcast is out of scope here:
82
+ * this stays sign-only, matching `WalletStandardBitcoinAdapter`.)
83
+ */
84
+ export class OkxBitcoinAdapter implements BitcoinTransportAdapter {
85
+ constructor(private readonly provider: OkxBitcoinProvider) {}
86
+
87
+ async getAccounts(): Promise<BitcoinAccount[]> {
88
+ const addresses = await this.provider.requestAccounts()
89
+ // The provider exposes one active public key; pair it with the first
90
+ // (active) address. Tolerate a missing key rather than failing the connect.
91
+ let publicKey = ''
92
+ try {
93
+ publicKey = await this.provider.getPublicKey()
94
+ } catch {
95
+ publicKey = ''
96
+ }
97
+ return addresses.map((address, index) => ({
98
+ address,
99
+ publicKey: index === 0 ? publicKey : ''
100
+ }))
101
+ }
102
+
103
+ async signMessage(params: BitcoinSignMessageParams): Promise<SignatureType> {
104
+ // OKX signs with its OWN active account (no address param), so the caller's
105
+ // `params.account` is advisory here. Map our protocol name to the
106
+ // provider's ('bip322' → 'bip322-simple').
107
+ const type = params.protocol === 'bip322' ? 'bip322-simple' : 'ecdsa'
108
+ const signature = await this.provider.signMessage(params.message, type)
109
+ if (!signature) {
110
+ throw new WalletConnectorError({
111
+ type: 'unknown',
112
+ message: 'okx bitcoin: signMessage returned no signature'
113
+ })
114
+ }
115
+ // OKX returns a base64 signature — matches our contract.
116
+ return { type: 'standard', signature }
117
+ }
118
+
119
+ async signPsbt(request: BitcoinPsbtSignRequest): Promise<string> {
120
+ // Our domain is base64 (BIP-174); OKX speaks hex. autoFinalized:false keeps
121
+ // this sign-only (the wallet returns the signed-but-unfinalized PSBT).
122
+ const psbtHex = bytesToHex(base64ToBytes(request.psbt))
123
+ // Forward which inputs to sign (index + address, optional sighash) so the
124
+ // wallet signs exactly the requested set — matching the Wallet Standard
125
+ // adapter's `inputsToSign`. `request.from` isn't forwarded: OKX signs with
126
+ // its single active account, so there's no per-`from` routing. Omitted →
127
+ // OKX signs all inputs it can.
128
+ const toSignInputs = request.inputsToSign?.flatMap(input =>
129
+ input.signingIndexes.map(index => ({
130
+ index,
131
+ address: input.address,
132
+ ...(input.sigHash
133
+ ? { sighashTypes: [SIGHASH_VALUES[input.sigHash]] }
134
+ : {})
135
+ }))
136
+ )
137
+ const signedHex = await this.provider.signPsbt(psbtHex, {
138
+ autoFinalized: false,
139
+ ...(toSignInputs ? { toSignInputs } : {})
140
+ })
141
+ if (!signedHex) {
142
+ throw new WalletConnectorError({
143
+ type: 'unknown',
144
+ message: 'okx bitcoin: signPsbt returned no PSBT'
145
+ })
146
+ }
147
+ return bytesToBase64(hexToBytes(signedHex))
148
+ }
149
+
150
+ disconnect(): Promise<void> {
151
+ // OKX's injected provider has no disconnect RPC; clearing session state is
152
+ // the caller's job.
153
+ return Promise.resolve()
154
+ }
155
+ }
@@ -77,7 +77,23 @@ export interface WalletStandardBitcoinWallet {
77
77
  export class WalletStandardBitcoinAdapter implements BitcoinTransportAdapter {
78
78
  private accounts: readonly WalletStandardBitcoinAccount[] | null = null
79
79
 
80
- constructor(private readonly wallet: WalletStandardBitcoinWallet) {}
80
+ /**
81
+ * @param wallet The discovered `bitcoin:*` wallet.
82
+ * @param initialAccounts Optional already-authorized accounts to seed the
83
+ * cache with. Passed on the SILENT restore path so the first
84
+ * `signMessage`/`signPsbt` doesn't re-invoke `bitcoin:connect` (which some
85
+ * wallets surface as UI) via `requireAccount`→`getAccounts`. Omit on a
86
+ * fresh connect, where `getAccounts()` should call `bitcoin:connect` to
87
+ * authorize.
88
+ */
89
+ constructor(
90
+ private readonly wallet: WalletStandardBitcoinWallet,
91
+ initialAccounts?: readonly WalletStandardBitcoinAccount[]
92
+ ) {
93
+ if (initialAccounts && initialAccounts.length > 0) {
94
+ this.accounts = initialAccounts
95
+ }
96
+ }
81
97
 
82
98
  async getAccounts(): Promise<BitcoinAccount[]> {
83
99
  const connect = this.wallet.features['bitcoin:connect']
@@ -153,7 +169,13 @@ export class WalletStandardBitcoinAdapter implements BitcoinTransportAdapter {
153
169
  address: string
154
170
  ): Promise<WalletStandardBitcoinAccount> {
155
171
  if (!this.accounts) await this.getAccounts()
156
- const account = this.accounts?.find(a => a.address === address)
172
+ const account =
173
+ this.accounts?.find(a => a.address === address) ??
174
+ // The cached set is from an earlier authorize; the wallet may have since
175
+ // switched accounts (its `accounts` update in place). Re-check the
176
+ // wallet's CURRENT accounts before failing — so signing the live account
177
+ // after a switch resolves without re-invoking `bitcoin:connect`.
178
+ this.wallet.accounts?.find(a => a.address === address)
157
179
  if (!account) {
158
180
  throw new WalletConnectorError({
159
181
  type: 'unknown',
@@ -33,6 +33,28 @@ const ethereumNetwork: Network = {
33
33
  networkType: 'evm'
34
34
  }
35
35
 
36
+ /** Same bip122 namespace as Bitcoin, but a different chain id. */
37
+ const dogeNetwork: Network = {
38
+ internalId: 3,
39
+ id: 'bip122:1a91e3dace36e2be3bf030a65679fe82',
40
+ namespace: 'bip122',
41
+ name: 'Dogecoin',
42
+ logoUrl: '',
43
+ nativeCurrency: { name: 'Dogecoin', symbol: 'DOGE', decimals: 8 },
44
+ networkType: 'dogecoin'
45
+ }
46
+
47
+ /** Same bip122 namespace as Bitcoin, but a different chain id. */
48
+ const litecoinNetwork: Network = {
49
+ internalId: 2,
50
+ id: 'bip122:12a765e31ffd4059bada1e25190f6e98',
51
+ namespace: 'bip122',
52
+ name: 'Litecoin',
53
+ logoUrl: '',
54
+ nativeCurrency: { name: 'Litecoin', symbol: 'LTC', decimals: 8 },
55
+ networkType: 'litecoin'
56
+ }
57
+
36
58
  const account: BitcoinAccount = {
37
59
  address: 'bc1qexampleaddress',
38
60
  publicKey: '02aabbcc',
@@ -66,6 +88,32 @@ describe('assertBitcoinNetwork', () => {
66
88
  ethereumNetwork.id
67
89
  )
68
90
  })
91
+
92
+ it('throws a distinct error for a bip122 network that is not Bitcoin mainnet (Dogecoin)', () => {
93
+ expect(() => assertBitcoinNetwork(dogeNetwork, 'test')).toThrow(
94
+ WalletConnectorError
95
+ )
96
+ expect(() => assertBitcoinNetwork(dogeNetwork, 'test')).toThrow(
97
+ dogeNetwork.id
98
+ )
99
+ // Distinct from the namespace-mismatch message — callers must be able to
100
+ // tell "wrong namespace" apart from "right namespace, wrong chain".
101
+ expect(() => assertBitcoinNetwork(dogeNetwork, 'test')).toThrow(
102
+ /unsupported bip122 chain/
103
+ )
104
+ expect(() => assertBitcoinNetwork(ethereumNetwork, 'test')).not.toThrow(
105
+ /unsupported bip122 chain/
106
+ )
107
+ })
108
+
109
+ it('throws a distinct error for a bip122 network that is not Bitcoin mainnet (Litecoin)', () => {
110
+ expect(() => assertBitcoinNetwork(litecoinNetwork, 'test')).toThrow(
111
+ WalletConnectorError
112
+ )
113
+ expect(() => assertBitcoinNetwork(litecoinNetwork, 'test')).toThrow(
114
+ /unsupported bip122 chain/
115
+ )
116
+ })
69
117
  })
70
118
 
71
119
  describe('BitcoinNamespace', () => {
@@ -90,6 +138,17 @@ describe('BitcoinNamespace', () => {
90
138
  ).toThrow(WalletConnectorError)
91
139
  })
92
140
 
141
+ it('assertSupportedNetwork rejects other bip122 chains (Dogecoin, Litecoin)', () => {
142
+ expect(() =>
143
+ new BitcoinNamespace(fakeAdapter()).assertSupportedNetwork(dogeNetwork)
144
+ ).toThrow(WalletConnectorError)
145
+ expect(() =>
146
+ new BitcoinNamespace(fakeAdapter()).assertSupportedNetwork(
147
+ litecoinNetwork
148
+ )
149
+ ).toThrow(WalletConnectorError)
150
+ })
151
+
93
152
  it('delegates getAccounts to the adapter', async () => {
94
153
  const adapter = fakeAdapter()
95
154
  const result = await new BitcoinNamespace(adapter).getAccounts()
@@ -8,6 +8,7 @@ import type {
8
8
  SignatureType,
9
9
  TransactionResult
10
10
  } from '@meshconnect/uwc-types'
11
+ import { bitcoinNetwork } from '@meshconnect/uwc-constants'
11
12
  import { WalletConnectorError } from '@meshconnect/uwc-types'
12
13
  import type { BitcoinTransportAdapter } from './adapter'
13
14
 
@@ -15,8 +16,13 @@ import type { BitcoinTransportAdapter } from './adapter'
15
16
  export const BITCOIN_NAMESPACE: Namespace = 'bip122'
16
17
 
17
18
  /**
18
- * Assert `network` is a bip122 network, else throw. Shared by the namespace
19
- * module and its transport adapters so the boundary check lives in one place.
19
+ * Assert `network` is the bip122 network this module actually supports
20
+ * (Bitcoin mainnet), else throw. Other bip122 chains (Dogecoin, Litecoin,
21
+ * etc.) share the namespace but differ in chain id — accepting them here
22
+ * would let a Bitcoin-only wallet silently return a Bitcoin address stamped
23
+ * with the wrong network id, so they must be rejected explicitly rather than
24
+ * only checking the namespace. Shared by the namespace module and its
25
+ * transport adapters so the boundary check lives in one place.
20
26
  */
21
27
  export function assertBitcoinNetwork(network: Network, method: string): void {
22
28
  if (network.namespace !== BITCOIN_NAMESPACE) {
@@ -25,6 +31,12 @@ export function assertBitcoinNetwork(network: Network, method: string): void {
25
31
  message: `${method}: expected a bip122 network but got "${network.id}"`
26
32
  })
27
33
  }
34
+ if (network.id !== bitcoinNetwork.id) {
35
+ throw new WalletConnectorError({
36
+ type: 'unknown',
37
+ message: `${method}: unsupported bip122 chain "${network.id}" — only Bitcoin mainnet ("${bitcoinNetwork.id}") is supported`
38
+ })
39
+ }
28
40
  }
29
41
 
30
42
  /**
package/src/encoding.ts CHANGED
@@ -27,3 +27,21 @@ export function base64ToBytes(base64: string): Uint8Array {
27
27
  for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)
28
28
  return bytes
29
29
  }
30
+
31
+ export function hexToBytes(hex: string): Uint8Array {
32
+ const clean = /^0x/i.test(hex) ? hex.slice(2) : hex
33
+ if (clean.length % 2 !== 0) {
34
+ throw new Error('hexToBytes: odd-length hex string')
35
+ }
36
+ // Validate before parsing: `parseInt` on a non-hex pair yields NaN, which a
37
+ // Uint8Array silently coerces to 0 — so bad input would corrupt data (a PSBT
38
+ // or public key) instead of failing fast. Reject it.
39
+ if (!/^[0-9a-fA-F]*$/.test(clean)) {
40
+ throw new Error('hexToBytes: non-hex characters')
41
+ }
42
+ const bytes = new Uint8Array(clean.length / 2)
43
+ for (let i = 0; i < bytes.length; i++) {
44
+ bytes[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16)
45
+ }
46
+ return bytes
47
+ }
package/src/index.ts CHANGED
@@ -9,5 +9,6 @@ export type {
9
9
  WalletStandardBitcoinWallet,
10
10
  WalletStandardBitcoinAccount
11
11
  } from './adapters/wallet-standard'
12
- export { WalletConnectBitcoinAdapter } from './adapters/walletconnect'
13
- export type { BitcoinWalletConnectRequest } from './adapters/walletconnect'
12
+ export { OkxBitcoinAdapter } from './adapters/okx'
13
+ export type { OkxBitcoinProvider } from './adapters/okx'
14
+ export { bytesToHex, hexToBytes } from './encoding'
@@ -1,36 +0,0 @@
1
- import type { BitcoinAccount, BitcoinPsbtSignRequest, BitcoinSendTransferRequest, BitcoinSignMessageParams, SignatureType, TransactionResult } from '@meshconnect/uwc-types';
2
- import type { BitcoinTransportAdapter } from '../adapter';
3
- /**
4
- * Issues a WalletConnect JSON-RPC request for a bip122 method. The WalletConnect
5
- * transport binds this to its live `SignClient` + session topic + chainId, e.g.
6
- * `(method, params) => signClient.request({ topic, chainId, request: { method, params } })`.
7
- * Kept as a plain function so `uwc-bitcoin` needs no `@walletconnect/*` dependency.
8
- */
9
- export type BitcoinWalletConnectRequest = (method: string, params: Record<string, unknown>) => Promise<unknown>;
10
- /**
11
- * WalletConnect (bip122) transport adapter — the Path A / mobile path (Trust
12
- * Wallet, …). The WalletConnect transport pairs the session and constructs this
13
- * adapter bound to the connected account's first external address plus a
14
- * `request` function; this adapter maps our domain shapes onto the Reown/CAIP-2
15
- * bip122 JSON-RPC methods (`getAccountAddresses`, `signMessage`, `signPsbt`,
16
- * `sendTransfer`).
17
- *
18
- * Wire format follows the Reown Bitcoin RPC reference (what wallets implement
19
- * over WalletConnect) — all-JSON strings, `account` on every request. NOTE:
20
- * untested end-to-end; grounded on the spec, not a live wallet.
21
- */
22
- export declare class WalletConnectBitcoinAdapter implements BitcoinTransportAdapter {
23
- private readonly account;
24
- private readonly request;
25
- /**
26
- * @param account The connected account's first external address — the
27
- * `account` parameter every bip122 method requires.
28
- * @param request Issues the bound WalletConnect JSON-RPC request.
29
- */
30
- constructor(account: string, request: BitcoinWalletConnectRequest);
31
- getAccounts(): Promise<BitcoinAccount[]>;
32
- signMessage(params: BitcoinSignMessageParams): Promise<SignatureType>;
33
- signPsbt(request: BitcoinPsbtSignRequest): Promise<string>;
34
- sendTransfer(request: BitcoinSendTransferRequest): Promise<TransactionResult>;
35
- }
36
- //# sourceMappingURL=walletconnect.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"walletconnect.d.ts","sourceRoot":"","sources":["../../src/adapters/walletconnect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,sBAAsB,EACtB,0BAA0B,EAE1B,wBAAwB,EACxB,aAAa,EACb,iBAAiB,EAClB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAEzD;;;;;GAKG;AACH,MAAM,MAAM,2BAA2B,GAAG,CACxC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,OAAO,CAAC,CAAA;AAmBrB;;;;;;;;;;;GAWG;AACH,qBAAa,2BAA4B,YAAW,uBAAuB;IAOvE,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAP1B;;;;OAIG;gBAEgB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,2BAA2B;IAGjD,WAAW,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAgBxC,WAAW,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,aAAa,CAAC;IAUrE,QAAQ,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,MAAM,CAAC;IA0B1D,YAAY,CAChB,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,iBAAiB,CAAC;CAQ9B"}
@@ -1,90 +0,0 @@
1
- import { WalletConnectorError } from '@meshconnect/uwc-types';
2
- /** Maps a SIGHASH flag to its numeric value (Bitcoin Core encoding). */
3
- const SIGHASH_VALUES = {
4
- ALL: 0x01,
5
- NONE: 0x02,
6
- SINGLE: 0x03,
7
- 'ALL|ANYONECANPAY': 0x81,
8
- 'NONE|ANYONECANPAY': 0x82,
9
- 'SINGLE|ANYONECANPAY': 0x83
10
- };
11
- /**
12
- * WalletConnect (bip122) transport adapter — the Path A / mobile path (Trust
13
- * Wallet, …). The WalletConnect transport pairs the session and constructs this
14
- * adapter bound to the connected account's first external address plus a
15
- * `request` function; this adapter maps our domain shapes onto the Reown/CAIP-2
16
- * bip122 JSON-RPC methods (`getAccountAddresses`, `signMessage`, `signPsbt`,
17
- * `sendTransfer`).
18
- *
19
- * Wire format follows the Reown Bitcoin RPC reference (what wallets implement
20
- * over WalletConnect) — all-JSON strings, `account` on every request. NOTE:
21
- * untested end-to-end; grounded on the spec, not a live wallet.
22
- */
23
- export class WalletConnectBitcoinAdapter {
24
- account;
25
- request;
26
- /**
27
- * @param account The connected account's first external address — the
28
- * `account` parameter every bip122 method requires.
29
- * @param request Issues the bound WalletConnect JSON-RPC request.
30
- */
31
- constructor(account, request) {
32
- this.account = account;
33
- this.request = request;
34
- }
35
- async getAccounts() {
36
- const result = (await this.request('getAccountAddresses', {
37
- account: this.account
38
- }));
39
- return result.map(entry => ({
40
- address: entry.address,
41
- // WalletConnect wallets MAY omit the public key; empty when absent.
42
- publicKey: entry.publicKey ?? '',
43
- ...(entry.intention === 'ordinal'
44
- ? { purpose: 'ordinals' }
45
- : entry.intention === 'payment'
46
- ? { purpose: 'payment' }
47
- : {})
48
- }));
49
- }
50
- async signMessage(params) {
51
- const result = (await this.request('signMessage', {
52
- account: this.account,
53
- message: params.message,
54
- address: params.account.address,
55
- ...(params.protocol ? { protocol: params.protocol } : {})
56
- }));
57
- return { type: 'standard', signature: result.signature };
58
- }
59
- async signPsbt(request) {
60
- if (!request.inputsToSign?.length) {
61
- throw new WalletConnectorError({
62
- type: 'unknown',
63
- message: 'walletconnect bitcoin: signPsbt requires inputsToSign (the WC signPsbt method has no "sign all" mode)'
64
- });
65
- }
66
- const signInputs = request.inputsToSign.flatMap(input => input.signingIndexes.map(index => ({
67
- address: input.address,
68
- index,
69
- ...(input.sigHash
70
- ? { sighashTypes: [SIGHASH_VALUES[input.sigHash]] }
71
- : {})
72
- })));
73
- const result = (await this.request('signPsbt', {
74
- account: this.account,
75
- psbt: request.psbt,
76
- signInputs,
77
- broadcast: false
78
- }));
79
- return result.psbt;
80
- }
81
- async sendTransfer(request) {
82
- const result = (await this.request('sendTransfer', {
83
- account: this.account,
84
- recipientAddress: request.to,
85
- amount: request.amount.toString()
86
- }));
87
- return result.txid;
88
- }
89
- }
90
- //# sourceMappingURL=walletconnect.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"walletconnect.js","sourceRoot":"","sources":["../../src/adapters/walletconnect.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAc7D,wEAAwE;AACxE,MAAM,cAAc,GAAmC;IACrD,GAAG,EAAE,IAAI;IACT,IAAI,EAAE,IAAI;IACV,MAAM,EAAE,IAAI;IACZ,kBAAkB,EAAE,IAAI;IACxB,mBAAmB,EAAE,IAAI;IACzB,qBAAqB,EAAE,IAAI;CAC5B,CAAA;AASD;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,2BAA2B;IAOnB;IACA;IAPnB;;;;OAIG;IACH,YACmB,OAAe,EACf,OAAoC;QADpC,YAAO,GAAP,OAAO,CAAQ;QACf,YAAO,GAAP,OAAO,CAA6B;IACpD,CAAC;IAEJ,KAAK,CAAC,WAAW;QACf,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE;YACxD,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC,CAAuB,CAAA;QACzB,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,oEAAoE;YACpE,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,EAAE;YAChC,GAAG,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS;gBAC/B,CAAC,CAAC,EAAE,OAAO,EAAE,UAAmB,EAAE;gBAClC,CAAC,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS;oBAC7B,CAAC,CAAC,EAAE,OAAO,EAAE,SAAkB,EAAE;oBACjC,CAAC,CAAC,EAAE,CAAC;SACV,CAAC,CAAC,CAAA;IACL,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,MAAgC;QAChD,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE;YAChD,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO;YAC/B,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1D,CAAC,CAA0B,CAAA;QAC5B,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAA;IAC1D,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,OAA+B;QAC5C,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;YAClC,MAAM,IAAI,oBAAoB,CAAC;gBAC7B,IAAI,EAAE,SAAS;gBACf,OAAO,EACL,uGAAuG;aAC1G,CAAC,CAAA;QACJ,CAAC;QACD,MAAM,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CACtD,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACjC,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,KAAK;YACL,GAAG,CAAC,KAAK,CAAC,OAAO;gBACf,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE;gBACnD,CAAC,CAAC,EAAE,CAAC;SACR,CAAC,CAAC,CACJ,CAAA;QACD,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE;YAC7C,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,UAAU;YACV,SAAS,EAAE,KAAK;SACjB,CAAC,CAAqB,CAAA;QACvB,OAAO,MAAM,CAAC,IAAI,CAAA;IACpB,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,OAAmC;QAEnC,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE;YACjD,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,gBAAgB,EAAE,OAAO,CAAC,EAAE;YAC5B,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE;SAClC,CAAC,CAAqB,CAAA;QACvB,OAAO,MAAM,CAAC,IAAI,CAAA;IACpB,CAAC;CACF"}
@@ -1,97 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest'
2
- import { WalletConnectorError } from '@meshconnect/uwc-types'
3
- import { WalletConnectBitcoinAdapter } from './walletconnect'
4
-
5
- const ACCOUNT = 'bc1qsender'
6
-
7
- describe('WalletConnectBitcoinAdapter', () => {
8
- it('getAccounts calls getAccountAddresses and maps intention -> purpose', async () => {
9
- const request = vi.fn().mockResolvedValue([
10
- { address: 'bc1qpay', publicKey: '02aa', intention: 'payment' },
11
- { address: 'bc1qord', intention: 'ordinal' }
12
- ])
13
- const adapter = new WalletConnectBitcoinAdapter(ACCOUNT, request)
14
-
15
- const accounts = await adapter.getAccounts()
16
-
17
- expect(request).toHaveBeenCalledWith('getAccountAddresses', {
18
- account: ACCOUNT
19
- })
20
- expect(accounts).toEqual([
21
- { address: 'bc1qpay', publicKey: '02aa', purpose: 'payment' },
22
- { address: 'bc1qord', publicKey: '', purpose: 'ordinals' }
23
- ])
24
- })
25
-
26
- it('signMessage passes account + address + raw message and wraps the signature', async () => {
27
- const request = vi.fn().mockResolvedValue({ signature: 'sigbase64' })
28
- const adapter = new WalletConnectBitcoinAdapter(ACCOUNT, request)
29
-
30
- const sig = await adapter.signMessage({
31
- account: { address: 'bc1qpay', publicKey: '02aa' },
32
- message: 'verify me',
33
- protocol: 'bip322'
34
- })
35
-
36
- expect(request).toHaveBeenCalledWith('signMessage', {
37
- account: ACCOUNT,
38
- message: 'verify me',
39
- address: 'bc1qpay',
40
- protocol: 'bip322'
41
- })
42
- expect(sig).toEqual({ type: 'standard', signature: 'sigbase64' })
43
- })
44
-
45
- it('signPsbt flattens inputsToSign per index and maps SIGHASH to numbers', async () => {
46
- const request = vi.fn().mockResolvedValue({ psbt: 'signedpsbt' })
47
- const adapter = new WalletConnectBitcoinAdapter(ACCOUNT, request)
48
-
49
- const signed = await adapter.signPsbt({
50
- from: 'bc1qpay',
51
- psbt: 'cHNidP8=',
52
- inputsToSign: [
53
- {
54
- address: 'bc1qpay',
55
- signingIndexes: [0, 2],
56
- sigHash: 'ALL|ANYONECANPAY'
57
- }
58
- ]
59
- })
60
-
61
- expect(request).toHaveBeenCalledWith('signPsbt', {
62
- account: ACCOUNT,
63
- psbt: 'cHNidP8=',
64
- signInputs: [
65
- { address: 'bc1qpay', index: 0, sighashTypes: [0x81] },
66
- { address: 'bc1qpay', index: 2, sighashTypes: [0x81] }
67
- ],
68
- broadcast: false
69
- })
70
- expect(signed).toBe('signedpsbt')
71
- })
72
-
73
- it('signPsbt rejects when inputsToSign is missing (WC has no sign-all mode)', async () => {
74
- const adapter = new WalletConnectBitcoinAdapter(ACCOUNT, vi.fn())
75
- await expect(
76
- adapter.signPsbt({ from: 'bc1qpay', psbt: 'cHNidP8=' })
77
- ).rejects.toBeInstanceOf(WalletConnectorError)
78
- })
79
-
80
- it('sendTransfer maps to recipientAddress + amount string and returns the txid', async () => {
81
- const request = vi.fn().mockResolvedValue({ txid: 'deadbeef' })
82
- const adapter = new WalletConnectBitcoinAdapter(ACCOUNT, request)
83
-
84
- const txid = await adapter.sendTransfer({
85
- from: 'bc1qpay',
86
- to: 'bc1qdest',
87
- amount: 100000n
88
- })
89
-
90
- expect(request).toHaveBeenCalledWith('sendTransfer', {
91
- account: ACCOUNT,
92
- recipientAddress: 'bc1qdest',
93
- amount: '100000'
94
- })
95
- expect(txid).toBe('deadbeef')
96
- })
97
- })