voltaire-effect 0.2.25 → 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.
Files changed (31) hide show
  1. package/README.md +184 -12
  2. package/dist/{index-BCOuszKZ.d.ts → index-3UKSP3cd.d.ts} +74 -31
  3. package/dist/index.d.ts +107 -106
  4. package/dist/index.js +1770 -1224
  5. package/dist/native/index.d.ts +7 -6
  6. package/dist/native/index.js +1795 -1249
  7. package/dist/primitives/index.d.ts +1 -1
  8. package/dist/primitives/index.js +37 -4
  9. package/dist/services/index.d.ts +1031 -519
  10. package/dist/services/index.js +1276 -762
  11. package/package.json +11 -11
  12. package/src/primitives/Abi/encodeFunctionData.bench.ts +79 -0
  13. package/src/primitives/Address/Hex.ts +15 -1
  14. package/src/primitives/Block/BlockSchema.ts +1 -1
  15. package/src/primitives/PrivateKey/Bytes.ts +35 -0
  16. package/src/primitives/PrivateKey/Hex.ts +49 -1
  17. package/src/primitives/PrivateKey/PrivateKey.error.test.ts +167 -0
  18. package/src/primitives/PrivateKey/PrivateKey.test.ts +79 -0
  19. package/src/primitives/PrivateKey/index.ts +24 -32
  20. package/src/services/Contract/ContractsService.test.ts +351 -0
  21. package/src/services/Contract/ContractsService.ts +206 -0
  22. package/src/services/Contract/index.ts +54 -13
  23. package/src/services/Provider/ExecutionPlanProvider.test.ts +123 -0
  24. package/src/services/Provider/ExecutionPlanProvider.ts +151 -0
  25. package/src/services/Provider/index.ts +19 -0
  26. package/src/services/Provider/providers.ts +224 -0
  27. package/src/services/RpcBatch/RpcResolver.ts +4 -4
  28. package/src/services/RpcBatch/index.ts +9 -9
  29. package/src/services/Transport/BrowserTransport.ts +4 -4
  30. package/src/services/Transport/TransportInterceptor.ts +4 -4
  31. package/src/services/index.ts +20 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "voltaire-effect",
3
- "version": "0.2.25",
3
+ "version": "0.3.0",
4
4
  "description": "Effect-TS integration for Voltaire Ethereum primitives library",
5
5
  "author": "TEVM",
6
6
  "license": "MIT",
@@ -78,20 +78,20 @@
78
78
  },
79
79
  "sideEffects": false,
80
80
  "peerDependencies": {
81
- "effect": "^3.12.0",
82
- "@tevm/voltaire": "0.2.25"
83
- },
84
- "dependencies": {
85
- "@effect/platform": "^0.77.0"
81
+ "@effect/platform": "^0.94.0",
82
+ "effect": "^3.19.0",
83
+ "@tevm/voltaire": "0.2.28"
86
84
  },
87
85
  "optionalDependencies": {
88
- "@effect/platform-browser": "^0.57.0",
89
- "@effect/platform-bun": "^0.60.0",
90
- "@effect/platform-node": "^0.77.0"
86
+ "@effect/platform-browser": "^0.74.0",
87
+ "@effect/platform-bun": "^0.87.0",
88
+ "@effect/platform-node": "^0.104.0"
91
89
  },
