voltaire-effect 0.3.0 → 1.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.
Files changed (88) hide show
  1. package/dist/{X25519Test-D5Q-5fL9.d.ts → X25519Test-avt1DUgp.d.ts} +231 -6
  2. package/dist/crypto/index.d.ts +2 -2
  3. package/dist/crypto/index.js +72 -2
  4. package/dist/{index-3UKSP3cd.d.ts → index-DxwZo3xo.d.ts} +7781 -5513
  5. package/dist/index.d.ts +990 -3096
  6. package/dist/index.js +2374 -1652
  7. package/dist/native/index.d.ts +6 -6
  8. package/dist/native/index.js +2399 -1677
  9. package/dist/primitives/index.d.ts +7 -6
  10. package/dist/primitives/index.js +2966 -2361
  11. package/dist/services/index.d.ts +1631 -1255
  12. package/dist/services/index.js +4493 -3977
  13. package/package.json +7 -3
  14. package/src/crypto/Signers/SignersService.ts +1 -1
  15. package/src/crypto/Signers/errors.ts +29 -0
  16. package/src/crypto/Signers/index.ts +1 -0
  17. package/src/crypto/Signers/operations.ts +26 -8
  18. package/src/crypto/index.ts +10 -11
  19. package/src/index.ts +1 -2
  20. package/src/jsonrpc/Anvil.ts +13 -8
  21. package/src/jsonrpc/Eth.ts +13 -8
  22. package/src/jsonrpc/Hardhat.ts +13 -8
  23. package/src/jsonrpc/IdCounter.ts +21 -5
  24. package/src/jsonrpc/JsonRpc.test.ts +126 -61
  25. package/src/jsonrpc/Net.ts +13 -8
  26. package/src/jsonrpc/Request.ts +16 -8
  27. package/src/jsonrpc/Txpool.ts +13 -8
  28. package/src/jsonrpc/Wallet.ts +13 -8
  29. package/src/jsonrpc/Web3.ts +13 -8
  30. package/src/jsonrpc/index.ts +1 -1
  31. package/src/primitives/Abi/AbiSchema.ts +3 -4
  32. package/src/primitives/Abi/fromBytecode.test.ts +47 -0
  33. package/src/primitives/Abi/fromBytecode.ts +81 -0
  34. package/src/primitives/Abi/index.ts +3 -0
  35. package/src/primitives/AccessList/from.ts +12 -9
  36. package/src/primitives/Address/Checksummed.ts +21 -27
  37. package/src/primitives/Address/from.ts +12 -15
  38. package/src/primitives/Address/toHex.ts +2 -1
  39. package/src/primitives/Base64/from.ts +21 -4
  40. package/src/primitives/Blob/from.ts +12 -4
  41. package/src/primitives/BlockHash/index.ts +2 -2
  42. package/src/primitives/BlockNumber/index.ts +3 -3
  43. package/src/primitives/Bytecode/from.ts +11 -2
  44. package/src/primitives/ContractSignature/verifySignature.ts +3 -5
  45. package/src/primitives/Ens/from.ts +12 -11
  46. package/src/primitives/Hex/from.ts +12 -4
  47. package/src/primitives/Signature/from.ts +11 -2
  48. package/src/primitives/Transaction/EIP2930/index.ts +12 -12
  49. package/src/primitives/Transaction/EIP4844/index.ts +14 -14
  50. package/src/primitives/Transaction/EIP7702/index.ts +13 -13
  51. package/src/primitives/Transaction/Legacy/index.ts +13 -13
  52. package/src/primitives/TransactionHash/index.ts +3 -2
  53. package/src/primitives/TransactionIndex/index.ts +2 -2
  54. package/src/primitives/Trie/Trie.test.ts +70 -0
  55. package/src/primitives/Trie/TrieSchema.ts +26 -0
  56. package/src/primitives/Trie/clear.ts +16 -0
  57. package/src/primitives/Trie/del.ts +18 -0
  58. package/src/primitives/Trie/get.ts +18 -0
  59. package/src/primitives/Trie/index.ts +30 -0
  60. package/src/primitives/Trie/init.ts +13 -0
  61. package/src/primitives/Trie/prove.ts +19 -0
  62. package/src/primitives/Trie/put.ts +20 -0
  63. package/src/primitives/Trie/rootHash.ts +14 -0
  64. package/src/primitives/Trie/verify.ts +18 -0
  65. package/src/primitives/Uint/from.ts +11 -2
  66. package/src/primitives/Uint16/index.ts +5 -4
  67. package/src/primitives/Uint64/index.ts +5 -4
  68. package/src/primitives/Uint8/index.ts +5 -4
  69. package/src/primitives/index.ts +3 -2
  70. package/src/services/BlockExplorerApi/BlockExplorerApi.test.ts +789 -0
  71. package/src/services/BlockExplorerApi/BlockExplorerApi.ts +797 -0
  72. package/src/services/BlockExplorerApi/BlockExplorerApiErrors.ts +176 -0
  73. package/src/services/BlockExplorerApi/BlockExplorerApiService.ts +60 -0
  74. package/src/services/BlockExplorerApi/BlockExplorerApiTypes.ts +225 -0
  75. package/src/services/BlockExplorerApi/index.ts +42 -0
  76. package/src/services/Contract/Contract.test.ts +2 -6
  77. package/src/services/Contract/ContractTypes.ts +26 -8
  78. package/src/services/Contract/estimateGas.test.ts +4 -7
  79. package/src/services/Provider/actions/multicall.ts +28 -9
  80. package/src/services/Provider/actions/readContract.test.ts +8 -11
  81. package/src/services/Provider/actions/readContract.ts +28 -9
  82. package/src/services/Provider/functions/getBlock.ts +2 -1
  83. package/src/services/Provider/functions/getBlockReceipts.ts +2 -1
  84. package/src/services/Provider/functions/getBlockTransactionCount.ts +2 -1
  85. package/src/services/Provider/functions/getUncle.ts +2 -1
  86. package/src/services/Provider/functions/getUncleCount.ts +2 -1
  87. package/src/services/Signer/actions/deployContract.ts +1 -1
  88. package/src/services/index.ts +25 -0
