voltaire-effect 0.2.25 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +184 -12
  2. package/dist/{index-BCOuszKZ.d.ts → index-3UKSP3cd.d.ts} +74 -31
  3. package/dist/index.d.ts +107 -106
  4. package/dist/index.js +1770 -1224
  5. package/dist/native/index.d.ts +7 -6
  6. package/dist/native/index.js +1795 -1249
  7. package/dist/primitives/index.d.ts +1 -1
  8. package/dist/primitives/index.js +37 -4
  9. package/dist/services/index.d.ts +1031 -519
  10. package/dist/services/index.js +1276 -762
  11. package/package.json +11 -11
  12. package/src/primitives/Abi/encodeFunctionData.bench.ts +79 -0
  13. package/src/primitives/Address/Hex.ts +15 -1
  14. package/src/primitives/Block/BlockSchema.ts +1 -1
  15. package/src/primitives/PrivateKey/Bytes.ts +35 -0
  16. package/src/primitives/PrivateKey/Hex.ts +49 -1
  17. package/src/primitives/PrivateKey/PrivateKey.error.test.ts +167 -0
  18. package/src/primitives/PrivateKey/PrivateKey.test.ts +79 -0
  19. package/src/primitives/PrivateKey/index.ts +24 -32
  20. package/src/services/Contract/ContractsService.test.ts +351 -0
  21. package/src/services/Contract/ContractsService.ts +206 -0
  22. package/src/services/Contract/index.ts +54 -13
  23. package/src/services/Provider/ExecutionPlanProvider.test.ts +123 -0
  24. package/src/services/Provider/ExecutionPlanProvider.ts +151 -0
  25. package/src/services/Provider/index.ts +19 -0
  26. package/src/services/Provider/providers.ts +224 -0
  27. package/src/services/RpcBatch/RpcResolver.ts +4 -4
  28. package/src/services/RpcBatch/index.ts +9 -9
  29. package/src/services/Transport/BrowserTransport.ts +4 -4
  30. package/src/services/Transport/TransportInterceptor.ts +4 -4
  31. package/src/services/index.ts +20 -1
package/README.md CHANGED
@@ -1,6 +1,121 @@
1
1
  # voltaire-effect
2
2
 