92
90
  "devDependencies": {
93
- "@effect/platform-node": "^0.77.0",
94
- "@effect/vitest": "^0.18.1",
91
+ "@effect/platform": "^0.94.0",
92
+ "@effect/platform-node": "^0.104.0",
93
+ "@effect/vitest": "^0.27.0",
94
+ "effect": "^3.19.0",
95
95
  "astro": "^5.0.0",
96
96
  "tsup": "^8.4.0",
97
97
  "typescript": "^5.9.3",
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Benchmark: voltaire vs voltaire-effect vs viem encodeFunctionData
3
+ */
4
+
5
+ import { Effect } from "effect";
6
+ import { bench, run } from "mitata";
7
+ import { encodeFunctionData as viemEncodeFunctionData } from "viem";
8
+ import * as Abi from "@tevm/voltaire/Abi";
9
+ import { encodeFunctionData } from "./encodeFunctionData.js";
10
+
11
+ const erc20Abi = [
12
+ {
13
+ type: "function",
14
+ name: "transfer",
15
+ stateMutability: "nonpayable",
16
+ inputs: [
17
+ { type: "address", name: "to" },
18
+ { type: "uint256", name: "amount" },
19
+ ],
20
+ outputs: [{ type: "bool", name: "" }],
21
+ },
22
+ {
23
+ type: "function",
24
+ name: "balanceOf",
25
+ stateMutability: "view",
26
+ inputs: [{ type: "address", name: "account" }],
27
+ outputs: [{ type: "uint256", name: "" }],
28
+ },
29
+ {
30
+ type: "function",
31
+ name: "approve",
32
+ stateMutability: "nonpayable",
33
+ inputs: [
34
+ { type: "address", name: "spender" },
35
+ { type: "uint256", name: "amount" },
36
+ ],
37
+ outputs: [{ type: "bool", name: "" }],
38
+ },
39
+ ] as const;
40
+
41
+ const recipient = "0x742d35cc6634c0532925a3b844bc9e7595f251e3";
42
+ const amount = 1000000000000000000n;
43
+
44
+ bench("encodeFunctionData - viem", () => {
45
+ viemEncodeFunctionData({
46
+ abi: erc20Abi,
47
+ functionName: "transfer",
48
+ args: [recipient, amount],
49
+ });
50
+ });
51
+
52
+ bench("encodeFunctionData - voltaire", () => {
53
+ Abi.encodeFunction(erc20Abi, "transfer", [recipient, amount]);
54
+ });
55
+
56
+ bench("encodeFunctionData - voltaire-effect (sync)", () => {
57
+ Effect.runSync(encodeFunctionData(erc20Abi, "transfer", [recipient, amount]));
58
+ });
59
+
60
+ await run();
61
+
62
+ // Also test balanceOf (single param)
63
+ bench("balanceOf - viem", () => {
64
+ viemEncodeFunctionData({
65
+ abi: erc20Abi,
66
+ functionName: "balanceOf",
67
+ args: [recipient],
68
+ });
69
+ });
70
+
71
+ bench("balanceOf - voltaire", () => {
72
+ Abi.encodeFunction(erc20Abi, "balanceOf", [recipient]);
73
+ });
74
+
75
+ bench("balanceOf - voltaire-effect (sync)", () => {
76
+ Effect.runSync(encodeFunctionData(erc20Abi, "balanceOf", [recipient]));
77
+ });
78
+
79
+ await run();
@@ -11,6 +11,12 @@ import * as ParseResult from "effect/ParseResult";
11
11
  import * as S from "effect/Schema";
12
12
  import { AddressTypeSchema } from "./AddressSchema.js";
13
13
 
14
+ // Pre-computed example addresses for schema annotations
15
+ const EXAMPLE_ADDRESSES: readonly [AddressType, AddressType] = [
16
+ Address("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"),
17
+ Address("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
18
+ ] as const;
19
+
14
20
  /**
15
21
  * Schema for Address encoded as a hex string.
16
22
  *
@@ -53,4 +59,12 @@ export const Hex: S.Schema<AddressType, string> = S.transformOrFail(
53
59
  return ParseResult.succeed(Address.toHex(addr));
54
60
  },
55
61
  },
56
- ).annotations({ identifier: "Address.Hex" });
62
+ ).annotations({
63
+ identifier: "Address.Hex",
64
+ title: "Ethereum Address",
65
+ description:
66
+ "A 20-byte Ethereum address as a hex string. Accepts checksummed, lowercase, or uppercase.",
67
+ examples: EXAMPLE_ADDRESSES,
68
+ message: () =>
69
+ "Invalid Ethereum address: expected 40 hex characters with 0x prefix",
70
+ });
@@ -198,4 +198,4 @@ export const BlockSchema: Schema.Schema<BlockType> = Schema.declare(
198
198
 
199
199
  return true;
200
200
  },
201
- );
201
+ ).annotations({ identifier: "BlockSchema" });
@@ -24,6 +24,7 @@
24
24
  */
25
25
 
26
26
  import { PrivateKey, type PrivateKeyType } from "@tevm/voltaire/PrivateKey";
27
+ import { Redacted } from "effect";
27
28
  import * as ParseResult from "effect/ParseResult";
28
29
  import * as S from "effect/Schema";
29
30
 
@@ -57,3 +58,37 @@ export const Bytes: S.Schema<PrivateKeyType, Uint8Array> = S.transformOrFail(
57
58
  },
58
59
  },
59
60
  ).annotations({ identifier: "PrivateKey.Bytes" });
