sats-connect 4.2.1 → 4.3.0-1764f8f

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.
package/README.md CHANGED
@@ -12,70 +12,101 @@ It’s already powering apps across the BitcoinFi ecosystem, with close to **2 m
12
12
 
13
13
  Developers building on Bitcoin and its L2s can use Sats Connect to interact directly with users’ wallets:
14
14
 
15
- * 🔑 **Retrieve wallet addresses** for Bitcoin, Spark, Starknet, Stacks, and more
16
- * ✍️ **Request signatures** of messages for authentication or verification
17
- * 🔗 **Sign & send transactions**:
18
- * Bitcoin PSBTs
19
- * Spark BTC & token transfers
20
- * Starknet & Stacks transfers & contract calls
21
- * 🖼️🔲🪙 **Ordinals, Runes & BRC-20**:
22
- * Track balances across assets
23
- * Transfer tokens and inscriptions
24
- * Inscribe sats with arbitrary content, create and mint tokens
15
+ - 🔑 **Retrieve wallet addresses** for Bitcoin, Spark, Starknet, Stacks, and more
16
+ - ✍️ **Request signatures** of messages for authentication or verification
17
+ - 🔗 **Sign & send transactions**:
18
+ - Bitcoin PSBTs
19
+ - Spark BTC & token transfers
20
+ - Starknet & Stacks transfers & contract calls
21
+ - 🖼️🔲🪙 **Ordinals, Runes & BRC-20**:
22
+ - Track balances across assets
23
+ - Transfer tokens and inscriptions
24
+ - Inscribe sats with arbitrary content, create and mint tokens
25
25
 
26
26
  ## 🛠️ Why Sats Connect?
27
27
 
28
- * **Wallet-first** – every interaction happens directly in the user’s wallet, with explicit approval
29
- * **Bitcoin-native & multi-asset** – supports Bitcoin L1 (BTC, Ordinals, Runes, BRC-20) as well as emerging L2s like Spark, Starknet, and Stacks
30
- * **Comprehensive toolkit** – from simple address retrieval to advanced features like signing PSBTs, transferring Spark tokens, or inscribing Ordinals
31
- * **Battle-tested** – trusted by leading BitcoinFi apps, with close to **2M downloads** and a growing developer ecosystem
28
+ - **Wallet-first** – every interaction happens directly in the user’s wallet, with explicit approval
29
+ - **Bitcoin-native & multi-asset** – supports Bitcoin L1 (BTC, Ordinals, Runes, BRC-20) as well as emerging L2s like Spark, Starknet, and Stacks
30
+ - **Comprehensive toolkit** – from simple address retrieval to advanced features like signing PSBTs, transferring Spark tokens, or inscribing Ordinals
31
+ - **Battle-tested** – trusted by leading BitcoinFi apps, with close to **2M downloads** and a growing developer ecosystem
32
32
 
33
33
  ## 🚀 Get started
34
+
34
35
  ```bash
35
36
  npm i sats-connect@4.2.x
36
37
  ```
