@universal-signer/core 0.0.1

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 ADDED
@@ -0,0 +1,471 @@
1
+ # @universal-signer/core
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@universal-signer/core.svg)](https://www.npmjs.com/package/@universal-signer/core)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)
6
+ [![Viem](https://img.shields.io/badge/Viem-v2.44-blue)](https://viem.sh)
7
+
8
+ A unified, type-safe library that provides a single interface for **AWS KMS**, **Google Cloud KMS**, **Ledger**, **Trezor**, **Turnkey**, and **Local Keys** — all compatible with [Viem v2](https://viem.sh).
9
+
10
+ Write your blockchain signing logic once. Switch providers through configuration.
11
+
12
+ ## Table of Contents
13
+
14
+ - [Features](#features)
15
+ - [Installation](#installation)
16
+ - [Quick Start](#quick-start)
17
+ - [Providers](#providers)
18
+ - [AWS KMS](#aws-kms)
19
+ - [Google Cloud KMS](#google-cloud-kms)
20
+ - [Ledger](#ledger)
21
+ - [Trezor](#trezor)
22
+ - [Turnkey](#turnkey)
23
+ - [Local](#local)
24
+ - [API Reference](#api-reference)
25
+ - [Technical Details](#technical-details)
26
+ - [Troubleshooting](#troubleshooting)
27
+ - [License](#license)
28
+
29
+ ## Features
30
+
31
+ - **Unified API** — All providers return a Viem-compatible `LocalAccount`
32
+ - **Full Signing Support** — Transactions, messages (`signMessage`), and typed data (`signTypedData` / EIP-712)
33
+ - **Cloud KMS Ready** — Automatic ASN.1 DER decoding and EIP-2 signature normalization
34
+ - **Hardware Wallet Support** — Ledger (USB HID) and Trezor (Connect) with proper resource management
35
+ - **Type-Safe** — Full TypeScript support with exported interfaces for all configurations
36
+ - **Modern Runtime** — Built for Bun and Node.js
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ # Bun (recommended)
42
+ bun add @universal-signer/core viem
43
+
44
+ # npm
45
+ npm install @universal-signer/core viem
46
+
47
+ # pnpm
48
+ pnpm add @universal-signer/core viem
49
+
50
+ # yarn
51
+ yarn add @universal-signer/core viem
52
+ ```
53
+
54
+ ## Quick Start
55
+
56
+ ```typescript
57
+ import { createUniversalClient, createAwsAccount } from "@universal-signer/core";
58
+ import { mainnet } from "viem/chains";
59
+ import { http } from "viem";
60
+
61
+ // 1. Create an account (using AWS KMS as example)
62
+ const account = await createAwsAccount({
63
+ keyId: "alias/my-eth-key",
64
+ region: "us-east-1",
65
+ });
66
+
67
+ // 2. Create a wallet client
68
+ const client = createUniversalClient(account, mainnet, http());
69
+
70
+ // 3. Use standard Viem methods
71
+ const hash = await client.sendTransaction({
72
+ to: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
73
+ value: 1_000_000_000_000_000n, // 0.001 ETH
74
+ });
75
+
76
+ // Sign a message
77
+ const signature = await account.signMessage({
78
+ message: "Hello, Ethereum!",
79
+ });
80
+
81
+ // Sign typed data (EIP-712)
82
+ const typedSignature = await account.signTypedData({
83
+ domain: {
84
+ name: "My App",
85
+ version: "1",
86
+ chainId: 1,
87
+ },
88
+ types: {
89
+ Message: [{ name: "content", type: "string" }],
90
+ },
91
+ primaryType: "Message",
92
+ message: { content: "Hello" },
93
+ });
94
+ ```
95
+
96
+ ## Providers
97
+
98
+ ### AWS KMS
99
+
100
+ Uses `@aws-sdk/client-kms` for signing with AWS Key Management Service.
101
+
102
+ #### Prerequisites
103
+
104
+ - **Key Spec**: `ECC_SECG_P256K1`
105
+ - **Key Usage**: `SIGN_VERIFY`
106
+ - **IAM Permissions**: `kms:GetPublicKey`, `kms:Sign`
107
+
108
+ #### Usage
109
+
110
+ ```typescript
111
+ import { createAwsAccount, type AwsKmsConfig } from "@universal-signer/core";
112
+
113
+ const config: AwsKmsConfig = {
114
+ // Key ID, ARN, or alias
115
+ keyId: "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012",
116
+ // AWS region (default: "us-east-1")
117
+ region: "us-east-1",
118
+ // Optional: explicit credentials (defaults to AWS SDK credential chain)
119
+ credentials: {
120
+ accessKeyId: "AKIA...",
121
+ secretAccessKey: "...",
122
+ },
123
+ };
124
+
125
+ const account = await createAwsAccount(config);
126
+ console.log("Address:", account.address);
127
+ ```
128
+
129
+ #### Configuration
130
+
131
+ | Property | Type | Required | Default | Description |
132
+ |----------|------|----------|---------|-------------|
133
+ | `keyId` | `string` | Yes | — | KMS key ID, ARN, or alias |
134
+ | `region` | `string` | No | `"us-east-1"` | AWS region |
135
+ | `credentials` | `object` | No | SDK default | AWS credentials |
136
+
137
+ ---
138
+
139
+ ### Google Cloud KMS
140
+
141
+ Uses `@google-cloud/kms` for signing with Google Cloud Key Management Service.
142
+
143
+ #### Prerequisites
144
+
145
+ - **Algorithm**: `EC_SIGN_SECP256K1_SHA256`
146
+ - **Purpose**: `ASYMMETRIC_SIGN`
147
+ - **IAM Role**: `roles/cloudkms.signerVerifier`
148
+
149
+ #### Usage
150
+
151
+ ```typescript
152
+ import { createGcpAccount, type GcpKmsConfig } from "@universal-signer/core";
153
+
154
+ const config: GcpKmsConfig = {
155
+ // Full resource name of the CryptoKeyVersion
156
+ name: "projects/my-project/locations/us-central1/keyRings/my-ring/cryptoKeys/eth-key/cryptoKeyVersions/1",
157
+ // Optional: GCP client options
158
+ clientOptions: {
159
+ projectId: "my-project",
160
+ // credentials: require("./service-account.json"),
161
+ },
162
+ };
163
+
164
+ const account = await createGcpAccount(config);
165
+ console.log("Address:", account.address);
166
+ ```
167
+
168
+ #### Configuration
169
+
170
+ | Property | Type | Required | Description |
171
+ |----------|------|----------|-------------|
172
+ | `name` | `string` | Yes | Full CryptoKeyVersion resource name |
173
+ | `clientOptions` | `ClientOptions` | No | GCP client configuration (from `google-gax`) |
174
+
175
+ ---
176
+
177
+ ### Ledger
178
+
179
+ Uses `@ledgerhq/hw-app-eth` over USB HID for hardware wallet signing.
180
+
181
+ #### Prerequisites
182
+
183
+ - Ledger device connected via USB
184
+ - Ethereum app installed and open
185
+ - **Blind signing** enabled (Settings > Blind signing)
186
+ - **Linux**: Configure udev rules for USB access
187
+
188
+ #### Usage
189
+
190
+ ```typescript
191
+ import { createLedgerAccount, type LedgerConfig, type LedgerAccount } from "@universal-signer/core";
192
+
193
+ const config: LedgerConfig = {
194
+ // BIP-44 derivation path (default: "44'/60'/0'/0/0")
195
+ derivationPath: "44'/60'/0'/0/0",
196
+ };
197
+
198
+ const account: LedgerAccount = await createLedgerAccount(config);
199
+ console.log("Address:", account.address);
200
+
201
+ // Sign transactions, messages, or typed data
202
+ const signature = await account.signMessage({ message: "Hello" });
203
+
204
+ // Important: Close the transport when done
205
+ await account.close();
206
+ ```
207
+
208
+ #### Configuration
209
+
210
+ | Property | Type | Required | Default | Description |
211
+ |----------|------|----------|---------|-------------|
212
+ | `derivationPath` | `string` | No | `"44'/60'/0'/0/0"` | BIP-44 derivation path |
213
+ | `transport` | `Transport` | No | Auto-created | Custom HID transport instance |
214
+
215
+ #### Extended Account
216
+
217
+ `LedgerAccount` extends `LocalAccount` with:
218
+
219
+ | Method | Description |
220
+ |--------|-------------|
221
+ | `close()` | Closes the USB HID transport connection |
222
+
223
+ ---
224
+
225
+ ### Trezor
226
+
227
+ Uses `@trezor/connect` for hardware wallet signing via Trezor Connect.
228
+
229
+ #### Prerequisites
230
+
231
+ - Trezor device connected
232
+ - Trezor Bridge installed (or using WebUSB)
233
+ - Valid manifest configuration (required by Trezor)
234
+
235
+ #### Usage
236
+
237
+ ```typescript
238
+ import { createTrezorAccount, type TrezorConfig } from "@universal-signer/core";
239
+
240
+ const config: TrezorConfig = {
241
+ // Required: Trezor Connect manifest
242
+ email: "developer@myapp.com",
243
+ appUrl: "https://myapp.com",
244
+ appName: "My Application",
245
+ // Optional: BIP-44 derivation path
246
+ derivationPath: "m/44'/60'/0'/0/0",
247
+ };
248
+
249
+ const account = await createTrezorAccount(config);
250
+ console.log("Address:", account.address);
251
+ ```
252
+
253
+ #### Configuration
254
+
255
+ | Property | Type | Required | Default | Description |
256
+ |----------|------|----------|---------|-------------|
257
+ | `email` | `string` | Yes | — | Contact email for manifest |
258
+ | `appUrl` | `string` | Yes | — | Application URL for manifest |
259
+ | `appName` | `string` | Yes | — | Application name for manifest |
260
+ | `derivationPath` | `string` | No | `"m/44'/60'/0'/0/0"` | BIP-44 derivation path |
261
+
262
+ ---
263
+
264
+ ### Turnkey
265
+
266
+ Uses `@turnkey/viem` for signing with Turnkey's key management infrastructure.
267
+
268
+ #### Usage
269
+
270
+ ```typescript
271
+ import { createTurnkeyAccount } from "@universal-signer/core";
272
+
273
+ const account = await createTurnkeyAccount({
274
+ baseUrl: "https://api.turnkey.com",
275
+ apiPublicKey: process.env.TURNKEY_API_PUBLIC_KEY!,
276
+ apiPrivateKey: process.env.TURNKEY_API_PRIVATE_KEY!,
277
+ organizationId: process.env.TURNKEY_ORG_ID!,
278
+ privateKeyId: process.env.TURNKEY_PRIVATE_KEY_ID!,
279
+ });
280
+
281
+ console.log("Address:", account.address);
282
+ ```
283
+
284
+ #### Configuration
285
+
286
+ | Property | Type | Required | Description |
287
+ |----------|------|----------|-------------|
288
+ | `baseUrl` | `string` | Yes | Turnkey API base URL |
289
+ | `apiPublicKey` | `string` | Yes | Turnkey API public key |
290
+ | `apiPrivateKey` | `string` | Yes | Turnkey API private key |
291
+ | `organizationId` | `string` | Yes | Turnkey organization ID |
292
+ | `privateKeyId` | `string` | Yes | Private key or wallet ID in Turnkey |
293
+
294
+ ---
295
+
296
+ ### Local
297
+
298
+ Wraps Viem's native account functions for development and testing.
299
+
300
+ > **Warning**: Never use local accounts with real private keys in production.
301
+
302
+ #### Usage
303
+
304
+ ```typescript
305
+ import { createLocalAccount } from "@universal-signer/core";
306
+
307
+ // From private key
308
+ const account = createLocalAccount({
309
+ privateKey: "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
310
+ });
311
+
312
+ // Or from mnemonic
313
+ const accountFromMnemonic = createLocalAccount({
314
+ mnemonic: "test test test test test test test test test test test junk",
315
+ });
316
+
317
+ console.log("Address:", account.address);
318
+ ```
319
+
320
+ #### Configuration
321
+
322
+ | Property | Type | Required | Description |
323
+ |----------|------|----------|-------------|
324
+ | `privateKey` | `Hex` | One of | 32-byte private key with 0x prefix |
325
+ | `mnemonic` | `string` | One of | BIP-39 mnemonic phrase |
326
+
327
+ ---
328
+
329
+ ## API Reference
330
+
331
+ ### Exports
332
+
333
+ ```typescript
334
+ // Provider functions
335
+ export { createAwsAccount } from "./providers/aws";
336
+ export { createGcpAccount } from "./providers/gcp";
337
+ export { createLedgerAccount } from "./providers/ledger";
338
+ export { createLocalAccount } from "./providers/local";
339
+ export { createTrezorAccount } from "./providers/trezor";
340
+ export { createTurnkeyAccount } from "./providers/turnkey";
341
+
342
+ // Configuration types
343
+ export type { AwsKmsConfig } from "./providers/aws";
344
+ export type { GcpKmsConfig } from "./providers/gcp";
345
+ export type { LedgerConfig, LedgerAccount } from "./providers/ledger";
346
+ export type { TrezorConfig } from "./providers/trezor";
347
+
348
+ // Utilities
349
+ export { normalizeKmsSignature } from "./utils/kms";
350
+ export { createUniversalClient } from "./index";
351
+ ```
352
+
353
+ ### `createUniversalClient`
354
+
355
+ Helper function to create a Viem `WalletClient` from any account.
356
+
357
+ ```typescript
358
+ function createUniversalClient(
359
+ account: Account,
360
+ chain?: Chain, // Default: mainnet
361
+ transport?: Transport // Default: http()
362
+ ): WalletClient
363
+ ```
364
+
365
+ ### `normalizeKmsSignature`
366
+
367
+ Low-level utility for converting KMS DER signatures to Ethereum format.
368
+
369
+ ```typescript
370
+ function normalizeKmsSignature(
371
+ derSignature: Uint8Array | Buffer,
372
+ digest: Hash,
373
+ expectedAddress: string
374
+ ): Promise<{ r: Hex; s: Hex; v: bigint }>
375
+ ```
376
+
377
+ ---
378
+
379
+ ## Technical Details
380
+
381
+ ### KMS Signature Normalization
382
+
383
+ Cloud KMS providers return ECDSA signatures in ASN.1 DER format. Ethereum requires raw `(r, s, v)` signatures. This library handles the conversion:
384
+
385
+ 1. **DER Parsing** — Extracts `r` and `s` integers from the ASN.1 structure
386
+ 2. **EIP-2 Normalization** — Ensures `s <= secp256k1.n / 2` to prevent signature malleability
387
+ 3. **Recovery ID** — Determines `v` (27 or 28) by trial recovery against the known public key
388
+
389
+ ### Supported Operations
390
+
391
+ | Provider | `signTransaction` | `signMessage` | `signTypedData` |
392
+ |----------|:-----------------:|:-------------:|:---------------:|
393
+ | AWS KMS | Yes | Yes | Yes |
394
+ | GCP KMS | Yes | Yes | Yes |
395
+ | Ledger | Yes | Yes | Yes |
396
+ | Trezor | Yes | Yes | Yes |
397
+ | Turnkey | Yes | Yes | Yes |
398
+ | Local | Yes | Yes | Yes |
399
+
400
+ ### Transaction Types
401
+
402
+ All providers support:
403
+ - Legacy transactions
404
+ - EIP-2930 (Type 1)
405
+ - EIP-1559 (Type 2)
406
+ - Contract deployments (no `to` address)
407
+
408
+ ---
409
+
410
+ ## Troubleshooting
411
+
412
+ ### "Invalid DER: Unexpected end of data"
413
+
414
+ The KMS returned a malformed signature. This can happen due to:
415
+ - Network issues truncating the response
416
+ - Incorrect key configuration
417
+
418
+ **Solution**: Retry the operation. If persistent, verify your KMS key configuration.
419
+
420
+ ### "AWS KMS: Unable to retrieve Public Key"
421
+
422
+ **Causes**:
423
+ - Incorrect key ID or ARN
424
+ - Missing IAM permissions
425
+ - Key is disabled or pending deletion
426
+
427
+ **Solution**: Verify the key exists and your credentials have `kms:GetPublicKey` permission.
428
+
429
+ ### "GCP KMS: Public Key not found"
430
+
431
+ **Causes**:
432
+ - Incorrect resource name format
433
+ - Missing IAM permissions
434
+ - Key version is disabled
435
+
436
+ **Solution**: Verify the full resource path and `cloudkms.cryptoKeyVersions.viewPublicKey` permission.
437
+
438
+ ### Ledger Connection Issues
439
+
440
+ **Causes**:
441
+ - Another application has the device open (Ledger Live, browser wallet)
442
+ - Ethereum app not open on device
443
+ - USB permissions (Linux)
444
+
445
+ **Solutions**:
446
+ 1. Close Ledger Live and any browser wallets
447
+ 2. Open the Ethereum app on your Ledger
448
+ 3. On Linux, add udev rules:
449
+ ```bash
450
+ # /etc/udev/rules.d/20-hw1.rules
451
+ SUBSYSTEM=="usb", ATTR{idVendor}=="2c97", MODE="0666"
452
+ ```
453
+
454
+ ### Trezor "Manifest not set"
455
+
456
+ Trezor Connect requires a valid manifest with `email`, `appUrl`, and `appName`.
457
+
458
+ **Solution**: Ensure all three manifest fields are provided in your configuration.
459
+
460
+ ### "Trezor: chainId is required"
461
+
462
+ Trezor requires an explicit `chainId` for transaction signing.
463
+
464
+ **Solution**: Include `chainId` in your transaction object.
465
+
466
+ ---
467
+
468
+ ## License
469
+
470
+ MIT
471
+
@@ -0,0 +1,9 @@
1
+ export { createAwsAccount, type AwsKmsConfig } from "./providers/aws";
2
+ export { createGcpAccount, type GcpKmsConfig } from "./providers/gcp";
3
+ export { createLedgerAccount, type LedgerAccount, type LedgerConfig, } from "./providers/ledger";
4
+ export { createLocalAccount } from "./providers/local";
5
+ export { createTrezorAccount, type TrezorConfig } from "./providers/trezor";
6
+ export { createTurnkeyAccount } from "./providers/turnkey";
7
+ export { normalizeKmsSignature } from "./utils/kms";
8
+ import { type Account, type Chain, type Transport, type WalletClient } from "viem";
9
+ export declare const createUniversalClient: (account: Account, chain?: Chain, transport?: Transport) => WalletClient<Transport, Chain, Account>;