voltaire-effect 0.2.23 → 0.2.24
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/KZGService-B7PJerOb.d.ts +146 -0
- package/dist/{X25519Test-DGsk1V9o.d.ts → X25519Test-C8Cicdd_.d.ts} +2 -139
- package/dist/crypto/index.d.ts +3 -2
- package/dist/{index-IgkEHjBe.d.ts → index-DsFjN_a3.d.ts} +17633 -11961
- package/dist/index.d.ts +3104 -938
- package/dist/index.js +4467 -2446
- package/dist/native/index.d.ts +14 -5
- package/dist/native/index.js +4489 -2468
- package/dist/primitives/index.d.ts +12 -4
- package/dist/primitives/index.js +3980 -2411
- package/package.json +2 -2
- package/src/crypto/Keccak256/index.ts +1 -0
- package/src/index.ts +2 -2
- package/src/primitives/Abi/decode.test.ts +38 -0
- package/src/primitives/Abi/decode.ts +67 -0
- package/src/primitives/Abi/decodeData.test.ts +37 -0
- package/src/primitives/Abi/decodeData.ts +77 -0
- package/src/primitives/Abi/decodeLog.test.ts +32 -0
- package/src/primitives/Abi/decodeLog.ts +81 -0
- package/src/primitives/Abi/decodeWrappedError.test.ts +41 -0
- package/src/primitives/Abi/decodeWrappedError.ts +68 -0
- package/src/primitives/Abi/encode.test.ts +43 -0
- package/src/primitives/Abi/encode.ts +67 -0
- package/src/primitives/Abi/encodePacked.test.ts +35 -0
- package/src/primitives/Abi/encodePacked.ts +62 -0
- package/src/primitives/Abi/encodeWrappedError.test.ts +21 -0
- package/src/primitives/Abi/encodeWrappedError.ts +64 -0
- package/src/primitives/Abi/findSelectorCollisions.test.ts +50 -0
- package/src/primitives/Abi/findSelectorCollisions.ts +83 -0
- package/src/primitives/Abi/index.ts +33 -7
- package/src/primitives/Abi/parseLogs.test.ts +37 -0
- package/src/primitives/Abi/parseLogs.ts +62 -0
- package/src/primitives/AccessList/AccessList.test.ts +229 -0
- package/src/primitives/AccessList/AccessListTypeSchema.ts +16 -0
- package/src/primitives/AccessList/addressCount.ts +21 -0
- package/src/primitives/AccessList/assertValid.ts +37 -0
- package/src/primitives/AccessList/create.ts +19 -0
- package/src/primitives/AccessList/deduplicate.ts +23 -0
- package/src/primitives/AccessList/from.ts +39 -0
- package/src/primitives/AccessList/fromBytes.ts +36 -0
- package/src/primitives/AccessList/gasCost.ts +22 -0
- package/src/primitives/AccessList/gasSavings.ts +26 -0
- package/src/primitives/AccessList/hasSavings.ts +23 -0
- package/src/primitives/AccessList/includesAddress.ts +25 -0
- package/src/primitives/AccessList/includesStorageKey.ts +28 -0
- package/src/primitives/AccessList/index.ts +99 -19
- package/src/primitives/AccessList/is.ts +23 -0
- package/src/primitives/AccessList/isEmpty.ts +23 -0
- package/src/primitives/AccessList/isItem.ts +22 -0
- package/src/primitives/AccessList/keysFor.ts +29 -0
- package/src/primitives/AccessList/merge.ts +24 -0
- package/src/primitives/AccessList/storageKeyCount.ts +21 -0
- package/src/primitives/AccessList/toBytes.ts +23 -0
- package/src/primitives/AccessList/withAddress.ts +27 -0
- package/src/primitives/AccessList/withStorageKey.ts +30 -0
- package/src/primitives/Address/Address.test.ts +146 -0
- package/src/primitives/Address/assert.ts +51 -0
- package/src/primitives/Address/calculateCreate2Address.ts +33 -0
- package/src/primitives/Address/calculateCreateAddress.ts +30 -0
- package/src/primitives/Address/deduplicateAddresses.ts +20 -0
- package/src/primitives/Address/from.ts +41 -0
- package/src/primitives/Address/fromAbiEncoded.ts +35 -0
- package/src/primitives/Address/fromBase64.ts +25 -0
- package/src/primitives/Address/fromBytes.ts +25 -0
- package/src/primitives/Address/fromHex.ts +31 -0
- package/src/primitives/Address/fromNumber.ts +25 -0
- package/src/primitives/Address/fromPrivateKey.ts +25 -0
- package/src/primitives/Address/fromPublicKey.ts +56 -0
- package/src/primitives/Address/index.ts +99 -35
- package/src/primitives/Address/is.ts +20 -0
- package/src/primitives/Address/isAddress.ts +22 -0
- package/src/primitives/Address/sortAddresses.ts +19 -0
- package/src/primitives/Address/toChecksummed.ts +32 -0
- package/src/primitives/Address/toHex.ts +18 -0
- package/src/primitives/Address/zero.ts +18 -0
- package/src/primitives/Base64/Base64.test.ts +75 -0
- package/src/primitives/Base64/convert.ts +37 -0
- package/src/primitives/Base64/decode.ts +59 -0
- package/src/primitives/Base64/encode.ts +42 -0
- package/src/primitives/Base64/from.ts +39 -0
- package/src/primitives/Base64/index.ts +81 -1
- package/src/primitives/Base64/size.ts +16 -0
- package/src/primitives/Base64/validation.ts +16 -0
- package/src/primitives/Blob/Blob.test.ts +292 -0
- package/src/primitives/Blob/calculateGas.ts +30 -0
- package/src/primitives/Blob/estimateBlobCount.ts +30 -0
- package/src/primitives/Blob/from.ts +38 -0
- package/src/primitives/Blob/fromData.ts +36 -0
- package/src/primitives/Blob/index.ts +99 -12
- package/src/primitives/Blob/isValidVersion.ts +27 -0
- package/src/primitives/Blob/joinData.ts +43 -0
- package/src/primitives/Blob/splitData.ts +36 -0
- package/src/primitives/Blob/toCommitment.ts +43 -0
- package/src/primitives/Blob/toProof.ts +50 -0
- package/src/primitives/Blob/toVersionedHash.ts +35 -0
- package/src/primitives/Blob/verify.ts +49 -0
- package/src/primitives/Blob/verifyBatch.ts +119 -0
- package/src/primitives/BloomFilter/BloomFilter.test.ts +138 -0
- package/src/primitives/BloomFilter/add.ts +23 -0
- package/src/primitives/BloomFilter/combine.ts +21 -0
- package/src/primitives/BloomFilter/contains.ts +26 -0
- package/src/primitives/BloomFilter/create.ts +35 -0
- package/src/primitives/BloomFilter/density.ts +22 -0
- package/src/primitives/BloomFilter/expectedFalsePositiveRate.ts +25 -0
- package/src/primitives/BloomFilter/fromHex.ts +37 -0
- package/src/primitives/BloomFilter/hash.ts +43 -0
- package/src/primitives/BloomFilter/index.ts +59 -37
- package/src/primitives/BloomFilter/isEmpty.ts +22 -0
- package/src/primitives/BloomFilter/merge.ts +24 -0
- package/src/primitives/BloomFilter/toHex.ts +22 -0
- package/src/primitives/Bytecode/analyze.ts +58 -0
- package/src/primitives/Bytecode/detectFusions.ts +10 -0
- package/src/primitives/Bytecode/equals.ts +10 -0
- package/src/primitives/Bytecode/extractRuntime.ts +12 -0
- package/src/primitives/Bytecode/format.ts +16 -0
- package/src/primitives/Bytecode/from.ts +16 -0
- package/src/primitives/Bytecode/fromHex.ts +14 -0
- package/src/primitives/Bytecode/getBlock.ts +12 -0
- package/src/primitives/Bytecode/getNextPc.ts +12 -0
- package/src/primitives/Bytecode/getPushSize.ts +8 -0
- package/src/primitives/Bytecode/hasMetadata.ts +10 -0
- package/src/primitives/Bytecode/hash.ts +9 -0
- package/src/primitives/Bytecode/index.ts +103 -13
- package/src/primitives/Bytecode/isPush.ts +8 -0
- package/src/primitives/Bytecode/isTerminator.ts +8 -0
- package/src/primitives/Bytecode/isValidJumpDest.ts +12 -0
- package/src/primitives/Bytecode/parseInstructions.ts +10 -0
- package/src/primitives/Bytecode/prettyPrint.ts +12 -0
- package/src/primitives/Bytecode/scan.ts +20 -0
- package/src/primitives/Bytecode/size.ts +9 -0
- package/src/primitives/Bytecode/stripMetadata.ts +10 -0
- package/src/primitives/Bytecode/toAbi.ts +10 -0
- package/src/primitives/Bytecode/toHex.ts +10 -0
- package/src/primitives/Bytecode/types.ts +23 -0
- package/src/primitives/Bytecode/validate.ts +9 -0
- package/src/primitives/Ens/Ens.test.ts +71 -0
- package/src/primitives/Ens/convert.ts +13 -0
- package/src/primitives/Ens/from.ts +32 -0
- package/src/primitives/Ens/hash.ts +46 -0
- package/src/primitives/Ens/index.ts +86 -4
- package/src/primitives/Ens/normalize.ts +45 -0
- package/src/primitives/Ens/validation.ts +38 -0
- package/src/primitives/EventLog/accessors.ts +32 -0
- package/src/primitives/EventLog/clone.ts +17 -0
- package/src/primitives/EventLog/create.ts +46 -0
- package/src/primitives/EventLog/filtering.ts +48 -0
- package/src/primitives/EventLog/index.ts +96 -6
- package/src/primitives/EventLog/status.ts +17 -0
- package/src/primitives/EventLog/toRpc.ts +49 -0
- package/src/primitives/Hardfork/HardforkSchema.ts +3 -3
- package/src/primitives/Hardfork/allIds.ts +13 -0
- package/src/primitives/Hardfork/allNames.ts +13 -0
- package/src/primitives/Hardfork/compare.ts +17 -0
- package/src/primitives/Hardfork/comparisons.ts +45 -0
- package/src/primitives/Hardfork/equals.ts +17 -0
- package/src/primitives/Hardfork/features.ts +61 -0
- package/src/primitives/Hardfork/fromString.ts +16 -0
- package/src/primitives/Hardfork/index.ts +128 -18
- package/src/primitives/Hardfork/isValidName.ts +14 -0
- package/src/primitives/Hardfork/minMax.ts +23 -0
- package/src/primitives/Hardfork/range.ts +19 -0
- package/src/primitives/Hardfork/toString.ts +16 -0
- package/src/primitives/Hash/Hash.test.ts +104 -0
- package/src/primitives/Hash/from.ts +47 -0
- package/src/primitives/Hash/fromBytes.ts +46 -0
- package/src/primitives/Hash/fromHex.ts +44 -0
- package/src/primitives/Hash/index.ts +45 -8
- package/src/primitives/Hash/isHash.ts +31 -0
- package/src/primitives/Hash/toHex.ts +29 -0
- package/src/primitives/Hex/Hex.test.ts +266 -155
- package/src/primitives/Hex/assertSize.ts +41 -0
- package/src/primitives/Hex/concat.ts +37 -0
- package/src/primitives/Hex/from.ts +38 -0
- package/src/primitives/Hex/fromBigInt.ts +40 -0
- package/src/primitives/Hex/fromBoolean.ts +29 -0
- package/src/primitives/Hex/fromNumber.ts +41 -0
- package/src/primitives/Hex/fromString.ts +28 -0
- package/src/primitives/Hex/index.ts +68 -37
- package/src/primitives/Hex/pad.ts +40 -0
- package/src/primitives/Hex/padRight.ts +39 -0
- package/src/primitives/Hex/size.ts +29 -0
- package/src/primitives/Hex/slice.ts +42 -0
- package/src/primitives/Hex/toBigInt.ts +27 -0
- package/src/primitives/Hex/toBoolean.ts +38 -0
- package/src/primitives/Hex/toNumber.ts +37 -0
- package/src/primitives/Hex/toStringHex.ts +39 -0
- package/src/primitives/Hex/trim.ts +27 -0
- package/src/primitives/Hex/validate.ts +37 -0
- package/src/primitives/Hex/xor.ts +39 -0
- package/src/primitives/Opcode/OpcodeSchema.ts +1 -1
- package/src/primitives/Opcode/disassemble.ts +15 -0
- package/src/primitives/Opcode/dupPosition.ts +15 -0
- package/src/primitives/Opcode/format.ts +15 -0
- package/src/primitives/Opcode/getters.ts +54 -0
- package/src/primitives/Opcode/index.ts +301 -11
- package/src/primitives/Opcode/info.ts +18 -0
- package/src/primitives/Opcode/jumpDests.ts +15 -0
- package/src/primitives/Opcode/logTopics.ts +15 -0
- package/src/primitives/Opcode/name.ts +15 -0
- package/src/primitives/Opcode/parse.ts +15 -0
- package/src/primitives/Opcode/predicates.ts +72 -0
- package/src/primitives/Opcode/pushBytes.ts +15 -0
- package/src/primitives/Opcode/pushOpcode.ts +15 -0
- package/src/primitives/Opcode/swapPosition.ts +15 -0
- package/src/primitives/Rlp/decodeBatch.ts +46 -0
- package/src/primitives/Rlp/decodeObject.ts +46 -0
- package/src/primitives/Rlp/decodeValue.ts +51 -0
- package/src/primitives/Rlp/encodeBatch.ts +50 -0
- package/src/primitives/Rlp/encodeObject.ts +48 -0
- package/src/primitives/Rlp/encodeVariadic.ts +49 -0
- package/src/primitives/Rlp/equals.ts +22 -0
- package/src/primitives/Rlp/from.ts +44 -0
- package/src/primitives/Rlp/fromJSON.ts +45 -0
- package/src/primitives/Rlp/getEncodedLength.ts +47 -0
- package/src/primitives/Rlp/getLength.ts +44 -0
- package/src/primitives/Rlp/index.ts +67 -14
- package/src/primitives/Rlp/isBytesData.ts +21 -0
- package/src/primitives/Rlp/isCanonical.ts +32 -0
- package/src/primitives/Rlp/isData.ts +21 -0
- package/src/primitives/Rlp/isList.ts +46 -0
- package/src/primitives/Rlp/isListData.ts +21 -0
- package/src/primitives/Rlp/isString.ts +46 -0
- package/src/primitives/Rlp/toJSON.ts +20 -0
- package/src/primitives/Rlp/toRaw.ts +31 -0
- package/src/primitives/Signature/Signature.test.ts +217 -0
- package/src/primitives/Signature/from.ts +38 -0
- package/src/primitives/Signature/fromBytes.ts +31 -0
- package/src/primitives/Signature/fromCompact.ts +33 -0
- package/src/primitives/Signature/fromDER.ts +35 -0
- package/src/primitives/Signature/fromEd25519.ts +24 -0
- package/src/primitives/Signature/fromHex.ts +29 -0
- package/src/primitives/Signature/fromP256.ts +25 -0
- package/src/primitives/Signature/fromRpc.ts +38 -0
- package/src/primitives/Signature/fromSecp256k1.ts +29 -0
- package/src/primitives/Signature/fromTuple.ts +33 -0
- package/src/primitives/Signature/getR.ts +24 -0
- package/src/primitives/Signature/getS.ts +24 -0
- package/src/primitives/Signature/getV.ts +24 -0
- package/src/primitives/Signature/index.ts +61 -11
- package/src/primitives/Signature/toDER.ts +24 -0
- package/src/primitives/Signature/toHex.ts +24 -0
- package/src/primitives/Signature/toRpc.ts +35 -0
- package/src/primitives/Signature/toTuple.ts +29 -0
- package/src/primitives/Signature/verify.ts +31 -0
- package/src/primitives/Siwe/create.ts +56 -0
- package/src/primitives/Siwe/hash.ts +27 -0
- package/src/primitives/Siwe/index.ts +57 -21
- package/src/primitives/Siwe/parse.ts +24 -0
- package/src/primitives/Siwe/verify.ts +47 -0
- package/src/primitives/Transaction/index.ts +467 -2
- package/src/primitives/Uint/Uint.test.ts +200 -0
- package/src/primitives/Uint/dividedBy.ts +34 -0
- package/src/primitives/Uint/from.ts +35 -0
- package/src/primitives/Uint/fromAbiEncoded.ts +29 -0
- package/src/primitives/Uint/fromBigInt.ts +30 -0
- package/src/primitives/Uint/fromBytes.ts +32 -0
- package/src/primitives/Uint/fromHex.ts +32 -0
- package/src/primitives/Uint/fromNumber.ts +30 -0
- package/src/primitives/Uint/index.ts +90 -45
- package/src/primitives/Uint/isUint256.ts +25 -0
- package/src/primitives/Uint/isValid.ts +25 -0
- package/src/primitives/Uint/modulo.ts +34 -0
- package/src/primitives/Uint/toHex.ts +31 -0
- package/src/primitives/Uint/tryFrom.ts +30 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview ABI encodePacked - compact encoding without padding.
|
|
3
|
+
* Provides Effect-based wrapper for packed encoding.
|
|
4
|
+
*
|
|
5
|
+
* @module Abi/encodePacked
|
|
6
|
+
* @since 0.0.1
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
AbiEncodingError as AbiEncodingErrorClass,
|
|
11
|
+
type AbiEncodingError,
|
|
12
|
+
type AbiParameterMismatchError,
|
|
13
|
+
encodePacked as _encodePacked,
|
|
14
|
+
} from "@tevm/voltaire/Abi";
|
|
15
|
+
import type { HexType } from "@tevm/voltaire/Hex";
|
|
16
|
+
import * as Effect from "effect/Effect";
|
|
17
|
+
|
|
18
|
+
type EncodePackedErrorType = AbiParameterMismatchError | AbiEncodingError;
|
|
19
|
+
|
|
20
|
+
const isAbiError = (e: unknown): e is EncodePackedErrorType =>
|
|
21
|
+
e !== null &&
|
|
22
|
+
typeof e === "object" &&
|
|
23
|
+
"name" in e &&
|
|
24
|
+
(e.name === "AbiParameterMismatchError" || e.name === "AbiEncodingError");
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Encodes values in packed format (no padding).
|
|
28
|
+
*
|
|
29
|
+
* @description
|
|
30
|
+
* Performs ABI encoding without padding, used for creating hashes
|
|
31
|
+
* where standard ABI encoding would waste space.
|
|
32
|
+
*
|
|
33
|
+
* @param {readonly string[]} types - Array of Solidity type strings.
|
|
34
|
+
* @param {readonly unknown[]} values - Array of values to encode.
|
|
35
|
+
* @returns {Effect.Effect<HexType, AbiParameterMismatchError | AbiEncodingError>}
|
|
36
|
+
* Effect yielding encoded hex string.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* import * as Effect from 'effect/Effect'
|
|
41
|
+
* import { encodePacked } from 'voltaire-effect/primitives/Abi'
|
|
42
|
+
*
|
|
43
|
+
* const encoded = await Effect.runPromise(
|
|
44
|
+
* encodePacked(['address', 'uint256'], [address, amount])
|
|
45
|
+
* )
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @since 0.0.1
|
|
49
|
+
*/
|
|
50
|
+
export const encodePacked = (
|
|
51
|
+
types: readonly string[],
|
|
52
|
+
values: readonly unknown[],
|
|
53
|
+
): Effect.Effect<HexType, AbiParameterMismatchError | AbiEncodingError> =>
|
|
54
|
+
Effect.try({
|
|
55
|
+
try: () => _encodePacked(types, values),
|
|
56
|
+
catch: (e) =>
|
|
57
|
+
isAbiError(e)
|
|
58
|
+
? e
|
|
59
|
+
: new AbiEncodingErrorClass("Failed to encode packed", {
|
|
60
|
+
cause: e instanceof Error ? e : undefined,
|
|
61
|
+
}),
|
|
62
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { describe, expect, it } from "@effect/vitest";
|
|
2
|
+
import { Address, Bytes, Selector } from "@tevm/voltaire";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import { encodeWrappedError } from "./encodeWrappedError.js";
|
|
5
|
+
|
|
6
|
+
describe("encodeWrappedError", () => {
|
|
7
|
+
describe("success cases", () => {
|
|
8
|
+
it.effect("encodes wrapped error", () =>
|
|
9
|
+
Effect.gen(function* () {
|
|
10
|
+
const encoded = yield* encodeWrappedError({
|
|
11
|
+
target: Address.from("0x742d35Cc6634C0532925a3b844Bc9e7595f251e3"),
|
|
12
|
+
selector: Selector.fromHex("0xa9059cbb"),
|
|
13
|
+
reason: Bytes.from(new Uint8Array([1, 2, 3])),
|
|
14
|
+
details: Bytes.from(new Uint8Array([4, 5, 6])),
|
|
15
|
+
});
|
|
16
|
+
expect(encoded).toBeInstanceOf(Uint8Array);
|
|
17
|
+
expect(encoded.length).toBeGreaterThan(4);
|
|
18
|
+
}),
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Encodes ERC-7751 wrapped error data.
|
|
3
|
+
* Provides Effect-based wrapper for wrapped error encoding.
|
|
4
|
+
*
|
|
5
|
+
* @module Abi/encodeWrappedError
|
|
6
|
+
* @since 0.0.1
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
AbiEncodingError as AbiEncodingErrorClass,
|
|
11
|
+
type AbiEncodingError,
|
|
12
|
+
type WrappedErrorType,
|
|
13
|
+
encodeWrappedError as _encodeWrappedError,
|
|
14
|
+
} from "@tevm/voltaire/Abi";
|
|
15
|
+
import * as Effect from "effect/Effect";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Wrapped error input structure (ERC-7751).
|
|
19
|
+
*/
|
|
20
|
+
export type WrappedErrorInput = WrappedErrorType;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Encodes ERC-7751 wrapped error data.
|
|
24
|
+
*
|
|
25
|
+
* @description
|
|
26
|
+
* Creates encoded error data following the ERC-7751 specification:
|
|
27
|
+
* `error WrappedError(address target, bytes4 selector, bytes reason, bytes details)`
|
|
28
|
+
*
|
|
29
|
+
* @param {WrappedErrorInput} wrappedError - Wrapped error data.
|
|
30
|
+
* @returns {Effect.Effect<Uint8Array, AbiEncodingError>}
|
|
31
|
+
* Effect yielding encoded error data (selector + ABI-encoded params).
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* import * as Effect from 'effect/Effect'
|
|
36
|
+
* import { encodeWrappedError } from 'voltaire-effect/primitives/Abi'
|
|
37
|
+
* import * as Address from '@tevm/voltaire/Address'
|
|
38
|
+
* import * as Selector from '@tevm/voltaire/Selector'
|
|
39
|
+
*
|
|
40
|
+
* const encoded = await Effect.runPromise(
|
|
41
|
+
* encodeWrappedError({
|
|
42
|
+
* target: Address.from('0x1234...'),
|
|
43
|
+
* selector: Selector.fromHex('0xabcd1234'),
|
|
44
|
+
* reason: new Uint8Array([...]),
|
|
45
|
+
* details: new Uint8Array([...])
|
|
46
|
+
* })
|
|
47
|
+
* )
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* @see https://eips.ethereum.org/EIPS/eip-7751
|
|
51
|
+
* @since 0.0.1
|
|
52
|
+
*/
|
|
53
|
+
export const encodeWrappedError = (
|
|
54
|
+
wrappedError: WrappedErrorInput,
|
|
55
|
+
): Effect.Effect<Uint8Array, AbiEncodingError> =>
|
|
56
|
+
Effect.try({
|
|
57
|
+
try: () => _encodeWrappedError(wrappedError),
|
|
58
|
+
catch: (e) =>
|
|
59
|
+
e instanceof AbiEncodingErrorClass
|
|
60
|
+
? e
|
|
61
|
+
: new AbiEncodingErrorClass("Failed to encode wrapped error", {
|
|
62
|
+
cause: e instanceof Error ? e : undefined,
|
|
63
|
+
}),
|
|
64
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, expect, it } from "@effect/vitest";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as S from "effect/Schema";
|
|
4
|
+
import { fromArray } from "./AbiSchema.js";
|
|
5
|
+
import {
|
|
6
|
+
findSelectorCollisions,
|
|
7
|
+
hasSelectorCollisions,
|
|
8
|
+
} from "./findSelectorCollisions.js";
|
|
9
|
+
|
|
10
|
+
const normalAbi = S.decodeUnknownSync(fromArray)([
|
|
11
|
+
{
|
|
12
|
+
type: "function",
|
|
13
|
+
name: "transfer",
|
|
14
|
+
stateMutability: "nonpayable",
|
|
15
|
+
inputs: [
|
|
16
|
+
{ name: "to", type: "address" },
|
|
17
|
+
{ name: "amount", type: "uint256" },
|
|
18
|
+
],
|
|
19
|
+
outputs: [{ type: "bool" }],
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
type: "function",
|
|
23
|
+
name: "balanceOf",
|
|
24
|
+
stateMutability: "view",
|
|
25
|
+
inputs: [{ name: "account", type: "address" }],
|
|
26
|
+
outputs: [{ type: "uint256" }],
|
|
27
|
+
},
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
describe("findSelectorCollisions", () => {
|
|
31
|
+
describe("success cases", () => {
|
|
32
|
+
it.effect("returns empty array for normal ABI", () =>
|
|
33
|
+
Effect.gen(function* () {
|
|
34
|
+
const collisions = yield* findSelectorCollisions(normalAbi);
|
|
35
|
+
expect(collisions).toEqual([]);
|
|
36
|
+
}),
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe("hasSelectorCollisions", () => {
|
|
42
|
+
describe("success cases", () => {
|
|
43
|
+
it.effect("returns false for normal ABI", () =>
|
|
44
|
+
Effect.gen(function* () {
|
|
45
|
+
const hasCollisions = yield* hasSelectorCollisions(normalAbi);
|
|
46
|
+
expect(hasCollisions).toBe(false);
|
|
47
|
+
}),
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Finds function selector collisions in an ABI.
|
|
3
|
+
* Provides Effect-based wrapper for collision detection.
|
|
4
|
+
*
|
|
5
|
+
* @module Abi/findSelectorCollisions
|
|
6
|
+
* @since 0.0.1
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
type ItemType,
|
|
11
|
+
findSelectorCollisions as _findSelectorCollisions,
|
|
12
|
+
hasSelectorCollisions as _hasSelectorCollisions,
|
|
13
|
+
} from "@tevm/voltaire/Abi";
|
|
14
|
+
import type { HexType } from "@tevm/voltaire/Hex";
|
|
15
|
+
import * as Effect from "effect/Effect";
|
|
16
|
+
|
|
17
|
+
type AbiInput = readonly ItemType[];
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Selector collision information.
|
|
21
|
+
*/
|
|
22
|
+
export interface SelectorCollision {
|
|
23
|
+
selector: HexType;
|
|
24
|
+
functions: readonly ItemType[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Finds function selector collisions in an ABI.
|
|
29
|
+
*
|
|
30
|
+
* @description
|
|
31
|
+
* Detects when multiple functions in an ABI share the same 4-byte selector.
|
|
32
|
+
* This can happen due to the birthday paradox with 4-byte hashes.
|
|
33
|
+
*
|
|
34
|
+
* @param {AbiInput} abi - The contract ABI to check.
|
|
35
|
+
* @returns {Effect.Effect<readonly SelectorCollision[], never>}
|
|
36
|
+
* Effect yielding array of collisions (empty if none).
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* import * as Effect from 'effect/Effect'
|
|
41
|
+
* import { findSelectorCollisions } from 'voltaire-effect/primitives/Abi'
|
|
42
|
+
*
|
|
43
|
+
* const collisions = await Effect.runPromise(
|
|
44
|
+
* findSelectorCollisions(abi)
|
|
45
|
+
* )
|
|
46
|
+
* if (collisions.length > 0) {
|
|
47
|
+
* console.warn('Selector collisions detected:', collisions)
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @since 0.0.1
|
|
52
|
+
*/
|
|
53
|
+
export const findSelectorCollisions = (
|
|
54
|
+
abi: AbiInput,
|
|
55
|
+
): Effect.Effect<readonly SelectorCollision[], never> =>
|
|
56
|
+
Effect.sync(() => _findSelectorCollisions([...abi]) as SelectorCollision[]);
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Checks if an ABI has any function selector collisions.
|
|
60
|
+
*
|
|
61
|
+
* @description
|
|
62
|
+
* Quick check for the presence of selector collisions.
|
|
63
|
+
*
|
|
64
|
+
* @param {AbiInput} abi - The contract ABI to check.
|
|
65
|
+
* @returns {Effect.Effect<boolean, never>}
|
|
66
|
+
* Effect yielding true if collisions exist.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```typescript
|
|
70
|
+
* import * as Effect from 'effect/Effect'
|
|
71
|
+
* import { hasSelectorCollisions } from 'voltaire-effect/primitives/Abi'
|
|
72
|
+
*
|
|
73
|
+
* const hasCollisions = await Effect.runPromise(
|
|
74
|
+
* hasSelectorCollisions(abi)
|
|
75
|
+
* )
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* @since 0.0.1
|
|
79
|
+
*/
|
|
80
|
+
export const hasSelectorCollisions = (
|
|
81
|
+
abi: AbiInput,
|
|
82
|
+
): Effect.Effect<boolean, never> =>
|
|
83
|
+
Effect.sync(() => _hasSelectorCollisions([...abi]));
|
|
@@ -21,33 +21,59 @@ export {
|
|
|
21
21
|
ReceiveSchema,
|
|
22
22
|
StateMutabilitySchema,
|
|
23
23
|
} from "./AbiSchema.js";
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
export {
|
|
27
|
-
export {
|
|
28
|
-
export {
|
|
24
|
+
|
|
25
|
+
// Core encoding
|
|
26
|
+
export { encode } from "./encode.js";
|
|
27
|
+
export { encodePacked } from "./encodePacked.js";
|
|
28
|
+
export { encodeWrappedError, type WrappedErrorInput } from "./encodeWrappedError.js";
|
|
29
|
+
|
|
30
|
+
// Core decoding
|
|
31
|
+
export { decode } from "./decode.js";
|
|
32
|
+
export { decodeData } from "./decodeData.js";
|
|
33
|
+
export { decodeLog, type LogInput } from "./decodeLog.js";
|
|
34
|
+
export { decodeWrappedError, type WrappedErrorResult } from "./decodeWrappedError.js";
|
|
35
|
+
|
|
29
36
|
// Error encoding/decoding
|
|
37
|
+
export { decodeError } from "./decodeError.js";
|
|
30
38
|
export { encodeError } from "./encodeError.js";
|
|
39
|
+
|
|
31
40
|
// Event encoding/decoding
|
|
41
|
+
export { decodeEventLog } from "./decodeEventLog.js";
|
|
32
42
|
export { encodeEventLog } from "./encodeEventLog.js";
|
|
43
|
+
|
|
33
44
|
// Function encoding/decoding
|
|
45
|
+
export { decodeFunction } from "./decodeFunction.js";
|
|
46
|
+
export { decodeFunctionData } from "./decodeFunctionData.js";
|
|
47
|
+
export { decodeFunctionResult } from "./decodeFunctionResult.js";
|
|
34
48
|
export { encodeFunction } from "./encodeFunction.js";
|
|
35
49
|
export { encodeFunctionData } from "./encodeFunctionData.js";
|
|
36
50
|
export { encodeFunctionResult } from "./encodeFunctionResult.js";
|
|
51
|
+
|
|
52
|
+
// Lookup functions
|
|
37
53
|
export { findError } from "./findError.js";
|
|
38
54
|
export { findEvent } from "./findEvent.js";
|
|
39
|
-
// Lookup functions
|
|
40
55
|
export { findFunction } from "./findFunction.js";
|
|
56
|
+
|
|
57
|
+
// Selector collision detection
|
|
58
|
+
export {
|
|
59
|
+
findSelectorCollisions,
|
|
60
|
+
hasSelectorCollisions,
|
|
61
|
+
type SelectorCollision,
|
|
62
|
+
} from "./findSelectorCollisions.js";
|
|
63
|
+
|
|
41
64
|
// Formatting
|
|
42
65
|
export { format } from "./format.js";
|
|
43
66
|
export { formatWithArgs } from "./formatWithArgs.js";
|
|
67
|
+
|
|
68
|
+
// Getters
|
|
44
69
|
export { getErrorSignature } from "./getErrorSignature.js";
|
|
45
70
|
export { getEvent } from "./getEvent.js";
|
|
46
71
|
export { getEventSignature } from "./getEventSignature.js";
|
|
47
72
|
export { getFunction } from "./getFunction.js";
|
|
48
73
|
export { getFunctionSignature } from "./getFunctionSignature.js";
|
|
49
|
-
// Selectors and signatures
|
|
50
74
|
export { getSelector } from "./getSelector.js";
|
|
75
|
+
|
|
51
76
|
// Parsing
|
|
52
77
|
export { AbiParseError, parse } from "./parse.js";
|
|
53
78
|
export { AbiItemParseError, parseItem } from "./parseItem.js";
|
|
79
|
+
export { parseLogs, type ParsedLog, type ParseLogsInput } from "./parseLogs.js";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, it } from "@effect/vitest";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as S from "effect/Schema";
|
|
4
|
+
import { fromArray } from "./AbiSchema.js";
|
|
5
|
+
import { parseLogs } from "./parseLogs.js";
|
|
6
|
+
|
|
7
|
+
const erc20Abi = S.decodeUnknownSync(fromArray)([
|
|
8
|
+
{
|
|
9
|
+
type: "event",
|
|
10
|
+
name: "Transfer",
|
|
11
|
+
inputs: [
|
|
12
|
+
{ name: "from", type: "address", indexed: true },
|
|
13
|
+
{ name: "to", type: "address", indexed: true },
|
|
14
|
+
{ name: "value", type: "uint256", indexed: false },
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
describe("parseLogs", () => {
|
|
20
|
+
describe("success cases", () => {
|
|
21
|
+
it.effect("returns empty array for no matching logs", () =>
|
|
22
|
+
Effect.gen(function* () {
|
|
23
|
+
const parsed = yield* parseLogs(erc20Abi, [
|
|
24
|
+
{ data: new Uint8Array(32), topics: [new Uint8Array(32)] },
|
|
25
|
+
]);
|
|
26
|
+
expect(parsed).toEqual([]);
|
|
27
|
+
}),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
it.effect("returns empty array for empty logs", () =>
|
|
31
|
+
Effect.gen(function* () {
|
|
32
|
+
const parsed = yield* parseLogs(erc20Abi, []);
|
|
33
|
+
expect(parsed).toEqual([]);
|
|
34
|
+
}),
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Parses multiple event logs using ABI.
|
|
3
|
+
* Provides Effect-based wrapper for batch log parsing.
|
|
4
|
+
*
|
|
5
|
+
* @module Abi/parseLogs
|
|
6
|
+
* @since 0.0.1
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { type Abi, type ItemType } from "@tevm/voltaire/Abi";
|
|
10
|
+
import type { HexType } from "@tevm/voltaire/Hex";
|
|
11
|
+
import * as Effect from "effect/Effect";
|
|
12
|
+
|
|
13
|
+
type AbiInput = readonly ItemType[];
|
|
14
|
+
|
|
15
|
+
const toAbi = (input: AbiInput): Abi => input as unknown as Abi;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Log input structure for parseLogs.
|
|
19
|
+
*/
|
|
20
|
+
export interface ParseLogsInput {
|
|
21
|
+
data: Uint8Array | string;
|
|
22
|
+
topics: readonly (Uint8Array | string)[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Parsed log result.
|
|
27
|
+
*/
|
|
28
|
+
export interface ParsedLog {
|
|
29
|
+
eventName: string;
|
|
30
|
+
args: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Parses multiple event logs using ABI.
|
|
35
|
+
*
|
|
36
|
+
* @description
|
|
37
|
+
* Parses an array of log entries, returning successfully decoded logs.
|
|
38
|
+
* Logs that don't match any event in the ABI are silently filtered out.
|
|
39
|
+
*
|
|
40
|
+
* @param {AbiInput} abi - The contract ABI.
|
|
41
|
+
* @param {readonly ParseLogsInput[]} logs - Array of log objects.
|
|
42
|
+
* @returns {Effect.Effect<readonly ParsedLog[], never>}
|
|
43
|
+
* Effect yielding array of parsed logs (never fails).
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* import * as Effect from 'effect/Effect'
|
|
48
|
+
* import { parseLogs } from 'voltaire-effect/primitives/Abi'
|
|
49
|
+
*
|
|
50
|
+
* const parsed = await Effect.runPromise(
|
|
51
|
+
* parseLogs(abi, logs)
|
|
52
|
+
* )
|
|
53
|
+
* // [{ eventName: 'Transfer', args: { from, to, value } }]
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @since 0.0.1
|
|
57
|
+
*/
|
|
58
|
+
export const parseLogs = (
|
|
59
|
+
abi: AbiInput,
|
|
60
|
+
logs: readonly ParseLogsInput[],
|
|
61
|
+
): Effect.Effect<readonly ParsedLog[], never> =>
|
|
62
|
+
Effect.sync(() => toAbi(abi).parseLogs(logs));
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import * as S from "effect/Schema";
|
|
4
|
+
import * as AccessList from "./index.js";
|
|
5
|
+
|
|
6
|
+
const makeAddress = (byte: number): Uint8Array & { readonly __brand?: symbol } =>
|
|
7
|
+
new Uint8Array(20).fill(byte) as Uint8Array & { readonly __brand?: symbol };
|
|
8
|
+
|
|
9
|
+
const makeHash = (byte: number): Uint8Array & { readonly __brand?: symbol } =>
|
|
10
|
+
new Uint8Array(32).fill(byte) as Uint8Array & { readonly __brand?: symbol };
|
|
11
|
+
|
|
12
|
+
describe("AccessList", () => {
|
|
13
|
+
describe("create", () => {
|
|
14
|
+
it("creates empty access list", () => {
|
|
15
|
+
const list = AccessList.create();
|
|
16
|
+
expect(AccessList.isEmpty(list)).toBe(true);
|
|
17
|
+
expect(AccessList.addressCount(list)).toBe(0);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("from", () => {
|
|
22
|
+
it("creates from items", async () => {
|
|
23
|
+
const addr = makeAddress(0x42);
|
|
24
|
+
const key = makeHash(0x01);
|
|
25
|
+
const list = await Effect.runPromise(
|
|
26
|
+
AccessList.from([{ address: addr, storageKeys: [key] }])
|
|
27
|
+
);
|
|
28
|
+
expect(AccessList.addressCount(list)).toBe(1);
|
|
29
|
+
expect(AccessList.storageKeyCount(list)).toBe(1);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("type guards", () => {
|
|
34
|
+
it("is() validates access list", () => {
|
|
35
|
+
const list = AccessList.create();
|
|
36
|
+
expect(AccessList.is(list)).toBe(true);
|
|
37
|
+
expect(AccessList.is([])).toBe(true);
|
|
38
|
+
expect(AccessList.is("not a list")).toBe(false);
|
|
39
|
+
expect(AccessList.is([{ invalid: true }])).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("isItem() validates items", () => {
|
|
43
|
+
const addr = makeAddress(0x42);
|
|
44
|
+
const key = makeHash(0x01);
|
|
45
|
+
expect(AccessList.isItem({ address: addr, storageKeys: [key] })).toBe(true);
|
|
46
|
+
expect(AccessList.isItem({ address: addr, storageKeys: [] })).toBe(true);
|
|
47
|
+
expect(AccessList.isItem({ address: "0x123", storageKeys: [] })).toBe(false);
|
|
48
|
+
expect(AccessList.isItem(null)).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("queries", () => {
|
|
53
|
+
it("isEmpty", () => {
|
|
54
|
+
expect(AccessList.isEmpty(AccessList.create())).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("addressCount", () => {
|
|
58
|
+
const addr1 = makeAddress(0x01);
|
|
59
|
+
const addr2 = makeAddress(0x02);
|
|
60
|
+
let list = AccessList.create();
|
|
61
|
+
list = AccessList.withAddress(list, addr1);
|
|
62
|
+
list = AccessList.withAddress(list, addr2);
|
|
63
|
+
expect(AccessList.addressCount(list)).toBe(2);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("storageKeyCount", () => {
|
|
67
|
+
const addr = makeAddress(0x42);
|
|
68
|
+
const key1 = makeHash(0x01);
|
|
69
|
+
const key2 = makeHash(0x02);
|
|
70
|
+
let list = AccessList.create();
|
|
71
|
+
list = AccessList.withStorageKey(list, addr, key1);
|
|
72
|
+
list = AccessList.withStorageKey(list, addr, key2);
|
|
73
|
+
expect(AccessList.storageKeyCount(list)).toBe(2);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("includesAddress", () => {
|
|
77
|
+
const addr = makeAddress(0x42);
|
|
78
|
+
const other = makeAddress(0x43);
|
|
79
|
+
let list = AccessList.create();
|
|
80
|
+
list = AccessList.withAddress(list, addr);
|
|
81
|
+
expect(AccessList.includesAddress(list, addr)).toBe(true);
|
|
82
|
+
expect(AccessList.includesAddress(list, other)).toBe(false);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("includesStorageKey", () => {
|
|
86
|
+
const addr = makeAddress(0x42);
|
|
87
|
+
const key = makeHash(0x01);
|
|
88
|
+
const other = makeHash(0x02);
|
|
89
|
+
let list = AccessList.create();
|
|
90
|
+
list = AccessList.withStorageKey(list, addr, key);
|
|
91
|
+
expect(AccessList.includesStorageKey(list, addr, key)).toBe(true);
|
|
92
|
+
expect(AccessList.includesStorageKey(list, addr, other)).toBe(false);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("keysFor", () => {
|
|
96
|
+
const addr = makeAddress(0x42);
|
|
97
|
+
const key = makeHash(0x01);
|
|
98
|
+
let list = AccessList.create();
|
|
99
|
+
list = AccessList.withStorageKey(list, addr, key);
|
|
100
|
+
const keys = AccessList.keysFor(list, addr);
|
|
101
|
+
expect(keys).toBeDefined();
|
|
102
|
+
expect(keys?.length).toBe(1);
|
|
103
|
+
expect(AccessList.keysFor(list, makeAddress(0x99))).toBeUndefined();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe("gas calculations", () => {
|
|
108
|
+
it("gasCost", () => {
|
|
109
|
+
const addr = makeAddress(0x42);
|
|
110
|
+
const key = makeHash(0x01);
|
|
111
|
+
let list = AccessList.create();
|
|
112
|
+
list = AccessList.withStorageKey(list, addr, key);
|
|
113
|
+
const cost = AccessList.gasCost(list);
|
|
114
|
+
expect(cost).toBe(AccessList.ADDRESS_COST + AccessList.STORAGE_KEY_COST);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("gasSavings", () => {
|
|
118
|
+
const addr = makeAddress(0x42);
|
|
119
|
+
let list = AccessList.create();
|
|
120
|
+
list = AccessList.withAddress(list, addr);
|
|
121
|
+
const savings = AccessList.gasSavings(list);
|
|
122
|
+
expect(savings).toBe(AccessList.COLD_ACCOUNT_ACCESS_COST - AccessList.ADDRESS_COST);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("hasSavings", () => {
|
|
126
|
+
const addr = makeAddress(0x42);
|
|
127
|
+
let list = AccessList.create();
|
|
128
|
+
list = AccessList.withAddress(list, addr);
|
|
129
|
+
expect(AccessList.hasSavings(list)).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe("transformations", () => {
|
|
134
|
+
it("withAddress adds new address", () => {
|
|
135
|
+
const addr = makeAddress(0x42);
|
|
136
|
+
let list = AccessList.create();
|
|
137
|
+
list = AccessList.withAddress(list, addr);
|
|
138
|
+
expect(AccessList.includesAddress(list, addr)).toBe(true);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("withAddress is idempotent", () => {
|
|
142
|
+
const addr = makeAddress(0x42);
|
|
143
|
+
let list = AccessList.create();
|
|
144
|
+
list = AccessList.withAddress(list, addr);
|
|
145
|
+
const list2 = AccessList.withAddress(list, addr);
|
|
146
|
+
expect(AccessList.addressCount(list2)).toBe(1);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("withStorageKey adds key", () => {
|
|
150
|
+
const addr = makeAddress(0x42);
|
|
151
|
+
const key = makeHash(0x01);
|
|
152
|
+
let list = AccessList.create();
|
|
153
|
+
list = AccessList.withStorageKey(list, addr, key);
|
|
154
|
+
expect(AccessList.includesStorageKey(list, addr, key)).toBe(true);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("deduplicate merges duplicate addresses", () => {
|
|
158
|
+
const addr = makeAddress(0x42);
|
|
159
|
+
const key1 = makeHash(0x01);
|
|
160
|
+
const key2 = makeHash(0x02);
|
|
161
|
+
const list = [
|
|
162
|
+
{ address: addr, storageKeys: [key1] },
|
|
163
|
+
{ address: addr, storageKeys: [key2, key1] },
|
|
164
|
+
] as AccessList.BrandedAccessList;
|
|
165
|
+
const deduped = AccessList.deduplicate(list);
|
|
166
|
+
expect(AccessList.addressCount(deduped)).toBe(1);
|
|
167
|
+
expect(AccessList.storageKeyCount(deduped)).toBe(2);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("merge combines lists", () => {
|
|
171
|
+
const addr1 = makeAddress(0x01);
|
|
172
|
+
const addr2 = makeAddress(0x02);
|
|
173
|
+
const list1 = AccessList.withAddress(AccessList.create(), addr1);
|
|
174
|
+
const list2 = AccessList.withAddress(AccessList.create(), addr2);
|
|
175
|
+
const merged = AccessList.merge(list1, list2);
|
|
176
|
+
expect(AccessList.addressCount(merged)).toBe(2);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("toBytes encodes to RLP", () => {
|
|
180
|
+
const list = AccessList.create();
|
|
181
|
+
const bytes = AccessList.toBytes(list);
|
|
182
|
+
expect(bytes).toBeInstanceOf(Uint8Array);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe("assertValid", () => {
|
|
187
|
+
it("succeeds for valid list", async () => {
|
|
188
|
+
const list = AccessList.create();
|
|
189
|
+
await Effect.runPromise(AccessList.assertValid(list));
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
describe("Rpc schema", () => {
|
|
194
|
+
it("decodes from JSON-RPC format", () => {
|
|
195
|
+
const input = [
|
|
196
|
+
{
|
|
197
|
+
address: "0x742d35Cc6634C0532925a3b844Bc9e7595f251e3",
|
|
198
|
+
storageKeys: [
|
|
199
|
+
"0x0000000000000000000000000000000000000000000000000000000000000001",
|
|
200
|
+
],
|
|
201
|
+
},
|
|
202
|
+
];
|
|
203
|
+
const list = S.decodeSync(AccessList.Rpc)(input);
|
|
204
|
+
expect(AccessList.addressCount(list)).toBe(1);
|
|
205
|
+
expect(AccessList.storageKeyCount(list)).toBe(1);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it("encodes to JSON-RPC format", () => {
|
|
209
|
+
const addr = makeAddress(0x42);
|
|
210
|
+
const key = makeHash(0x01);
|
|
211
|
+
let list = AccessList.create();
|
|
212
|
+
list = AccessList.withStorageKey(list, addr, key);
|
|
213
|
+
const json = S.encodeSync(AccessList.Rpc)(list);
|
|
214
|
+
expect(json).toHaveLength(1);
|
|
215
|
+
expect(json[0].address).toMatch(/^0x[0-9a-f]{40}$/);
|
|
216
|
+
expect(json[0].storageKeys).toHaveLength(1);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe("constants", () => {
|
|
221
|
+
it("exports gas constants", () => {
|
|
222
|
+
expect(AccessList.ADDRESS_COST).toBe(2400n);
|
|
223
|
+
expect(AccessList.STORAGE_KEY_COST).toBe(1900n);
|
|
224
|
+
expect(AccessList.COLD_ACCOUNT_ACCESS_COST).toBe(2600n);
|
|
225
|
+
expect(AccessList.COLD_STORAGE_ACCESS_COST).toBe(2100n);
|
|
226
|
+
expect(AccessList.WARM_STORAGE_ACCESS_COST).toBe(100n);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module AccessListTypeSchema
|
|
3
|
+
* @description Effect Schema declaration for BrandedAccessList type
|
|
4
|
+
* @since 0.1.0
|
|
5
|
+
*/
|
|
6
|
+
import { AccessList, type BrandedAccessList } from "@tevm/voltaire/AccessList";
|
|
7
|
+
import * as S from "effect/Schema";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Schema declaration for the BrandedAccessList type
|
|
11
|
+
* @since 0.1.0
|
|
12
|
+
*/
|
|
13
|
+
export const AccessListTypeSchema = S.declare<BrandedAccessList>(
|
|
14
|
+
(u): u is BrandedAccessList => AccessList.is(u),
|
|
15
|
+
{ identifier: "AccessList" },
|
|
16
|
+
);
|