3
- Effect-TS integration for the [Voltaire](https://github.com/evmts/voltaire) Ethereum primitives library.
3
+ Effect-TS integration for the [Voltaire](https://github.com/evmts/voltaire) Ethereum primitives library. Type-safe contract interactions with composable, error-handled operations.
4
+
5
+ ## Quick Start
6
+
7
+ **viem** - implicit 3 retries hidden in transport config:
8
+ ```typescript
9
+ import { createPublicClient, http } from 'viem'
10
+
11
+ const client = createPublicClient({
12
+ transport: http('https://eth.llamarpc.com', { retryCount: 3 }) // hidden default
13
+ })
14
+
15
+ const balance = await client.readContract({
16
+ address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
17
+ abi: erc20Abi,
18
+ functionName: 'balanceOf',
19
+ args: [userAddress]
20
+ })
21
+ ```
22
+
23
+ **voltaire-effect** - explicit control over retry, timeout, and composition:
24
+ ```typescript
25
+ import { Effect } from 'effect'
26
+ import { ContractRegistryService, makeContractRegistry, HttpProvider } from 'voltaire-effect'
27
+
28
+ const Contracts = makeContractRegistry({
29
+ USDC: { abi: erc20Abi, address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' },
30
+ WETH: { abi: erc20Abi, address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' },
31
+ })
32
+
33
+ const program = Effect.gen(function* () {
34
+ const { USDC, WETH } = yield* ContractRegistryService
35
+ const usdcBalance = yield* USDC.read.balanceOf(userAddress)
36
+ const wethBalance = yield* WETH.read.balanceOf(userAddress)
37
+ return { usdcBalance, wethBalance }
38
+ }).pipe(
39
+ Effect.retry({ times: 3 }), // explicit retry policy
40
+ Effect.timeout('10 seconds'), // explicit timeout
41
+ Effect.provide(Contracts),
42
+ Effect.provide(HttpProvider('https://eth.llamarpc.com'))
43
+ )
44
+
45
+ const { usdcBalance, wethBalance } = await Effect.runPromise(program)
46
+ ```
47
+
48
+ **viem** - Address and Bytecode are both `0x${string}`, easily confused:
49
+ ```typescript
50
+ import { type Address, type Hex } from 'viem'
51
+
52
+ const address: Address = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'
53
+ const bytecode: Hex = '0x608060405234801561001057600080fd5b50'
54
+
55
+ // TypeScript allows this - runtime bug waiting to happen
56
+ await client.readContract({
57
+ address: bytecode, // oops, passed bytecode as address - compiles fine!
58
+ abi: erc20Abi,
59
+ functionName: 'balanceOf',
60
+ args: [address]
61
+ })
62
+ ```
63
+
64
+ **voltaire-effect** - branded types prevent mixing:
65
+ ```typescript
66
+ import * as Address from '@tevm/voltaire/Address'
67
+ import * as Bytecode from '@tevm/voltaire/Bytecode'
68
+
69
+ const address = Address.from('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48')
70
+ const bytecode = Bytecode.from('0x608060405234801561001057600080fd5b50')
71
+
72
+ await client.readContract({
73
+ address: bytecode, // Type error: Bytecode is not assignable to Address
74
+ ...
75
+ })
76
+ ```
77
+
78
+ ### Performance: encodeFunctionData
79
+
80
+ Both encode the same calldata, but Voltaire's WASM-optimized keccak256 (used for function selectors) is ~9x faster:
81
+
82
+ **viem**:
83
+ ```typescript
84
+ import { encodeFunctionData } from 'viem'
85
+
86
+ const calldata = encodeFunctionData({
87
+ abi: erc20Abi,
88
+ functionName: 'transfer',
89
+ args: [recipient, amount]
90
+ })
91
+ // Throws on error - must wrap in try/catch
92
+ ```
93
+
94
+ **voltaire**:
95
+ ```typescript
96
+ import * as Abi from '@tevm/voltaire/Abi'
97
+
98
+ const calldata = Abi.encodeFunction(erc20Abi, 'transfer', [recipient, amount])
99
+ ```
100
+
101
+ **voltaire-effect** (typed errors):
102
+ ```typescript
103
+ import { Effect } from 'effect'
104
+ import { encodeFunctionData } from 'voltaire-effect/primitives/Abi'
105
+
106
+ const calldata = await Effect.runPromise(
107
+ encodeFunctionData(erc20Abi, 'transfer', [recipient, amount])
108
+ )
109
+ // Effect<Hex, AbiItemNotFoundError | AbiEncodingError>
110
+ ```
111
+
112
+ | Operation | viem | voltaire | Speedup |
113
+ |-----------|------|----------|---------|
114
+ | keccak256 (32B) | 3.22 µs | 349 ns | **9.2x** |
115
+ | keccak256 (256B) | 6.23 µs | 571 ns | **10.9x** |
116
+ | keccak256 (1KB) | 24.4 µs | 1.87 µs | **13x** |
117
+
118
+ *Benchmarks on Apple M3 Max, bun 1.3.4. Voltaire uses WASM-compiled Zig keccak256.*
4
119
 
5
120
  ## Installation
6
121
 
@@ -10,33 +125,90 @@ npm install voltaire-effect effect @tevm/voltaire
10
125
 
11
126
  ## Features
12
127
 
128
+ - **Contract Registry**: Define contracts once, use everywhere with full type safety
13
129
  - **Typed Errors**: All operations return `Effect<A, E, R>` with precise error types
14
130
  - **Composable**: Chain operations using Effect's powerful combinators
15
- - **Services**: Dependency injection for crypto operations
131
+ - **Services**: Dependency injection for Provider, Signer, and contracts
16
132
  - **Zero Runtime Overhead**: Effect's tree-shaking keeps bundles small
17
133
 
18
- ## Usage
134
+ ## Contract Patterns
135
+
136
+ ### Pre-configured Contracts (Addressed Mode)
137
+
138
+ When you provide an address, you get a fully instantiated `ContractInstance`:
139
+
140
+ ```typescript
141
+ const Contracts = makeContractRegistry({
142
+ USDC: { abi: erc20Abi, address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' }
143
+ })
144
+
145
+ const program = Effect.gen(function* () {
146
+ const { USDC } = yield* ContractRegistryService
147
+ const balance = yield* USDC.read.balanceOf(account)
148
+ const txHash = yield* USDC.write.transfer(recipient, amount)
149
+ })
150
+ ```
151
+
152
+ ### Factory Pattern (No Address)
153
+
154
+ When you omit the address, you get a `ContractFactory` with an `.at()` method:
19
155
 
20
156
  ```typescript
21
- import { Effect } from "effect";
22
- import * as Address from "voltaire-effect/primitives";
23
- import * as Keccak from "voltaire-effect/crypto";
157
+ const Contracts = makeContractRegistry({
158
+ ERC20: { abi: erc20Abi } // No address
159
+ })
24
160
 
25
161
  const program = Effect.gen(function* () {
26
- const addr = yield* Address.from("0x1234567890abcdef1234567890abcdef12345678");
27
- const hash = yield* Keccak.keccak256(new Uint8Array([1, 2, 3]));
28
- return { addr, hash };
29
- });
162
+ const { ERC20 } = yield* ContractRegistryService
163
+ const token = yield* ERC20.at(userProvidedAddress)
164
+ const balance = yield* token.read.balanceOf(account)
165
+ })
166
+ ```
167
+
168
+ ### Mixed Mode
30
169
 
31
- Effect.runPromise(program);
170
+ Combine both patterns:
171
+
172
+ ```typescript
173
+ const Contracts = makeContractRegistry({
174
+ // Known addresses
175
+ USDC: { abi: erc20Abi, address: USDC_ADDRESS },
176
+ WETH: { abi: wethAbi, address: WETH_ADDRESS },
177
+ // Generic factories
178
+ ERC20: { abi: erc20Abi },
179
+ ERC721: { abi: erc721Abi }
180
+ })
181
+ ```
182
+
183
+ ## Single Contract Usage
184
+
185
+ For one-off contracts, use `Contract()` directly:
186
+
187
+ ```typescript
188
+ import { Contract, HttpProvider } from 'voltaire-effect'
189
+
190
+ const program = Effect.gen(function* () {
191
+ const token = yield* Contract(tokenAddress, erc20Abi)
192
+ const balance = yield* token.read.balanceOf(userAddress)
193
+ return balance
194
+ }).pipe(
195
+ Effect.provide(HttpProvider('https://eth.llamarpc.com'))
196
+ )
32
197
  ```
33
198
 
34
199
  ## Subpath Exports
35
200
 
36
- - `voltaire-effect` - Main entry
201
+ - `voltaire-effect` - Main entry (services, contracts, provider)
37
202
  - `voltaire-effect/primitives` - Address, Hex, Bytes32, etc.
38
203
  - `voltaire-effect/crypto` - Keccak, secp256k1, etc.
39
204
 
205
+ ## Documentation
206
+
207
+ - [Contract Registry Service](./docs/services/contract-registry.mdx)
208
+ - [Contract Factory](./docs/services/contract.mdx)
209
+ - [Provider Service](./docs/services/provider.mdx)
210
+ - [Signer Service](./docs/services/signer.mdx)
211
+
40
212
  ## License
41
213
 
42
214
  MIT
@@ -1,7 +1,7 @@
1
1
  import { BrandedSiwe, Siwe, BeaconBlockRoot, BinaryTree, BrandedBlob as BrandedBlob$a, Block, BlockBody, BlockFilter, BlockHash, BlockHeader, BlockNumber, BrandedBloomFilter, CallTrace, ChainHead, CompilerVersion, ContractCode, ContractResult, BrandedAddress as BrandedAddress$1, BrandedHash, DecodedData, Domain, DomainSeparator, EncodedData, Ens, Epoch, ErrorSignature, EventSignature, FilterId, ForkId, FunctionSignature, GasEstimate, Gas, GasRefund, GasUsed, Hardfork as Hardfork$2, BrandedInt8, BrandedInt16, BrandedInt32, BrandedInt64, BrandedInt128, BrandedInt256, License, LogIndex, MemoryDump, MerkleTree, Metadata, MultiTokenId, NodeInfo, Opcode, OpStep, PeerId, PeerInfo, PendingTransactionFilter, Permit, Proof as Proof$4, ProtocolVersion, Proxy, ReturnData, RevertReason, RuntimeCode, SignedData, Slot, SourceMap, State, StateDiff, StateProof, StorageProof, TokenBalance, TokenId, TransactionIndex, TransactionStatus, TransactionUrl, BrandedUint8, BrandedUint16, BrandedUint32, Uint64, BrandedUint128, Uncle, ValidatorIndex, Withdrawal, WithdrawalIndex } from '@tevm/voltaire';
2
2
  import * as S from 'effect/Schema';
3
3
  import { Signature as Signature$1, GetMessageHash, InvalidFieldError, InvalidNonceLengthError, InvalidSiweMessageError, MissingFieldError, SiweParseError, Verify, VerifyMessage } from '@tevm/voltaire/Siwe';
4
- import { Effect as Effect$1 } from 'effect';
4
+ import { Effect as Effect$1, Redacted } from 'effect';
5
5
  import { AddressType as AddressType$1, InvalidAddressError, InvalidHexFormatError, InvalidAddressLengthError, InvalidValueError as InvalidValueError$1, InvalidAbiEncodedPaddingError, Address, InvalidChecksumError, BrandedAddress, HEX_SIZE, InvalidHexStringError, NATIVE_ASSET_ADDRESS, NotImplementedError, SIZE, ZERO_ADDRESS } from '@tevm/voltaire/Address';
6
6
  import { BrandedAccessList, Item, ADDRESS_COST, COLD_ACCOUNT_ACCESS_COST, COLD_STORAGE_ACCESS_COST, STORAGE_KEY_COST, WARM_STORAGE_ACCESS_COST } from '@tevm/voltaire/AccessList';
7
7
  import { InvalidFormatError, InvalidLengthError, DecodingError, ValidationError } from '@tevm/voltaire/errors';
@@ -15917,6 +15917,31 @@ declare namespace index$P {
15917
15917
  */
15918
15918
 
15919
15919
  declare const Bytes$d: S.Schema<PrivateKeyType, Uint8Array>;
15920
+ /**
15921
+ * Schema for PrivateKey encoded as raw bytes, wrapped in Redacted.
15922
+ *
15923
+ * @description
15924
+ * Transforms Uint8Array to Redacted<PrivateKeyType> for secure handling.
15925
+ * The redacted wrapper prevents accidental logging of private keys.
15926
+ * Use `Redacted.value()` to explicitly unwrap for cryptographic operations.
15927
+ *
15928
+ * @example Decoding
15929
+ * ```typescript
15930
+ * import * as PrivateKey from 'voltaire-effect/primitives/PrivateKey'
15931
+ * import { Redacted } from 'effect'
15932
+ * import * as S from 'effect/Schema'
15933
+ *
15934
+ * const bytes = new Uint8Array(32).fill(1)
15935
+ * const pk = S.decodeSync(PrivateKey.RedactedBytes)(bytes)
15936
+ * console.log(pk) // Redacted(<redacted>)
15937
+ *
15938
+ * const unwrapped = Redacted.value(pk)
15939
+ * // Use unwrapped for signing
15940
+ * ```
15941
+ *
15942
+ * @since 0.1.0
15943
+ */
15944
+ declare const RedactedBytes: S.Schema<Redacted.Redacted<PrivateKeyType>, Uint8Array>;
15920
15945
 
15921
15946
  /**
15922
15947
  * Schema for PrivateKey encoded as a hex string.
@@ -15945,6 +15970,30 @@ declare const Bytes$d: S.Schema<PrivateKeyType, Uint8Array>;
15945
15970
  */
15946
15971
 
15947
15972
  declare const Hex$d: S.Schema<PrivateKeyType, string>;
15973
+ /**
15974
+ * Schema for PrivateKey encoded as hex string, wrapped in Redacted.
15975
+ *
15976
+ * @description
15977
+ * Transforms hex strings to Redacted<PrivateKeyType> for secure handling.
15978
+ * The redacted wrapper prevents accidental logging of private keys.
15979
+ * Use `Redacted.value()` to explicitly unwrap for cryptographic operations.
15980
+ *
15981
+ * @example Decoding
15982
+ * ```typescript
15983
+ * import * as PrivateKey from 'voltaire-effect/primitives/PrivateKey'
15984
+ * import { Redacted } from 'effect'
15985
+ * import * as S from 'effect/Schema'
15986
+ *
15987
+ * const pk = S.decodeSync(PrivateKey.RedactedHex)('0x0123...')
15988
+ * console.log(pk) // Redacted(<redacted>)
15989
+ *
15990
+ * const unwrapped = Redacted.value(pk)
15991
+ * // Use unwrapped for signing
15992
+ * ```
15993
+ *
15994
+ * @since 0.1.0
15995
+ */
15996
+ declare const RedactedHex: S.Schema<Redacted.Redacted<PrivateKeyType>, string>;
15948
15997
 
15949
15998
  /**
15950
15999
  * @fileoverview Effect-based private key validation.
@@ -15991,63 +16040,57 @@ declare const random: () => Effect.Effect<PrivateKeyType>;
15991
16040
  * @module PrivateKey
15992
16041
  * @description Effect Schemas for secp256k1 private keys with cryptographic operations.
15993
16042
  *
15994
- * ## Type Declarations
15995
- *
15996
- * ```typescript
15997
- * import * as PrivateKey from 'voltaire-effect/primitives/PrivateKey'
15998
- *
15999
- * function signMessage(key: PrivateKey.PrivateKeyType) {
16000
- * // ...
16001
- * }
16002
- * ```
16003
- *
16004
16043
  * ## Schemas
16005
16044
  *
16006
- * | Schema | Input | Output |
16007
- * |--------|-------|--------|
16008
- * | `PrivateKey.Hex` | hex string | PrivateKeyType |
16009
- * | `PrivateKey.Bytes` | Uint8Array | PrivateKeyType |
16045
+ * | Schema | Input | Output | Use Case |
16046
+ * |--------|-------|--------|----------|
16047
+ * | `PrivateKey.Hex` | hex string | PrivateKeyType | Development |
16048
+ * | `PrivateKey.Bytes` | Uint8Array | PrivateKeyType | Development |
16049
+ * | `PrivateKey.RedactedHex` | hex string | Redacted<PrivateKeyType> | **Production** |
16050
+ * | `PrivateKey.RedactedBytes` | Uint8Array | Redacted<PrivateKeyType> | **Production** |
16010
16051
  *
16011
- * ## Usage
16052
+ * ## Redacted Schemas (Recommended)
16053
+ *
16054
+ * Use `RedactedHex` or `RedactedBytes` in production to prevent accidental logging:
16012
16055
  *
16013
16056
  * ```typescript
16014
16057
  * import * as PrivateKey from 'voltaire-effect/primitives/PrivateKey'
16058
+ * import { Redacted } from 'effect'
16015
16059
  * import * as S from 'effect/Schema'
16016
16060
  *
16017
- * // Decode (parse input)
16018
- * const pk = S.decodeSync(PrivateKey.Hex)('0x0123456789abcdef...')
16061
+ * const pk = S.decodeSync(PrivateKey.RedactedHex)('0x0123...')
16062
+ * console.log(pk) // Redacted(<redacted>)
16019
16063
  *
16020
- * // Encode (format output)
16021
- * const hex = S.encodeSync(PrivateKey.Hex)(pk)
16022
- * const bytes = S.encodeSync(PrivateKey.Bytes)(pk)
16064
+ * // Explicit unwrap for crypto operations
16065
+ * const unwrapped = Redacted.value(pk)
16066
+ * const sig = Secp256k1.sign(hash, unwrapped)
16023
16067
  * ```
16024
16068
  *
16025
- * ## Cryptographic Operations
16069
+ * ## Standard Usage
16026
16070
  *
16027
16071
  * ```typescript
16028
- * import * as Effect from 'effect/Effect'
16072
+ * import * as PrivateKey from 'voltaire-effect/primitives/PrivateKey'
16073
+ * import * as S from 'effect/Schema'
16029
16074
  *
16030
- * const program = Effect.gen(function* () {
16031
- * const pk = yield* PrivateKey.random()
16032
- * const publicKey = yield* PrivateKey.toPublicKey(pk)
16033
- * const address = yield* PrivateKey.toAddress(pk)
16034
- * const signature = yield* PrivateKey.sign(pk, messageHash)
16035
- * return { publicKey, address, signature }
16036
- * })
16075
+ * const pk = S.decodeSync(PrivateKey.Hex)('0x0123456789abcdef...')
16076
+ * const hex = S.encodeSync(PrivateKey.Hex)(pk)
16037
16077
  * ```
16038
16078
  *
16039
16079
  * ## Pure Functions
16040
16080
  *
16041
16081
  * ```typescript
16042
16082
  * PrivateKey.isValid(value) // Effect<boolean>
16083
+ * PrivateKey.random() // Effect<PrivateKeyType>
16043
16084
  * ```
16044
16085
  *
16045
16086
  * @since 0.1.0
16046
16087
  */
16047
16088
 
16089
+ declare const index$O_RedactedBytes: typeof RedactedBytes;
16090
+ declare const index$O_RedactedHex: typeof RedactedHex;
16048
16091
  declare const index$O_random: typeof random;
16049
16092
  declare namespace index$O {
16050
- export { Bytes$d as Bytes, Hex$d as Hex, isValid$2 as isValid, index$O_random as random };
16093
+ export { Bytes$d as Bytes, Hex$d as Hex, index$O_RedactedBytes as RedactedBytes, index$O_RedactedHex as RedactedHex, isValid$2 as isValid, index$O_random as random };
16051
16094
  }
16052
16095
 
16053
16096
  type ProofType$1 = Proof$4.ProofType;