@polkadot-apps/signer 0.1.1 → 0.1.2
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 +318 -0
- package/package.json +5 -4
package/README.md
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
# @polkadot-apps/signer
|
|
2
|
+
|
|
3
|
+
Multi-provider signer manager for Polkadot -- Host API, browser extensions, and dev accounts.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @polkadot-apps/signer
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**Optional peer dependency**: `@novasamatech/product-sdk` is required only when using the Host API provider (Polkadot Desktop/Mobile containers).
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add @novasamatech/product-sdk
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { SignerManager } from "@polkadot-apps/signer";
|
|
21
|
+
|
|
22
|
+
const manager = new SignerManager();
|
|
23
|
+
|
|
24
|
+
const result = await manager.connect();
|
|
25
|
+
if (!result.ok) throw result.error;
|
|
26
|
+
|
|
27
|
+
const accounts = result.value;
|
|
28
|
+
manager.selectAccount(accounts[0].address);
|
|
29
|
+
|
|
30
|
+
const signer = manager.getSigner();
|
|
31
|
+
// Pass `signer` to polkadot-api transaction calls
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Auto-detection
|
|
35
|
+
|
|
36
|
+
`connect()` detects the runtime environment and picks the best available provider:
|
|
37
|
+
|
|
38
|
+
- **Inside a container** (Polkadot Desktop/Mobile): tries Host API first, falls back to Extension.
|
|
39
|
+
- **Outside a container** (standalone browser): tries Extension first, falls back to Dev.
|
|
40
|
+
|
|
41
|
+
Override auto-detection by passing a provider type explicitly:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
await manager.connect("dev"); // force dev accounts
|
|
45
|
+
await manager.connect("extension"); // force browser extension
|
|
46
|
+
await manager.connect("host"); // force Host API
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Check the environment programmatically:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { isInsideContainer } from "@polkadot-apps/signer";
|
|
53
|
+
|
|
54
|
+
if (isInsideContainer()) {
|
|
55
|
+
console.log("Running inside Polkadot Desktop/Mobile");
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## State management
|
|
60
|
+
|
|
61
|
+
Subscribe to state changes to drive your UI. The callback fires on every status transition, account list change, or account selection.
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
const unsubscribe = manager.subscribe((state) => {
|
|
65
|
+
console.log(state.status); // "disconnected" | "connecting" | "connected"
|
|
66
|
+
console.log(state.accounts); // readonly SignerAccount[]
|
|
67
|
+
console.log(state.selectedAccount); // SignerAccount | null
|
|
68
|
+
console.log(state.activeProvider); // "host" | "extension" | "dev" | null
|
|
69
|
+
console.log(state.error); // SignerError | null
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Get a snapshot without subscribing
|
|
73
|
+
const snapshot = manager.getState();
|
|
74
|
+
|
|
75
|
+
// Clean up
|
|
76
|
+
unsubscribe();
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Account selection and signing
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
const selectResult = manager.selectAccount("5GrwvaEF...");
|
|
83
|
+
if (!selectResult.ok) {
|
|
84
|
+
console.error(selectResult.error);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const signer = manager.getSigner(); // PolkadotSigner | null
|
|
88
|
+
|
|
89
|
+
// Sign raw bytes (for non-transaction signing)
|
|
90
|
+
const signResult = await manager.signRaw(new Uint8Array([1, 2, 3]));
|
|
91
|
+
if (signResult.ok) {
|
|
92
|
+
console.log(signResult.value); // Uint8Array signature
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Host API features
|
|
97
|
+
|
|
98
|
+
When connected via the Host API provider, additional methods are available for product accounts, aliases, and ring VRF proofs.
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const productAccount = manager.getProductAccount("myDapp", 0);
|
|
102
|
+
const alias = manager.getProductAccountAlias("myDapp", 0);
|
|
103
|
+
const proof = await manager.createRingVRFProof("myDapp", 0, message);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Providers
|
|
107
|
+
|
|
108
|
+
Each provider handles a different signing backend. You rarely need to use providers directly -- `SignerManager` manages them for you. They are exported for advanced use cases or testing.
|
|
109
|
+
|
|
110
|
+
### DevProvider
|
|
111
|
+
|
|
112
|
+
Local dev accounts from the well-known Substrate mnemonic.
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { DevProvider } from "@polkadot-apps/signer";
|
|
116
|
+
|
|
117
|
+
const provider = new DevProvider({
|
|
118
|
+
names: ["Alice", "Bob"],
|
|
119
|
+
ss58Prefix: 42,
|
|
120
|
+
keyType: "sr25519", // or "ed25519"
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### ExtensionProvider
|
|
125
|
+
|
|
126
|
+
Browser wallet extensions discovered via `window.injectedWeb3`.
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import { ExtensionProvider } from "@polkadot-apps/signer";
|
|
130
|
+
|
|
131
|
+
const provider = new ExtensionProvider({
|
|
132
|
+
extensionName: "polkadot-js",
|
|
133
|
+
dappName: "My App",
|
|
134
|
+
injectionWait: 1_000,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// List available extensions
|
|
138
|
+
const extensions = await manager.getAvailableExtensions();
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### HostProvider
|
|
142
|
+
|
|
143
|
+
Polkadot Desktop/Mobile via product-sdk. Requires `@novasamatech/product-sdk`.
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { HostProvider } from "@polkadot-apps/signer";
|
|
147
|
+
|
|
148
|
+
const provider = new HostProvider({
|
|
149
|
+
hostTimeout: 10_000,
|
|
150
|
+
dappName: "My App",
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Inject Spektr compatibility layer
|
|
154
|
+
HostProvider.injectSpektr();
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Cleanup
|
|
158
|
+
|
|
159
|
+
Always destroy the manager when your application unmounts to release resources and close connections.
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
manager.disconnect();
|
|
163
|
+
manager.destroy();
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Error handling
|
|
167
|
+
|
|
168
|
+
All errors extend `SignerError`. Use type guards to narrow errors by provider.
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
import {
|
|
172
|
+
isHostError,
|
|
173
|
+
isExtensionError,
|
|
174
|
+
HostUnavailableError,
|
|
175
|
+
ExtensionNotFoundError,
|
|
176
|
+
NoAccountsError,
|
|
177
|
+
} from "@polkadot-apps/signer";
|
|
178
|
+
|
|
179
|
+
const result = await manager.connect();
|
|
180
|
+
if (!result.ok) {
|
|
181
|
+
if (isHostError(result.error)) {
|
|
182
|
+
console.error("Host API problem:", result.error.message);
|
|
183
|
+
} else if (isExtensionError(result.error)) {
|
|
184
|
+
console.error("Extension problem:", result.error.message);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## API
|
|
190
|
+
|
|
191
|
+
### `SignerManager`
|
|
192
|
+
|
|
193
|
+
#### `constructor(options?: SignerManagerOptions)`
|
|
194
|
+
|
|
195
|
+
Create a new manager instance.
|
|
196
|
+
|
|
197
|
+
#### `getState(): SignerState`
|
|
198
|
+
|
|
199
|
+
Return a snapshot of the current status, accounts, selected account, active provider, and error.
|
|
200
|
+
|
|
201
|
+
#### `subscribe(callback): () => void`
|
|
202
|
+
|
|
203
|
+
Subscribe to state changes. Returns an unsubscribe function.
|
|
204
|
+
|
|
205
|
+
| Parameter | Type | Description |
|
|
206
|
+
|-----------|------|-------------|
|
|
207
|
+
| `callback` | `(state: SignerState) => void` | Called on every state transition. |
|
|
208
|
+
|
|
209
|
+
#### `connect(providerType?): Promise<Result<SignerAccount[], SignerError>>`
|
|
210
|
+
|
|
211
|
+
Connect to accounts. Auto-detects the provider when no type is given.
|
|
212
|
+
|
|
213
|
+
| Parameter | Type | Description |
|
|
214
|
+
|-----------|------|-------------|
|
|
215
|
+
| `providerType` | `ProviderType` | Optional. `"host"`, `"extension"`, or `"dev"`. |
|
|
216
|
+
|
|
217
|
+
#### `disconnect(): void`
|
|
218
|
+
|
|
219
|
+
Disconnect the active provider and clear accounts.
|
|
220
|
+
|
|
221
|
+
#### `selectAccount(address): Result<SignerAccount, SignerError>`
|
|
222
|
+
|
|
223
|
+
Select an account by SS58 address.
|
|
224
|
+
|
|
225
|
+
#### `getSigner(): PolkadotSigner | null`
|
|
226
|
+
|
|
227
|
+
Return the signer for the currently selected account, or `null`.
|
|
228
|
+
|
|
229
|
+
#### `signRaw(data): Promise<Result<Uint8Array, SignerError>>`
|
|
230
|
+
|
|
231
|
+
Sign raw bytes with the selected account.
|
|
232
|
+
|
|
233
|
+
#### `getProductAccount(dappName, index): ProductAccount`
|
|
234
|
+
|
|
235
|
+
Host API only. Get a product account by dapp name and index.
|
|
236
|
+
|
|
237
|
+
#### `getProductAccountAlias(dappName, index): ContextualAlias`
|
|
238
|
+
|
|
239
|
+
Host API only. Get the contextual alias for a product account.
|
|
240
|
+
|
|
241
|
+
#### `createRingVRFProof(dappName, index, message): Promise<RingLocation>`
|
|
242
|
+
|
|
243
|
+
Host API only. Create a ring VRF proof.
|
|
244
|
+
|
|
245
|
+
#### `getAvailableExtensions(): Promise<string[]>`
|
|
246
|
+
|
|
247
|
+
List browser extension names found in `window.injectedWeb3`.
|
|
248
|
+
|
|
249
|
+
#### `destroy(): void`
|
|
250
|
+
|
|
251
|
+
Release all resources. The manager must not be used after this call.
|
|
252
|
+
|
|
253
|
+
### Standalone functions
|
|
254
|
+
|
|
255
|
+
| Function | Signature | Description |
|
|
256
|
+
|----------|-----------|-------------|
|
|
257
|
+
| `isInsideContainer` | `() => boolean` | Synchronous container environment check. |
|
|
258
|
+
| `ok` | `<T>(value: T) => Result<T, never>` | Create a successful `Result`. |
|
|
259
|
+
| `err` | `<E>(error: E) => Result<never, E>` | Create a failed `Result`. |
|
|
260
|
+
| `isHostError` | `(e: SignerError) => boolean` | Type guard for Host API errors. |
|
|
261
|
+
| `isExtensionError` | `(e: SignerError) => boolean` | Type guard for extension errors. |
|
|
262
|
+
|
|
263
|
+
## Types
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
type ConnectionStatus = "disconnected" | "connecting" | "connected";
|
|
267
|
+
|
|
268
|
+
type ProviderType = "host" | "extension" | "dev";
|
|
269
|
+
|
|
270
|
+
interface SignerAccount {
|
|
271
|
+
address: string;
|
|
272
|
+
h160Address: `0x${string}`;
|
|
273
|
+
publicKey: Uint8Array;
|
|
274
|
+
name: string | null;
|
|
275
|
+
source: ProviderType;
|
|
276
|
+
getSigner(): PolkadotSigner;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
interface SignerState {
|
|
280
|
+
status: ConnectionStatus;
|
|
281
|
+
accounts: readonly SignerAccount[];
|
|
282
|
+
selectedAccount: SignerAccount | null;
|
|
283
|
+
activeProvider: ProviderType | null;
|
|
284
|
+
error: SignerError | null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
interface SignerManagerOptions {
|
|
288
|
+
ss58Prefix?: number; // default: 42
|
|
289
|
+
hostTimeout?: number; // default: 10_000
|
|
290
|
+
extensionTimeout?: number; // default: 1_000
|
|
291
|
+
maxRetries?: number; // default: 3
|
|
292
|
+
createProvider?: ProviderFactory;
|
|
293
|
+
dappName?: string; // default: "polkadot-app"
|
|
294
|
+
persistence?: AccountPersistence | null;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Error classes
|
|
301
|
+
|
|
302
|
+
| Class | Extends | When thrown |
|
|
303
|
+
|-------|---------|------------|
|
|
304
|
+
| `SignerError` | `Error` | Base class for all signer errors. |
|
|
305
|
+
| `HostUnavailableError` | `SignerError` | Host API not found or not responding. |
|
|
306
|
+
| `HostRejectedError` | `SignerError` | Host API rejected the request. |
|
|
307
|
+
| `HostDisconnectedError` | `SignerError` | Host API connection lost. |
|
|
308
|
+
| `ExtensionNotFoundError` | `SignerError` | Browser extension not installed or not injected. |
|
|
309
|
+
| `ExtensionRejectedError` | `SignerError` | User rejected the extension prompt. |
|
|
310
|
+
| `SigningFailedError` | `SignerError` | Signing operation failed. |
|
|
311
|
+
| `NoAccountsError` | `SignerError` | Provider returned zero accounts. |
|
|
312
|
+
| `TimeoutError` | `SignerError` | Operation timed out. |
|
|
313
|
+
| `AccountNotFoundError` | `SignerError` | Selected address not in the account list. |
|
|
314
|
+
| `DestroyedError` | `SignerError` | Method called after `destroy()`. |
|
|
315
|
+
|
|
316
|
+
## License
|
|
317
|
+
|
|
318
|
+
Apache-2.0
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polkadot-apps/signer",
|
|
3
|
-
"
|
|
3
|
+
"description": "Multi-provider signer manager for Polkadot — Host API, browser extensions, and dev accounts",
|
|
4
|
+
"version": "0.1.2",
|
|
4
5
|
"type": "module",
|
|
5
6
|
"main": "./dist/index.js",
|
|
6
7
|
"types": "./dist/index.d.ts",
|
|
@@ -19,9 +20,9 @@
|
|
|
19
20
|
},
|
|
20
21
|
"dependencies": {
|
|
21
22
|
"polkadot-api": "^1.23.3",
|
|
22
|
-
"@polkadot-apps/address": "0.3.
|
|
23
|
-
"@polkadot-apps/keys": "0.3.
|
|
24
|
-
"@polkadot-apps/logger": "0.1.
|
|
23
|
+
"@polkadot-apps/address": "0.3.2",
|
|
24
|
+
"@polkadot-apps/keys": "0.3.3",
|
|
25
|
+
"@polkadot-apps/logger": "0.1.4"
|
|
25
26
|
},
|
|
26
27
|
"peerDependencies": {
|
|
27
28
|
"@novasamatech/product-sdk": "^0.6.6"
|