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.
- package/dist/{X25519Test-D5Q-5fL9.d.ts → X25519Test-avt1DUgp.d.ts} +231 -6
- package/dist/crypto/index.d.ts +2 -2
- package/dist/crypto/index.js +72 -2
- package/dist/{index-3UKSP3cd.d.ts → index-DxwZo3xo.d.ts} +7781 -5513
- package/dist/index.d.ts +990 -3096
- package/dist/index.js +2374 -1652
- package/dist/native/index.d.ts +6 -6
- package/dist/native/index.js +2399 -1677
- package/dist/primitives/index.d.ts +7 -6
- package/dist/primitives/index.js +2966 -2361
- package/dist/services/index.d.ts +1631 -1255
- package/dist/services/index.js +4493 -3977
- package/package.json +7 -3
- package/src/crypto/Signers/SignersService.ts +1 -1
- package/src/crypto/Signers/errors.ts +29 -0
- package/src/crypto/Signers/index.ts +1 -0
- package/src/crypto/Signers/operations.ts +26 -8
- package/src/crypto/index.ts +10 -11
- package/src/index.ts +1 -2
- package/src/jsonrpc/Anvil.ts +13 -8
- package/src/jsonrpc/Eth.ts +13 -8
- package/src/jsonrpc/Hardhat.ts +13 -8
- package/src/jsonrpc/IdCounter.ts +21 -5
- package/src/jsonrpc/JsonRpc.test.ts +126 -61
- package/src/jsonrpc/Net.ts +13 -8
- package/src/jsonrpc/Request.ts +16 -8
- package/src/jsonrpc/Txpool.ts +13 -8
- package/src/jsonrpc/Wallet.ts +13 -8
- package/src/jsonrpc/Web3.ts +13 -8
- package/src/jsonrpc/index.ts +1 -1
- package/src/primitives/Abi/AbiSchema.ts +3 -4
- package/src/primitives/Abi/fromBytecode.test.ts +47 -0
- package/src/primitives/Abi/fromBytecode.ts +81 -0
- package/src/primitives/Abi/index.ts +3 -0
- package/src/primitives/AccessList/from.ts +12 -9
- package/src/primitives/Address/Checksummed.ts +21 -27
- package/src/primitives/Address/from.ts +12 -15
- package/src/primitives/Address/toHex.ts +2 -1
- package/src/primitives/Base64/from.ts +21 -4
- package/src/primitives/Blob/from.ts +12 -4
- package/src/primitives/BlockHash/index.ts +2 -2
- package/src/primitives/BlockNumber/index.ts +3 -3
- package/src/primitives/Bytecode/from.ts +11 -2
- package/src/primitives/ContractSignature/verifySignature.ts +3 -5
- package/src/primitives/Ens/from.ts +12 -11
- package/src/primitives/Hex/from.ts +12 -4
- package/src/primitives/Signature/from.ts +11 -2
- package/src/primitives/Transaction/EIP2930/index.ts +12 -12
- package/src/primitives/Transaction/EIP4844/index.ts +14 -14
- package/src/primitives/Transaction/EIP7702/index.ts +13 -13
- package/src/primitives/Transaction/Legacy/index.ts +13 -13
- package/src/primitives/TransactionHash/index.ts +3 -2
- package/src/primitives/TransactionIndex/index.ts +2 -2
- package/src/primitives/Trie/Trie.test.ts +70 -0
- package/src/primitives/Trie/TrieSchema.ts +26 -0
- package/src/primitives/Trie/clear.ts +16 -0
- package/src/primitives/Trie/del.ts +18 -0
- package/src/primitives/Trie/get.ts +18 -0
- package/src/primitives/Trie/index.ts +30 -0
- package/src/primitives/Trie/init.ts +13 -0
- package/src/primitives/Trie/prove.ts +19 -0
- package/src/primitives/Trie/put.ts +20 -0
- package/src/primitives/Trie/rootHash.ts +14 -0
- package/src/primitives/Trie/verify.ts +18 -0
- package/src/primitives/Uint/from.ts +11 -2
- package/src/primitives/Uint16/index.ts +5 -4
- package/src/primitives/Uint64/index.ts +5 -4
- package/src/primitives/Uint8/index.ts +5 -4
- package/src/primitives/index.ts +3 -2
- package/src/services/BlockExplorerApi/BlockExplorerApi.test.ts +789 -0
- package/src/services/BlockExplorerApi/BlockExplorerApi.ts +797 -0
- package/src/services/BlockExplorerApi/BlockExplorerApiErrors.ts +176 -0
- package/src/services/BlockExplorerApi/BlockExplorerApiService.ts +60 -0
- package/src/services/BlockExplorerApi/BlockExplorerApiTypes.ts +225 -0
- package/src/services/BlockExplorerApi/index.ts +42 -0
- package/src/services/Contract/Contract.test.ts +2 -6
- package/src/services/Contract/ContractTypes.ts +26 -8
- package/src/services/Contract/estimateGas.test.ts +4 -7
- package/src/services/Provider/actions/multicall.ts +28 -9
- package/src/services/Provider/actions/readContract.test.ts +8 -11
- package/src/services/Provider/actions/readContract.ts +28 -9
- package/src/services/Provider/functions/getBlock.ts +2 -1
- package/src/services/Provider/functions/getBlockReceipts.ts +2 -1
- package/src/services/Provider/functions/getBlockTransactionCount.ts +2 -1
- package/src/services/Provider/functions/getUncle.ts +2 -1
- package/src/services/Provider/functions/getUncleCount.ts +2 -1
- package/src/services/Signer/actions/deployContract.ts +1 -1
- package/src/services/index.ts +25 -0
package/src/jsonrpc/Txpool.ts
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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");
|
package/src/jsonrpc/Wallet.ts
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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) =>
|
package/src/jsonrpc/Web3.ts
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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");
|
package/src/jsonrpc/index.ts
CHANGED
|
@@ -85,7 +85,7 @@ export {
|
|
|
85
85
|
UserRejectedRequestError,
|
|
86
86
|
} from "./errors.js";
|
|
87
87
|
export * as Hardhat from "./Hardhat.js";
|
|
88
|
-
export {
|
|
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
|
|
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
|
|
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,
|
|
30
|
+
): Effect.Effect<BrandedAccessList, ValidationError> =>
|
|
36
31
|
Effect.try({
|
|
37
32
|
try: () => AccessList.from(value),
|
|
38
|
-
catch: (
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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,
|
|
26
|
+
): Effect.Effect<AddressType, ValidationError> =>
|
|
38
27
|
Effect.try({
|
|
39
28
|
try: () => Address.from(value),
|
|
40
|
-
catch: (
|
|
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):
|
|
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,
|
|
22
|
+
): Effect.Effect<BrandedBase64, ValidationError> =>
|
|
22
23
|
Effect.try({
|
|
23
24
|
try: () => Base64.from(value),
|
|
24
|
-
catch: (
|
|
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,
|
|
44
|
+
): Effect.Effect<BrandedBase64Url, ValidationError> =>
|
|
36
45
|
Effect.try({
|
|
37
46
|
try: () => Base64.fromUrlSafe(value),
|
|
38
|
-
catch: (
|
|
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
|
|
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
|
|
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,
|
|
34
|
+
): Effect.Effect<BrandedBlob, ValidationError> =>
|
|
35
35
|
Effect.try({
|
|
36
36
|
try: () => BlobNamespace.from(value),
|
|
37
|
-
catch: (
|
|
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,
|
|
13
|
+
): Effect.Effect<BrandedBytecode, ValidationError> =>
|
|
13
14
|
Effect.try({
|
|
14
15
|
try: () => _from(value),
|
|
15
|
-
catch: (
|
|
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
|
-
|
|
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
|
|
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,
|
|
21
|
+
export const from = (name: string): Effect.Effect<EnsType, ValidationError> =>
|
|
29
22
|
Effect.try({
|
|
30
23
|
try: () => Ens.from(name),
|
|
31
|
-
catch: (
|
|
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
|
});
|