61
+
62
+ /**
63
+ * Schema for PrivateKey encoded as raw bytes, wrapped in Redacted.
64
+ *
65
+ * @description
66
+ * Transforms Uint8Array to Redacted<PrivateKeyType> for secure handling.
67
+ * The redacted wrapper prevents accidental logging of private keys.
68
+ * Use `Redacted.value()` to explicitly unwrap for cryptographic operations.
69
+ *
70
+ * @example Decoding
71
+ * ```typescript
72
+ * import * as PrivateKey from 'voltaire-effect/primitives/PrivateKey'
73
+ * import { Redacted } from 'effect'
74
+ * import * as S from 'effect/Schema'
75
+ *
76
+ * const bytes = new Uint8Array(32).fill(1)
77
+ * const pk = S.decodeSync(PrivateKey.RedactedBytes)(bytes)
78
+ * console.log(pk) // Redacted(<redacted>)
79
+ *
80
+ * const unwrapped = Redacted.value(pk)
81
+ * // Use unwrapped for signing
82
+ * ```
83
+ *
84
+ * @since 0.1.0
85
+ */
86
+ export const RedactedBytes: S.Schema<
87
+ Redacted.Redacted<PrivateKeyType>,
88
+ Uint8Array
89
+ > = Bytes.pipe(S.Redacted).annotations({
90
+ identifier: "PrivateKey.RedactedBytes",
91
+ title: "Private Key (Redacted)",
92
+ description:
93
+ "A 32-byte secp256k1 private key wrapped in Redacted to prevent accidental logging",
94
+ });
@@ -29,9 +29,15 @@ import {
29
29
  PrivateKey,
30
30
  type PrivateKeyType,
31
31
  } from "@tevm/voltaire/PrivateKey";
32
+ import { Redacted } from "effect";
32
33
  import * as ParseResult from "effect/ParseResult";
33
34
  import * as S from "effect/Schema";
34
35
 
36
+ // Pre-computed example private key for schema annotations (test vector only)
37
+ const EXAMPLE_PRIVATE_KEY: PrivateKeyType = PrivateKey.from(
38
+ "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
39
+ );
40
+
35
41
  const PrivateKeyTypeSchema = S.declare<PrivateKeyType>(
36
42
  (u): u is PrivateKeyType => u instanceof Uint8Array && u.length === 32,
37
43
  { identifier: "PrivateKey" },
@@ -61,4 +67,46 @@ export const Hex: S.Schema<PrivateKeyType, string> = S.transformOrFail(
61
67
  }
62
68
  },
63
69
  },
