sats-connect 4.2.1 → 4.3.0-2b92219
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 +45 -14
- package/dist/index.d.mts +22 -3
- package/dist/index.mjs +109 -0
- package/package.json +8 -4
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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,
|
|
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<
|
|
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.
|
|
3
|
+
"version": "4.3.0-2b92219",
|
|
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.
|
|
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
|
-
"
|
|
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",
|