@@ -1,16 +1,21 @@
1
- import { nextId } from "./IdCounter.js";
1
+ import * as Effect from "effect/Effect";
2
+ import { IdCounterService } from "./IdCounter.js";
2
3
  import type { JsonRpcRequestType } from "./Request.js";
3
4
 
4
5
  function makeRequest(
5
6
  method: string,
6
7
  params: unknown[] = [],
7
- ): JsonRpcRequestType {
8
- return {
9
- jsonrpc: "2.0",
10
- method,
11
- params,
12
- id: nextId(),
13
- };
8
+ ): Effect.Effect<JsonRpcRequestType, never, IdCounterService> {
9
+ return Effect.gen(function* () {
10
+ const counter = yield* IdCounterService;
11
+ const id = yield* counter.next();
12
+ return {
13
+ jsonrpc: "2.0",
14
+ method,
15
+ params,
16
+ id,
17
+ };
18
+ });
14
19
  }
15
20
 
16
21
  export const StatusRequest = () => makeRequest("txpool_status");
@@ -1,16 +1,21 @@
1
- import { nextId } from "./IdCounter.js";
1
+ import * as Effect from "effect/Effect";
2
+ import { IdCounterService } from "./IdCounter.js";
2
3
  import type { JsonRpcRequestType } from "./Request.js";
3
4
 
4
5
  function makeRequest(
5
6
  method: string,
6
7
  params: unknown[] = [],
7
- ): JsonRpcRequestType {
8
- return {
9
- jsonrpc: "2.0",
10
- method,
11
- params,
12
- id: nextId(),
13
- };
8
+ ): Effect.Effect<JsonRpcRequestType, never, IdCounterService> {
9
+ return Effect.gen(function* () {
10
+ const counter = yield* IdCounterService;
11
+ const id = yield* counter.next();
12
+ return {
13
+ jsonrpc: "2.0",
14
+ method,
15
+ params,
16
+ id,
17
+ };
18
+ });
14
19
  }
15
20
 
16
21
  export const SwitchEthereumChainRequest = (chainId: string) =>
@@ -1,16 +1,21 @@
1
- import { nextId } from "./IdCounter.js";
1
+ import * as Effect from "effect/Effect";
2
+ import { IdCounterService } from "./IdCounter.js";
2
3
  import type { JsonRpcRequestType } from "./Request.js";
3
4
 