64
- ).annotations({ identifier: "PrivateKey.Hex" });
70
+ ).annotations({
71
+ identifier: "PrivateKey.Hex",
72
+ title: "Private Key",
73
+ description:
74
+ "A 32-byte secp256k1 private key as a hex string. NEVER log or expose this value.",
75
+ examples: [EXAMPLE_PRIVATE_KEY],
76
+ message: () => "Invalid private key: expected 64 hex characters (32 bytes)",
77
+ });
78
+
79
+ /**
80
+ * Schema for PrivateKey encoded as hex string, wrapped in Redacted.
81
+ *
82
+ * @description
83
+ * Transforms hex strings to Redacted<PrivateKeyType> for secure handling.
84
+ * The redacted wrapper prevents accidental logging of private keys.
85
+ * Use `Redacted.value()` to explicitly unwrap for cryptographic operations.
86
+ *
87
+ * @example Decoding
88
+ * ```typescript
89
+ * import * as PrivateKey from 'voltaire-effect/primitives/PrivateKey'
90
+ * import { Redacted } from 'effect'
91
+ * import * as S from 'effect/Schema'
92
+ *
93
+ * const pk = S.decodeSync(PrivateKey.RedactedHex)('0x0123...')
94
+ * console.log(pk) // Redacted(<redacted>)
95
+ *
96
+ * const unwrapped = Redacted.value(pk)
97
+ * // Use unwrapped for signing
98
+ * ```
99
+ *
100
+ * @since 0.1.0
101
+ */
102
+ export const RedactedHex: S.Schema<
103
+ Redacted.Redacted<PrivateKeyType>,
104
+ string
105
+ > = Hex.pipe(S.Redacted).annotations({
106
+ identifier: "PrivateKey.RedactedHex",
107
+ title: "Private Key (Redacted)",
108
+ description:
109
+ "A 32-byte secp256k1 private key wrapped in Redacted to prevent accidental logging. NEVER log or expose the unwrapped value.",
110
+ examples: [Redacted.make(EXAMPLE_PRIVATE_KEY)],
111
+ message: () => "Invalid private key: expected 64 hex characters (32 bytes)",
112
+ });
@@ -0,0 +1,167 @@
1
+ import { describe, expect, it } from "@effect/vitest";
2
+ import { TreeFormatter } from "effect/ParseResult";
3
+ import * as S from "effect/Schema";
4
+ import * as PrivateKey from "./index.js";
5
+
6
+ const formatError = TreeFormatter.formatErrorSync;
7
+
8
+ describe("PrivateKey parse error messages", () => {
9
+ describe("PrivateKey.Hex", () => {
10
+ it("shows helpful error for wrong length (too short)", () => {
11
+ const result = S.decodeUnknownEither(PrivateKey.Hex)("0x1234");
12
+
13
+ expect(result._tag).toBe("Left");
14
+ if (result._tag === "Left") {
15
+ const formatted = formatError(result.left);
16
+
17
+ // Should mention expected length
18
+ expect(formatted).toContain("64");
19
+
20
+ // Should NOT expose the attempted value
21
+ expect(formatted).not.toContain("1234");
22
+
23
+ expect(formatted).toMatchInlineSnapshot(`"Invalid private key: expected 64 hex characters (32 bytes)"`);
24
+ }
25
+ });
26
+
27
+ it("shows helpful error for invalid hex characters", () => {
28
+ const result = S.decodeUnknownEither(PrivateKey.Hex)("0x" + "gg".repeat(32));
29
+
30
+ expect(result._tag).toBe("Left");
31
+ if (result._tag === "Left") {
32
+ const formatted = formatError(result.left);
33
+
34
+ // Should NOT expose the invalid input
35
+ expect(formatted).not.toContain("gggg");
36
+
37
+ expect(formatted).toMatchInlineSnapshot(`"Invalid private key: expected 64 hex characters (32 bytes)"`);
38
+ }
39
+ });
40
+
41
+ it("shows helpful error for wrong type (number)", () => {
42
+ const result = S.decodeUnknownEither(PrivateKey.Hex)(12345);
43
+
44
+ expect(result._tag).toBe("Left");
45
+ if (result._tag === "Left") {
46
+ const formatted = formatError(result.left);
47
+
48
+ expect(formatted).toMatchInlineSnapshot(`
49
+ "PrivateKey.Hex
50
+ └─ Encoded side transformation failure
51
+ └─ Expected string, actual 12345"
52
+ `);
53
+ }
54
+ });
55
+
56
+ it("shows helpful error for null", () => {
57
+ const result = S.decodeUnknownEither(PrivateKey.Hex)(null);
58
+
59
+ expect(result._tag).toBe("Left");
60
+ if (result._tag === "Left") {
61
+ const formatted = formatError(result.left);
62
+
63
+ expect(formatted).toContain("Expected");
64
+ expect(formatted).toMatchInlineSnapshot(`
65
+ "PrivateKey.Hex
66
+ └─ Encoded side transformation failure
67
+ └─ Expected string, actual null"
68
+ `);
69
+ }
70
+ });
71
+
72
+ it("does not leak key material in errors for almost valid key", () => {
73
+ // 62 valid hex chars + 2 invalid chars - almost looks like a real key
74
+ const almostValidKey = "0x" + "ab".repeat(31) + "xx";
75
+ const result = S.decodeUnknownEither(PrivateKey.Hex)(almostValidKey);
76
+
77
+ expect(result._tag).toBe("Left");
78
+ if (result._tag === "Left") {
79
+ const formatted = formatError(result.left);
80
+
81
+ // Error should NOT contain ANY of the key material
82
+ expect(formatted).not.toContain("abab");
83
+ expect(formatted).not.toContain("ab".repeat(31));
84
+ expect(formatted).not.toContain(almostValidKey);
85
+ expect(formatted).not.toContain(almostValidKey.slice(2)); // without 0x
86
+ }
87
+ });
88
+ });
89
+
90
+ describe("PrivateKey.Bytes", () => {
91
+ it("shows helpful error for wrong length (too short)", () => {
92
+ const bytes = new Uint8Array(16);
93
+ const result = S.decodeUnknownEither(PrivateKey.Bytes)(bytes);
94
+
95
+ expect(result._tag).toBe("Left");
96
+ if (result._tag === "Left") {
97
+ const formatted = formatError(result.left);
98
+
99
+ // Should mention expected length
100
+ expect(formatted).toContain("32");
101
+
102
+ expect(formatted).toMatchInlineSnapshot(`
103
+ "PrivateKey.Bytes
104
+ └─ Transformation process failure
105
+ └─ Private key must be 32 bytes, got 16
106
+
107
+ Docs: https://voltaire.dev/primitives/private-key"
108
+ `);
109
+ }
110
+ });
111
+
112
+ it("shows helpful error for wrong type (not Uint8Array)", () => {
113
+ const result = S.decodeUnknownEither(PrivateKey.Bytes)([1, 2, 3]);
114
+
115
+ expect(result._tag).toBe("Left");
116
+ if (result._tag === "Left") {
117
+ const formatted = formatError(result.left);
118
+
119
+ expect(formatted).toMatchInlineSnapshot(`
120
+ "PrivateKey.Bytes
121
+ └─ Encoded side transformation failure
122
+ └─ Expected Uint8ArrayFromSelf, actual [1,2,3]"
123
+ `);
124
+ }
125
+ });
126
+ });
127
+
128
+ describe("PrivateKey.RedactedHex", () => {
129
+ it("does not leak key material for invalid input", () => {
130
+ const almostValidKey = "0x" + "cd".repeat(31) + "zz";
131
+ const result = S.decodeUnknownEither(PrivateKey.RedactedHex)(almostValidKey);
132
+
133
+ expect(result._tag).toBe("Left");
134
+ if (result._tag === "Left") {
135
+ const formatted = formatError(result.left);
136
+
137
+ // Redacted schema should be extra careful
138
+ expect(formatted).not.toContain("cdcd");
139
+ expect(formatted).not.toContain(almostValidKey);
140
+
141
+ expect(formatted).toMatchInlineSnapshot(`"Invalid private key: expected 64 hex characters (32 bytes)"`);
142
+ }
143
+ });
144
+ });
145
+
146
+ describe("PrivateKey.RedactedBytes", () => {
147
+ it("shows helpful error for wrong length", () => {
148
+ const bytes = new Uint8Array(31);
149
+ const result = S.decodeUnknownEither(PrivateKey.RedactedBytes)(bytes);
150
+
151
+ expect(result._tag).toBe("Left");
152
+ if (result._tag === "Left") {
153
+ const formatted = formatError(result.left);
154
+
155
+ expect(formatted).toMatchInlineSnapshot(`
156
+ "PrivateKey.RedactedBytes
157
+ └─ Encoded side transformation failure
158
+ └─ PrivateKey.Bytes
159
+ └─ Transformation process failure
160
+ └─ Private key must be 32 bytes, got 31
161
+
162
+ Docs: https://voltaire.dev/primitives/private-key"
163
+ `);
164
+ }
165
+ });
166
+ });
167
+ });
@@ -3,6 +3,7 @@ import { Hash } from "@tevm/voltaire";
3
3
  import { PrivateKey as CorePrivateKey } from "@tevm/voltaire/PrivateKey";