38
+
37
39
  Then request a [wallet connection ](https://docs.xverse.app/sats-connect/connecting-to-the-wallet/connect-to-xverse-wallet)— and explore the docs to unlock the full Bitcoin stack: Bitcoin L1, Spark, Starknet, Stacks, and the assets built on top.
38
40
 
39
41
  ## 💻 Usage
42
+
40
43
  ### import
44
+
41
45
  ```ts
42
46
  import { request } from 'sats-connect';
43
47
  ```
48
+
44
49
  ### Connect Wallet
50
+
45
51
  ```ts
46
52
  const response = await request('getAccounts', {
47
53
  purposes: [AddressPurpose.Payment, AddressPurpose.Ordinals, AddressPurpose.Stacks],
48
54
  message: 'Cool app wants to know your addresses!',
49
55
  });
50
56
  ```
57
+
51
58
  ### Request a wallet action
59
+
52
60
  ```ts
53
61
  await request('sendTransfer', {...});
54
62
  ```
55
63
 
56
64
  ### Disconnect Wallet
65
+
57
66
  ```ts
58
67
  await Wallet.disconnect();
59
68
  ```
60
69
 
70
+ ### Build a PSBT input from address metadata
71
+
72
+ Wallets can return the scripts needed to construct an input. Decode them before passing the input to `@scure/btc-signer`:
73
+
74
+ ```ts
75
+ const decoded = decodeAddressPsbtData(address);
76
+
77
+ tx.addInput({
78
+ txid,
79
+ index,
80
+ witnessUtxo: { script: decoded.scriptPubKey, amount },
81
+ ...decoded.unlockDefinition,
82
+ });
83
+ ```
84
+
61
85
  ## 💻 Development
86
+
62
87
  ### Build the package
88
+
63
89
  ```bash
64
90
  npm run build
65
91
  ```
92
+
66
93
  ### Run example app
94
+
67
95
  ```bash
68
96
  npm run dev:example
69
97
  ```
70
98
 
71
99
  ## 📚 Documentation
100
+
72
101
  For full documentation, visit [docs.xverse.app](https://docs.xverse.app/sats-connect/).
73
102
 
74
103
  ## ⚡ Power your Bitcoin apps with plug-and-play infra
104
+
75
105
  Sats Connect lets your app connect directly to users’ wallets.\
76
106
  The [Xverse API](https://docs.xverse.app/api) gives you **plug-and-play access to Bitcoin data and infrastructure** — Ordinals, Runes, mempool, balances, transactions, collections, and more — without running your own nodes or indexers.
77
107
 
78
108
  Together, they remove the heavy lifting so you can focus on building the next generation of Bitcoin apps.
79
109
 
80
110
  ## 🤝 Join the community
111
+
81
112
  If you’re building with Sats Connect or Xverse API and need help with integration, join our [developer forum](https://discord.gg/tN84HhSDrz) to connect with the community and the Xverse team. 
package/dist/index.d.mts CHANGED
@@ -1,6 +1,25 @@
1
- import { SupportedWallet, Requests, Params, RpcResult, AddListener } from '@sats-connect/core';
1
+ import { Address, SupportedWallet, Method, RpcRequestParams, RequestReturn, AddListener } from '@sats-connect/core';
2
2
  export * from '@sats-connect/core';
3
3
  import { Config } from '@sats-connect/ui';
4
+ import { TaprootControlBlock } from '@scure/btc-signer';
5
+
6
+ type TaprootControlBlockValue = ReturnType<typeof TaprootControlBlock.decode>;
7
+ type DecodedAddressPsbtData = {
8
+ scriptPubKey: Uint8Array;
9
+ unlockDefinition: Record<string, never> | {
10
+ redeemScript: Uint8Array;
11
+ } | {
12
+ tapInternalKey: Uint8Array;
13
+ } | {
14
+ witnessScript: Uint8Array;
15
+ } | {
16
+ tapLeafScript: Array<[TaprootControlBlockValue, Uint8Array]>;
17
+ };
18
+ };
19
+ declare class DecodeAddressPsbtDataError extends Error {
20
+ readonly name = "DecodeAddressPsbtDataError";
21
+ }
22
+ declare const decodeAddressPsbtData: (address: Pick<Address, "scriptPubKey" | "unlockDefinition">) => DecodedAddressPsbtData;
4
23
 
5
24
  declare class Wallet {
6
25
  private providerId;
@@ -10,10 +29,10 @@ declare class Wallet {
10
29
  setCreateCustomConfig(createCustomConfig: (providers: SupportedWallet[]) => Config): void;
11
30
  selectProvider(): Promise<void>;
12
31
  disconnect(): Promise<void>;
13
- request<Method extends keyof Requests>(method: Method, params: Params<Method>): Promise<RpcResult<Method>>;
32
+ request<M extends Method>(method: M, params: RpcRequestParams<M>): Promise<RequestReturn<M>>;
14
33
  addListener: AddListener;
15
34
  }
16
35
 
17
36
  declare const _default: Wallet;
18
37
 
19
- export { _default as default };
38
+ export { DecodeAddressPsbtDataError, type DecodedAddressPsbtData, decodeAddressPsbtData, _default as default };
package/dist/index.mjs CHANGED
@@ -17,6 +17,113 @@ import {
17
17
  walletOpen
18
18
  } from "@sats-connect/ui";
19
19
  export * from "@sats-connect/core";
20
+
21
+ // src/decodeAddressPsbtData.ts
22
+ import { hex } from "@scure/base";
23
+ import { TaprootControlBlock } from "@scure/btc-signer";
24
+ var DecodeAddressPsbtDataError = class extends Error {
25
+ name = "DecodeAddressPsbtDataError";
26
+ };
27
+ var malformed = (field, cause) => new DecodeAddressPsbtDataError(`${field} is malformed`, { cause });
28
+ var decodeHex = (value, field) => {
29
+ if (typeof value !== "string" || !/^(?:[0-9a-f]{2})+$/.test(value)) {
30
+ throw malformed(field);
31
+ }
32
+ try {
33
+ return hex.decode(value);
34
+ } catch (error) {
35
+ throw malformed(field, error);
36
+ }
37
+ };
38
+ var parseUnlockDefinition = (value) => {
39
+ if (!value || typeof value !== "object" || !("type" in value)) {
40
+ throw malformed("unlockDefinition");
41
+ }
42
+ const definition = value;
43
+ switch (definition.type) {
44
+ case "p2wpkh":
45
+ return { type: "p2wpkh" };
46
+ case "p2sh-p2wpkh":
47
+ decodeHex(definition.redeemScript, "redeemScript");
48
+ return definition;
49
+ case "p2tr-key-path":
50
+ if (decodeHex(definition.tapInternalKey, "tapInternalKey").length !== 32) {
51
+ throw malformed("tapInternalKey");
52
+ }
53
+ return definition;
54
+ case "p2wsh":
55
+ decodeHex(definition.witnessScript, "witnessScript");
56
+ return definition;
57
+ case "p2tr-script-path": {
58
+ if (!Array.isArray(definition.tapLeafScript) || definition.tapLeafScript.length === 0) {
59
+ throw malformed("tapLeafScript");
60
+ }
61
+ for (const leaf of definition.tapLeafScript) {
62
+ if (!leaf || typeof leaf !== "object") throw malformed("tapLeafScript");
63
+ const { controlBlock, script } = leaf;
64
+ const controlBlockBytes = decodeHex(controlBlock, "controlBlock");
65
+ const scriptBytes = decodeHex(script, "script");
66
+ if (controlBlockBytes.length < 33 || controlBlockBytes.length > 33 + 32 * 128 || (controlBlockBytes.length - 33) % 32 !== 0) {
67
+ throw malformed("controlBlock");
68
+ }
69
+ if (scriptBytes.at(-1) !== (controlBlockBytes[0] & 254)) {
70
+ throw malformed("tapLeafScript");
71
+ }
72
+ }
73
+ return definition;
74
+ }
75
+ default:
76
+ throw malformed("unlockDefinition");
77
+ }
78
+ };
79
+ var decodeAddressPsbtData = (address) => {
80
+ if (!address || typeof address !== "object") throw malformed("address");
81
+ if (address.scriptPubKey === void 0 || address.unlockDefinition === void 0) {
82
+ throw new DecodeAddressPsbtDataError("scriptPubKey and unlockDefinition are required");
83
+ }
84
+ const scriptPubKey = decodeHex(address.scriptPubKey, "scriptPubKey");
85
+ const definition = parseUnlockDefinition(address.unlockDefinition);
86
+ switch (definition.type) {
87
+ case "p2wpkh":
88
+ return { scriptPubKey, unlockDefinition: {} };
89
+ case "p2sh-p2wpkh":
90
+ return {
91
+ scriptPubKey,
92
+ unlockDefinition: { redeemScript: decodeHex(definition.redeemScript, "redeemScript") }
93
+ };
94
+ case "p2tr-key-path":
95
+ return {
96
+ scriptPubKey,
97
+ unlockDefinition: {
98
+ tapInternalKey: decodeHex(definition.tapInternalKey, "tapInternalKey")
99
+ }
100
+ };
101
+ case "p2wsh":
102
+ return {
103
+ scriptPubKey,
104
+ unlockDefinition: {
105
+ witnessScript: decodeHex(definition.witnessScript, "witnessScript")
106
+ }
107
+ };
108
+ case "p2tr-script-path":
109
+ try {
110
+ return {
111
+ scriptPubKey,
112
+ unlockDefinition: {
113
+ tapLeafScript: definition.tapLeafScript.map(({ controlBlock, script }) => [
114
+ TaprootControlBlock.decode(decodeHex(controlBlock, "controlBlock")),
115
+ decodeHex(script, "script")
116
+ ])
117
+ }
118
+ };
119
+ } catch (error) {
120
+ if (error instanceof DecodeAddressPsbtDataError) throw error;
121
+ throw malformed("controlBlock", error);
122
+ }
123
+ }
124
+ };
125
+
126
+ // src/index.ts
20
127
  var Wallet = class {
21
128
  providerId;
22
129
  defaultAdapters = defaultAdapters;
@@ -117,5 +224,7 @@ var Wallet = class {
117
224
  };
118
225
  var index_default = new Wallet();
119
226
  export {
227
+ DecodeAddressPsbtDataError,
228
+ decodeAddressPsbtData,
120
229
  index_default as default
121
230
  };
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "sats-connect",
3
- "version": "4.2.1",
3
+ "version": "4.3.0-1764f8f",
4
4
  "main": "dist/index.mjs",
5
5
  "files": [
6
6
  "dist"
7
7
  ],
8
8
  "scripts": {
9
- "test": "jest",
9
+ "test": "NODE_OPTIONS=--experimental-vm-modules jest",
10
10
  "build": "npm run clean && tsup src/index.ts --format esm --dts",
11
11
  "build:watch": "npm run clean && tsup src/index.ts --format esm --dts --watch",
12
12
  "dev:build": "tsup src/index.ts --format esm --dts --watch",
@@ -24,13 +24,17 @@
24
24
  ]
25
25
  },
26
26
  "dependencies": {
27
- "@sats-connect/core": "0.10.0",
27
+ "@sats-connect/core": "0.18.2-263ae91",
28
28
  "@sats-connect/make-default-provider-config": "0.0.10",
29
29
  "@sats-connect/ui": "0.0.7",
30
- "valibot": "1.1.0"
30
+ "@scure/base": "2.4.0",
31
+ "@scure/btc-signer": "2.4.1",
32
+ "valibot": "1.2.0"
31
33
  },
32
34
  "devDependencies": {
35
+ "@types/jest": "29.5.14",
33
36
  "husky": "9.1.7",
37
+ "jest": "29.7.0",
34
38
  "lint-staged": "16.1.5",
35
39
  "prettier": "3.6.2",
36
40
  "prettier-plugin-organize-imports": "4.2.0",