4
5
  function makeRequest(
5
6
  method: string,
6
7
  params: unknown[] = [],
7
- ): JsonRpcRequestType {
8
- return {
9
- jsonrpc: "2.0",
10
- method,
11
- params,
12
- id: nextId(),
13
- };
8
+ ): Effect.Effect<JsonRpcRequestType, never, IdCounterService> {
9
+ return Effect.gen(function* () {
10
+ const counter = yield* IdCounterService;
11
+ const id = yield* counter.next();
12
+ return {
13
+ jsonrpc: "2.0",
14
+ method,
15
+ params,
16
+ id,
17
+ };
18
+ });
14
19
  }
15
20
 
16
21
  export const ClientVersionRequest = () => makeRequest("web3_clientVersion");
@@ -85,7 +85,7 @@ export {
85
85
  UserRejectedRequestError,
86
86
  } from "./errors.js";
87
87
  export * as Hardhat from "./Hardhat.js";
88
- export { nextId, resetId } from "./IdCounter.js";
88
+ export { IdCounterService, IdCounterLive } from "./IdCounter.js";
89
89
  export * as Net from "./Net.js";
90
90
  export {
91
91
  from as requestFrom,
@@ -12,9 +12,8 @@ import * as S from "effect/Schema";
12
12
 
13
13
  /**
14
14
  * Type alias for Abi instances returned by the Abi factory.
15
- * @internal
16
15
  */
17
- type AbiInstance = ReturnType<typeof Abi>;
16
+ export type AbiInstance = ReturnType<typeof Abi>;
18
17
 
19
18
  // =============================================================================
20
19
  // StateMutability Schema
@@ -444,7 +443,7 @@ const AbiInstanceGuardSchema = S.declare<AbiInstance>(
444
443
  *
445
444
  * @since 0.1.0
446
445
  */
447
- export const AbiSchema = AbiInstanceGuardSchema;
446
+ export const AbiSchema: S.Schema<AbiInstance, AbiInstance> = AbiInstanceGuardSchema;
448
447
 
449
448
  /**
450
449
  * Schema that parses raw ABI arrays into Abi instances.
@@ -464,7 +463,7 @@ export const AbiSchema = AbiInstanceGuardSchema;
464
463
  *
465
464
  * @since 0.1.0
466
465
  */
467
- export const fromArray = S.transformOrFail(
466
+ export const fromArray: S.Schema<AbiInstance, S.Schema.Encoded<typeof AbiSchemaInternal>> = S.transformOrFail(
468
467
  AbiSchemaInternal,
469
468
  AbiInstanceGuardSchema,
470
469
  {
@@ -0,0 +1,47 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Data from "effect/Data";
4
+
5
+ // Note: The actual fromBytecode function imports from @shazow/whatsabi which
6
+ // requires @noble/hashes@^1, but this repo uses @noble/hashes@^2.
7
+ // We test the error type directly here without importing the problematic module.
8
+
9
+ // Duplicate the error class for testing purposes
10
+ class AbiBytecodeError extends Data.TaggedError("AbiBytecodeError")<{
11
+ readonly message: string;
12
+ readonly bytecode?: string;
13
+ readonly cause?: unknown;
14
+ }> {}
15
+
16
+ describe("fromBytecode", () => {
17
+ describe("error typing", () => {
18
+ it("AbiBytecodeError has correct tag", () => {
19
+ const error = new AbiBytecodeError({
20
+ message: "test error",
21
+ bytecode: "0x123",
22
+ });
23
+ expect(error._tag).toBe("AbiBytecodeError");
24
+ });
25
+
26
+ it("can be caught with Effect.catchTag", async () => {
27
+ const program = Effect.fail(
28
+ new AbiBytecodeError({ message: "test" })
29
+ ).pipe(
30
+ Effect.catchTag("AbiBytecodeError", (e) =>
31
+ Effect.succeed({ caught: true, message: e.message })
32
+ )
33
+ );
34
+
35
+ const result = await Effect.runPromise(program);
36
+ expect(result).toEqual({ caught: true, message: "test" });
37
+ });
38
+
39
+ it("preserves bytecode context in error", () => {
40
+ const error = new AbiBytecodeError({
41
+ message: "test error",
42
+ bytecode: "0x608060",
43
+ });
44
+ expect(error.bytecode).toBe("0x608060");
45
+ });
46
+ });
47
+ });
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @fileoverview Recovers ABI from EVM bytecode using static analysis.
3
+ * Uses WhatsABI to analyze bytecode and extract function/event signatures.
4
+ *
5
+ * @module Abi/fromBytecode
6
+ * @since 0.0.1
7
+ */
8
+
9
+ import { abiFromBytecode as whatsabiFromBytecode } from "@shazow/whatsabi";
10
+ import * as Data from "effect/Data";
11
+ import * as Effect from "effect/Effect";
12
+
13
+ /**
14
+ * Error thrown when bytecode analysis fails.
15
+ */
16
+ export class AbiBytecodeError extends Data.TaggedError("AbiBytecodeError")<{
17
+ readonly message: string;
18
+ readonly bytecode?: string;
19
+ readonly cause?: unknown;
20
+ }> {}
21
+
22
+ /**
23
+ * Recovered ABI item from bytecode analysis.
24
+ * Note: Names may be missing or placeholder values since they're not in bytecode.
25
+ */
26
+ export type RecoveredAbiItem = {
27
+ readonly type: "function" | "event";
28
+ readonly selector?: string;
29
+ readonly name?: string;
30
+ readonly inputs?: ReadonlyArray<{ type: string; name: string }>;
31
+ readonly outputs?: ReadonlyArray<{ type: string; name: string }>;
32
+ readonly stateMutability?: "nonpayable" | "payable" | "view" | "pure";
33
+ };
34
+
35
+ /**
36
+ * Recovers an ABI from EVM bytecode using static analysis.
37
+ *
38
+ * @description
39
+ * Analyzes EVM bytecode to extract function selectors and attempt to
40
+ * recover function/event signatures. This is useful when source code
41
+ * is not available or the contract is not verified on any block explorer.
42
+ *
43
+ * Note that recovered ABIs may be incomplete:
44
+ * - Function/event names may be missing (only selectors available)
45
+ * - Parameter names are typically placeholders
46
+ * - Some functions may not be detected
47
+ *
48
+ * @param {string} bytecode - The EVM bytecode as hex string (with or without 0x prefix).
49
+ * @returns {Effect.Effect<ReadonlyArray<RecoveredAbiItem>, AbiBytecodeError>}
50
+ * Effect yielding the recovered ABI items.
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * import * as Effect from 'effect/Effect'
55
+ * import { fromBytecode } from 'voltaire-effect/primitives/Abi'
56
+ *
57
+ * const bytecode = '0x608060405234801561001057...'
58
+ * const abi = await Effect.runPromise(fromBytecode(bytecode))
59
+ * // => [{ type: 'function', selector: '0xa9059cbb', ... }]
60
+ * ```
61
+ *
62
+ * @since 0.0.1
63
+ */
64
+ export const fromBytecode = (
65
+ bytecode: string,
66
+ ): Effect.Effect<ReadonlyArray<RecoveredAbiItem>, AbiBytecodeError> =>
67
+ Effect.try({
68
+ try: () => {
69
+ if (!bytecode || bytecode === "0x" || bytecode === "0x0") {
70
+ return [] as ReadonlyArray<RecoveredAbiItem>;
71
+ }
72
+ const result = whatsabiFromBytecode(bytecode);
73
+ return result as unknown as ReadonlyArray<RecoveredAbiItem>;
74
+ },
75
+ catch: (e) =>
76
+ new AbiBytecodeError({
77
+ message: `Failed to recover ABI from bytecode: ${e instanceof Error ? e.message : String(e)}`,
78
+ bytecode: bytecode.slice(0, 100) + (bytecode.length > 100 ? "..." : ""),
79
+ cause: e,
80
+ }),
81
+ });
@@ -77,3 +77,6 @@ export { getSelector } from "./getSelector.js";
77
77
  export { AbiParseError, parse } from "./parse.js";
78
78
  export { AbiItemParseError, parseItem } from "./parseItem.js";
79
79
  export { parseLogs, type ParsedLog, type ParseLogsInput } from "./parseLogs.js";
80
+
81
+ // Bytecode analysis
82
+ export { AbiBytecodeError, fromBytecode, type RecoveredAbiItem } from "./fromBytecode.js";
@@ -9,18 +9,13 @@ import {
9
9
  type BrandedAccessList,
10
10
  type Item,
11
11
  } from "@tevm/voltaire/AccessList";
12
- import type {
13
- InvalidFormatError,
14
- InvalidLengthError,
15
- } from "@tevm/voltaire/errors";
16
-
17
- type AccessListError = InvalidFormatError | InvalidLengthError;
12
+ import { ValidationError } from "@tevm/voltaire/errors";
18
13
 
19
14
  /**
20
15
  * Create AccessList from array or bytes
21
16
  *
22
17
  * @param value - AccessList items or RLP bytes
23
- * @returns Effect yielding BrandedAccessList or failing with AccessListError
18
+ * @returns Effect yielding BrandedAccessList or failing with ValidationError
24
19
  * @example
25
20
  * ```typescript
26
21
  * import * as AccessList from 'voltaire-effect/primitives/AccessList'
@@ -32,8 +27,16 @@ type AccessListError = InvalidFormatError | InvalidLengthError;
32
27
  */
33
28
  export const from = (
34
29
  value: readonly Item[] | Uint8Array,
35
- ): Effect.Effect<BrandedAccessList, AccessListError> =>
30
+ ): Effect.Effect<BrandedAccessList, ValidationError> =>
36
31
  Effect.try({
37
32
  try: () => AccessList.from(value),
38
- catch: (e) => e as AccessListError,
33
+ catch: (error) =>
34
+ new ValidationError(
35
+ error instanceof Error ? error.message : "Invalid access list input",
36
+ {
37
+ value,
38
+ expected: "array of {address, storageKeys} or RLP-encoded bytes",
39
+ cause: error instanceof Error ? error : undefined,
40
+ },
41
+ ),
39
42
  });
@@ -7,10 +7,9 @@
7
7
  */
8
8
 
9
9
  import { Address, type AddressType } from "@tevm/voltaire/Address";
10
- import * as Effect from "effect/Effect";
10
+ import { hash as keccak256 } from "@tevm/voltaire/Keccak256";
11
11
  import * as ParseResult from "effect/ParseResult";
12
12
  import * as S from "effect/Schema";
13
- import { KeccakService } from "../../crypto/Keccak256/index.js";
14
13
  import { AddressTypeSchema } from "./AddressSchema.js";
15
14
 
16
15
  /**
@@ -36,22 +35,19 @@ import { AddressTypeSchema } from "./AddressSchema.js";
36
35
  * S.decodeSync(Address.Checksummed)('0x742d35cc6634c0532925a3b844bc9e7595f251e3') // Error!
37
36
  * ```
38
37
  *
39
- * @example Encoding (requires KeccakService)
38
+ * @example Encoding (sync)
40
39
  * ```typescript
41
40
  * import * as Address from 'voltaire-effect/primitives/Address'
42
41
  * import * as S from 'effect/Schema'
43
- * import * as Effect from 'effect/Effect'
44
- * import { KeccakLive } from 'voltaire-effect/crypto/Keccak256'
45
42
  *
46
43
  * const addr = S.decodeSync(Address.Hex)('0x742d35cc6634c0532925a3b844bc9e7595f251e3')
47
- * const program = S.encode(Address.Checksummed)(addr)
48
- * const checksummed = await Effect.runPromise(program.pipe(Effect.provide(KeccakLive)))
44
+ * const checksummed = S.encodeSync(Address.Checksummed)(addr)
49
45
  * // "0x742d35Cc6634C0532925a3b844Bc9e7595f251e3"
50
46
  * ```
51
47
  *
52
48
  * @since 0.1.0
53
49
  */
54
- export const Checksummed: S.Schema<AddressType, string, KeccakService> =
50
+ export const Checksummed: S.Schema<AddressType, string> =
55
51
  S.transformOrFail(S.String, AddressTypeSchema, {
56
52
  strict: true,
57
53
  decode: (s, _options, ast) => {
@@ -69,25 +65,23 @@ export const Checksummed: S.Schema<AddressType, string, KeccakService> =
69
65
  );
70
66
  }
71
67
  },
72
- encode: (addr, _options, _ast) =>
73
- Effect.gen(function* () {
74
- const keccak = yield* KeccakService;
75
- const hex = Address.toHex(addr).slice(2).toLowerCase();
76
- const hashResult = yield* keccak.hash(new TextEncoder().encode(hex));
77
- const hashHex = Array.from(hashResult)
78
- .map((b) => b.toString(16).padStart(2, "0"))
79
- .join("");
68
+ encode: (addr, _options, _ast) => {
69
+ const hex = Address.toHex(addr).slice(2).toLowerCase();
70
+ const hashResult = keccak256(new TextEncoder().encode(hex));
71
+ const hashHex = Array.from(hashResult as Uint8Array)
72
+ .map((b) => b.toString(16).padStart(2, "0"))
73
+ .join("");
80
74
 
81
- let checksummed = "0x";
82
- for (let i = 0; i < hex.length; i++) {
83
- const char = hex[i];
84
- if (char >= "a" && char <= "f") {
85
- const hashNibble = Number.parseInt(hashHex[i], 16);
86
- checksummed += hashNibble >= 8 ? char.toUpperCase() : char;
87
- } else {
88
- checksummed += char;
89
- }
75
+ let checksummed = "0x";
76
+ for (let i = 0; i < hex.length; i++) {
77
+ const char = hex[i];
78
+ if (char >= "a" && char <= "f") {
79
+ const hashNibble = Number.parseInt(hashHex[i], 16);
80
+ checksummed += hashNibble >= 8 ? char.toUpperCase() : char;
81
+ } else {
82
+ checksummed += char;
90
83
  }
91
- return checksummed;
92
- }),
84
+ }
85
+ return ParseResult.succeed(checksummed);
86
+ },
93
87
  }).annotations({ identifier: "Address.Checksummed" });
@@ -5,24 +5,13 @@
5
5
  */
6
6
  import { Effect } from "effect";
7
7
  import { Address, type AddressType } from "@tevm/voltaire/Address";
8
- import type {
9
- InvalidAddressError,
10
- InvalidHexFormatError,
11
- InvalidAddressLengthError,
12
- InvalidValueError,
13
- } from "@tevm/voltaire/Address";
14
-
15
- type AddressError =
16
- | InvalidAddressError
17
- | InvalidHexFormatError
18
- | InvalidAddressLengthError
19
- | InvalidValueError;
8
+ import { ValidationError } from "@tevm/voltaire/errors";
20
9
 
21
10
  /**
22
11
  * Create Address from hex string, bytes, or number
23
12
  *
24
13
  * @param value - Hex string, Uint8Array, number, or bigint
25
- * @returns Effect yielding AddressType or failing with AddressError
14
+ * @returns Effect yielding AddressType or failing with ValidationError
26
15
  * @example
27
16
  * ```typescript
28
17
  * import * as Address from 'voltaire-effect/primitives/Address'
@@ -34,8 +23,16 @@ type AddressError =
34
23
  */
35
24
  export const from = (
36
25
  value: string | Uint8Array | number | bigint,
37
- ): Effect.Effect<AddressType, AddressError> =>
26
+ ): Effect.Effect<AddressType, ValidationError> =>
38
27
  Effect.try({
39
28
  try: () => Address.from(value),
40
- catch: (e) => e as AddressError,
29
+ catch: (error) =>
30
+ new ValidationError(
31
+ error instanceof Error ? error.message : "Invalid address input",
32
+ {
33
+ value,
34
+ expected: "20-byte hex string or Uint8Array",
35
+ cause: error instanceof Error ? error : undefined,
36
+ },
37
+ ),
41
38
  });
@@ -4,6 +4,7 @@
4
4
  * @since 0.1.0
5
5
  */
6
6
  import { Address, type AddressType } from "@tevm/voltaire/Address";
7
+ import type { HexType } from "@tevm/voltaire/Hex";
7
8
 
8
9
  /**
9
10
  * Convert Address to lowercase hex string
@@ -15,4 +16,4 @@ import { Address, type AddressType } from "@tevm/voltaire/Address";
15
16
  * Address.toHex(addr) // "0x742d35cc6634c0532925a3b844bc9e7595f251e3"
16
17
  * ```
17
18
  */
18
- export const toHex = (addr: AddressType): string => Address.toHex(addr);
19
+ export const toHex = (addr: AddressType): HexType => Address.toHex(addr);
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { Effect } from "effect";
7
7
  import { Base64, type BrandedBase64, type BrandedBase64Url } from "@tevm/voltaire/Base64";
8
+ import { ValidationError } from "@tevm/voltaire/errors";
8
9
 
9
10
  /**
10
11
  * Create validated standard Base64 string from input
@@ -18,10 +19,18 @@ import { Base64, type BrandedBase64, type BrandedBase64Url } from "@tevm/voltair
18
19
  */
19
20
  export const from = (
20
21
  value: string | Uint8Array,
21
- ): Effect.Effect<BrandedBase64, Error> =>
22
+ ): Effect.Effect<BrandedBase64, ValidationError> =>
22
23
  Effect.try({
23
24
  try: () => Base64.from(value),
24
- catch: (e) => e as Error,
25
+ catch: (error) =>
26
+ new ValidationError(
27
+ error instanceof Error ? error.message : "Invalid Base64 input",
28
+ {
29
+ value,
30
+ expected: "valid Base64 string or Uint8Array",
31
+ cause: error instanceof Error ? error : undefined,
32
+ },
33
+ ),
25
34
  });
26
35
 
27
36
  /**
@@ -32,8 +41,16 @@ export const from = (
32
41
  */
33
42
  export const fromUrlSafe = (
34
43
  value: string | Uint8Array,
35
- ): Effect.Effect<BrandedBase64Url, Error> =>
44
+ ): Effect.Effect<BrandedBase64Url, ValidationError> =>
36
45
  Effect.try({
37
46
  try: () => Base64.fromUrlSafe(value),
38
- catch: (e) => e as Error,
47
+ catch: (error) =>
48
+ new ValidationError(
49
+ error instanceof Error ? error.message : "Invalid URL-safe Base64 input",
50
+ {
51
+ value,
52
+ expected: "valid URL-safe Base64 string or Uint8Array",
53
+ cause: error instanceof Error ? error : undefined,
54
+ },
55
+ ),
39
56
  });
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { BrandedBlob as BlobNamespace } from "@tevm/voltaire";
7
7
  import { Effect } from "effect";