4
4
  import * as Secp256k1 from "@tevm/voltaire/Secp256k1";
5
5
  import * as Effect from "effect/Effect";
6
+ import { Redacted } from "effect";
6
7
  import * as S from "effect/Schema";
7
8
  import * as PrivateKey from "./index.js";
8
9
 
@@ -373,6 +374,84 @@ describe("cryptographic operations", () => {
373
374
  });
374
375
  });
375
376
 
377
+ describe("PrivateKey.RedactedHex", () => {
378
+ it("decodes to Redacted type", () => {
379
+ const pk = S.decodeSync(PrivateKey.RedactedHex)(validHex);
380
+ expect(Redacted.isRedacted(pk)).toBe(true);
381
+ });
382
+
383
+ it("does not expose value when logged", () => {
384
+ const pk = S.decodeSync(PrivateKey.RedactedHex)(validHex);
385
+ expect(String(pk)).toBe("<redacted>");
386
+ expect(String(pk)).not.toContain("ab");
387
+ });
388
+
389
+ it("allows explicit unwrap", () => {
390
+ const pk = S.decodeSync(PrivateKey.RedactedHex)(validHex);
391
+ const unwrapped = Redacted.value(pk);
392
+ expect(unwrapped).toBeInstanceOf(Uint8Array);
393
+ expect(unwrapped.length).toBe(32);
394
+ });
395
+
396
+ it("round-trips correctly", () => {
397
+ const pk = S.decodeSync(PrivateKey.RedactedHex)(validHex);
398
+ const encoded = S.encodeSync(PrivateKey.RedactedHex)(pk);
399
+ expect(encoded).toBe(validHex.toLowerCase());
400
+ });
401
+
402
+ it("works with cryptographic operations after unwrap", () => {
403
+ const pk = S.decodeSync(PrivateKey.RedactedHex)(validHex);
404
+ const unwrapped = Redacted.value(pk);
405
+ const publicKey = Secp256k1.derivePublicKey(unwrapped);
406
+ expect(publicKey.length).toBe(64);
407
+ });
408
+
409
+ it("fails on invalid hex", () => {
410
+ expect(() => S.decodeSync(PrivateKey.RedactedHex)("0x1234")).toThrow();
411
+ });
412
+
413
+ it("parses without prefix", () => {
414
+ const pk = S.decodeSync(PrivateKey.RedactedHex)(validHexNoPrefix);
415
+ expect(Redacted.isRedacted(pk)).toBe(true);
416
+ expect(Redacted.value(pk).length).toBe(32);
417
+ });
418
+ });
419
+
420
+ describe("PrivateKey.RedactedBytes", () => {
421
+ it("decodes to Redacted type", () => {
422
+ const bytes = new Uint8Array(32).fill(0xab);
423
+ const pk = S.decodeSync(PrivateKey.RedactedBytes)(bytes);
424
+ expect(Redacted.isRedacted(pk)).toBe(true);
425
+ });
426
+
427
+ it("does not expose value when logged", () => {
428
+ const bytes = new Uint8Array(32).fill(0xab);
429
+ const pk = S.decodeSync(PrivateKey.RedactedBytes)(bytes);
430
+ expect(String(pk)).toBe("<redacted>");
431
+ });
432
+
433
+ it("allows explicit unwrap", () => {
434
+ const bytes = new Uint8Array(32).fill(0xab);
435
+ const pk = S.decodeSync(PrivateKey.RedactedBytes)(bytes);
436
+ const unwrapped = Redacted.value(pk);
437
+ expect(unwrapped).toBeInstanceOf(Uint8Array);
438
+ expect(unwrapped.length).toBe(32);
439
+ });
440
+
441
+ it("round-trips correctly", () => {
442
+ const bytes = new Uint8Array(32).fill(0xab);
443
+ const pk = S.decodeSync(PrivateKey.RedactedBytes)(bytes);
444
+ const encoded = S.encodeSync(PrivateKey.RedactedBytes)(pk);
445
+ expect([...encoded]).toEqual([...bytes]);
446
+ });
447
+
448
+ it("fails on wrong length", () => {
449
+ expect(() =>
450
+ S.decodeSync(PrivateKey.RedactedBytes)(new Uint8Array(31)),
451
+ ).toThrow();
452
+ });
453
+ });
454
+
376
455
  describe("secp256k1 curve constraints", () => {
377
456
  it("rejects zero private key", () => {
378
457
  const zeroKey = new Uint8Array(32);
@@ -2,61 +2,53 @@
2
2
  * @module PrivateKey
3
3
  * @description Effect Schemas for secp256k1 private keys with cryptographic operations.
4
4
  *
5
- * ## Type Declarations
6
- *
7
- * ```typescript
8
- * import * as PrivateKey from 'voltaire-effect/primitives/PrivateKey'
9
- *
10
- * function signMessage(key: PrivateKey.PrivateKeyType) {
11
- * // ...
12
- * }
13
- * ```
14
- *
15
5
  * ## Schemas
16
6
  *
17
- * | Schema | Input | Output |
18
- * |--------|-------|--------|
19
- * | `PrivateKey.Hex` | hex string | PrivateKeyType |
20
- * | `PrivateKey.Bytes` | Uint8Array | PrivateKeyType |
7
+ * | Schema | Input | Output | Use Case |
8
+ * |--------|-------|--------|----------|
9
+ * | `PrivateKey.Hex` | hex string | PrivateKeyType | Development |
10
+ * | `PrivateKey.Bytes` | Uint8Array | PrivateKeyType | Development |
11
+ * | `PrivateKey.RedactedHex` | hex string | Redacted<PrivateKeyType> | **Production** |
12
+ * | `PrivateKey.RedactedBytes` | Uint8Array | Redacted<PrivateKeyType> | **Production** |
13
+ *
14
+ * ## Redacted Schemas (Recommended)
21
15
  *
22
- * ## Usage
16
+ * Use `RedactedHex` or `RedactedBytes` in production to prevent accidental logging:
23
17
  *
24
18
  * ```typescript
25
19
  * import * as PrivateKey from 'voltaire-effect/primitives/PrivateKey'
20
+ * import { Redacted } from 'effect'
26
21
  * import * as S from 'effect/Schema'
27
22
  *
28
- * // Decode (parse input)
29
- * const pk = S.decodeSync(PrivateKey.Hex)('0x0123456789abcdef...')
23
+ * const pk = S.decodeSync(PrivateKey.RedactedHex)('0x0123...')
24
+ * console.log(pk) // Redacted(<redacted>)
30
25
  *
31
- * // Encode (format output)
32
- * const hex = S.encodeSync(PrivateKey.Hex)(pk)
33
- * const bytes = S.encodeSync(PrivateKey.Bytes)(pk)
26
+ * // Explicit unwrap for crypto operations
27
+ * const unwrapped = Redacted.value(pk)
28
+ * const sig = Secp256k1.sign(hash, unwrapped)
34
29
  * ```
35
30
  *
36
- * ## Cryptographic Operations
31
+ * ## Standard Usage
37
32
  *
38
33
  * ```typescript
39
- * import * as Effect from 'effect/Effect'
40
- *
41
- * const program = Effect.gen(function* () {
42
- * const pk = yield* PrivateKey.random()
43
- * const publicKey = yield* PrivateKey.toPublicKey(pk)
44
- * const address = yield* PrivateKey.toAddress(pk)
45
- * const signature = yield* PrivateKey.sign(pk, messageHash)
46
- * return { publicKey, address, signature }
47
- * })
34
+ * import * as PrivateKey from 'voltaire-effect/primitives/PrivateKey'
35
+ * import * as S from 'effect/Schema'
36
+ *
37
+ * const pk = S.decodeSync(PrivateKey.Hex)('0x0123456789abcdef...')
38
+ * const hex = S.encodeSync(PrivateKey.Hex)(pk)
48
39
  * ```
49
40
  *
50
41
  * ## Pure Functions
51
42
  *
52
43
  * ```typescript
53
44
  * PrivateKey.isValid(value) // Effect<boolean>
45
+ * PrivateKey.random() // Effect<PrivateKeyType>
54
46
  * ```
55
47
  *
56
48
  * @since 0.1.0
57
49
  */
58
50
 
59
- export { Bytes } from "./Bytes.js";
60
- export { Hex } from "./Hex.js";
51
+ export { Bytes, RedactedBytes } from "./Bytes.js";
52
+ export { Hex, RedactedHex } from "./Hex.js";
61
53
  export { isValid } from "./isValid.js";
62
54
  export { random } from "./random.js";