8
- import type { InvalidBlobDataSizeError } from "@tevm/voltaire/Blob";
8
+ import { ValidationError } from "@tevm/voltaire/errors";
9
9
 
10
10
  type BrandedBlob = BlobNamespace.BrandedBlob;
11
11
 
@@ -15,7 +15,7 @@ type BrandedBlob = BlobNamespace.BrandedBlob;
15
15
  * If input is 131072 bytes, returns as-is. Otherwise encodes with padding.
16
16
  *
17
17
  * @param value - Uint8Array (either 131072 bytes blob or data to encode)
18
- * @returns Effect yielding BrandedBlob or failing with InvalidBlobDataSizeError
18
+ * @returns Effect yielding BrandedBlob or failing with ValidationError
19
19
  * @example
20
20
  * ```typescript
21
21
  * import * as Blob from 'voltaire-effect/primitives/Blob'
@@ -31,8 +31,16 @@ type BrandedBlob = BlobNamespace.BrandedBlob;
31
31
  */
32
32
  export const from = (
33
33
  value: Uint8Array,
34
- ): Effect.Effect<BrandedBlob, InvalidBlobDataSizeError> =>
34
+ ): Effect.Effect<BrandedBlob, ValidationError> =>
35
35
  Effect.try({
36
36
  try: () => BlobNamespace.from(value),
37
- catch: (e) => e as InvalidBlobDataSizeError,
37
+ catch: (error) =>
38
+ new ValidationError(
39
+ error instanceof Error ? error.message : "Invalid blob input",
40
+ {
41
+ value,
42
+ expected: "Uint8Array (131072 bytes or data to encode)",
43
+ cause: error instanceof Error ? error : undefined,
44
+ },
45
+ ),
38
46
  });
@@ -47,8 +47,8 @@ export { Hex } from "./Hex.js";
47
47
  // Re-export pure functions from voltaire
48
48
  import { BlockHash } from "@tevm/voltaire";
49
49
 
50
- export const equals = BlockHash.equals;
51
- export const toHex = BlockHash.toHex;
50
+ export const equals: (a: BlockHashType, b: BlockHashType) => boolean = BlockHash.equals;
51
+ export const toHex: (hash: BlockHashType) => string = BlockHash.toHex;
52
52
 
53
53
  // Type export
54
54
  import type { BlockHash as _BlockHash } from "@tevm/voltaire";
@@ -51,9 +51,9 @@ export { Number } from "./Number.js";
51
51
  // Re-export pure functions from voltaire
52
52
  import { BlockNumber } from "@tevm/voltaire";
53
53
 
54
- export const equals = BlockNumber.equals;
55
- export const toBigInt = BlockNumber.toBigInt;
56
- export const toNumber = BlockNumber.toNumber;
54
+ export const equals: (a: BlockNumberType, b: BlockNumberType) => boolean = BlockNumber.equals;
55
+ export const toBigInt: (blockNumber: BlockNumberType) => bigint = BlockNumber.toBigInt;
56
+ export const toNumber: (blockNumber: BlockNumberType) => number = BlockNumber.toNumber;
57
57
 
58
58
  // Type export
59
59
  import type { BlockNumber as _BlockNumber } from "@tevm/voltaire";
@@ -5,12 +5,21 @@
5
5
  */
6
6
  import { Effect } from "effect";
7
7
  import { from as _from } from "@tevm/voltaire/Bytecode";
8
+ import { ValidationError } from "@tevm/voltaire/errors";
8
9
  import type { BrandedBytecode } from "./types.js";
9
10
 
10
11
  export const from = (
11
12
  value: string | Uint8Array,
12
- ): Effect.Effect<BrandedBytecode, Error> =>
13
+ ): Effect.Effect<BrandedBytecode, ValidationError> =>
13
14
  Effect.try({
14
15
  try: () => _from(value),
15
- catch: (e) => e as Error,
16
+ catch: (error) =>
17
+ new ValidationError(
18
+ error instanceof Error ? error.message : "Invalid bytecode input",
19
+ {
20
+ value,
21
+ expected: "hex string or Uint8Array",
22
+ cause: error instanceof Error ? error : undefined,
23
+ },
24
+ ),
16
25
  });
@@ -12,6 +12,7 @@ import {
12
12
  type BrandedAddress,
13
13
  type BrandedHash,
14
14
  ContractSignature,
15
+ Hex,
15
16
  Secp256k1,
16
17
  } from "@tevm/voltaire";
17
18
  import * as Data from "effect/Data";
@@ -247,11 +248,8 @@ export const verifySignature = (
247
248
  });
248
249
 
249
250
  // Build EIP-1271 isValidSignature call data
250
- const hashHex =
251
- hash instanceof Uint8Array
252
- ? `0x${Buffer.from(hash).toString("hex")}`
253
- : `0x${Buffer.from(hash).toString("hex")}`;
254
- const sigHex = `0x${Buffer.from(signatureBytes).toString("hex")}`;
251
+ const hashHex = Hex(hash);
252
+ const sigHex = Hex(signatureBytes);
255
253
 
256
254
  // isValidSignature(bytes32 hash, bytes signature) selector: 0x1626ba7e
257
255
  // ABI encode: selector + hash (32 bytes) + offset to signature (32) + signature length (32) + signature data
@@ -5,28 +5,29 @@
5
5
  */
6
6
  import { Effect } from "effect";
7
7
  import { Ens } from "@tevm/voltaire";
8
+ import { ValidationError } from "@tevm/voltaire/errors";
8
9
  import type { EnsType } from "./String.js";
9
10
 
10
- type EnsError =
11
- | Ens.DisallowedCharacterError
12
- | Ens.EmptyLabelError
13
- | Ens.IllegalMixtureError
14
- | Ens.InvalidLabelExtensionError
15
- | Ens.InvalidUtf8Error
16
- | Ens.WholeConfusableError;
17
-
18
11
  /**
19
12
  * Create ENS name from string
20
13
  *
21
14
  * @param name - ENS name string
22
- * @returns Effect yielding EnsType or failing with EnsError
15
+ * @returns Effect yielding EnsType or failing with ValidationError
23
16
  * @example
24
17
  * ```typescript
25
18
  * const ens = await Effect.runPromise(Ens.from('vitalik.eth'))
26
19
  * ```
27
20
  */
28
- export const from = (name: string): Effect.Effect<EnsType, EnsError> =>
21
+ export const from = (name: string): Effect.Effect<EnsType, ValidationError> =>
29
22
  Effect.try({
30
23
  try: () => Ens.from(name),
31
- catch: (e) => e as EnsError,
24
+ catch: (error) =>
25
+ new ValidationError(
26
+ error instanceof Error ? error.message : "Invalid ENS name",
27
+ {
28
+ value: name,
29
+ expected: "valid ENS name (e.g., vitalik.eth)",
30
+ cause: error instanceof Error ? error : undefined,
31
+ },
32
+ ),
32
33
  });