@corpus-core/colibri-stateless 1.1.1 → 1.1.3

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.
@@ -0,0 +1,314 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2025 corpus.core
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ * this software and associated documentation files (the "Software"), to deal in
7
+ * the Software without restriction, including without limitation the rights to
8
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ * the Software, and to permit persons to whom the Software is furnished to do so,
10
+ * subject to the following conditions:
11
+ *
12
+ * The above copyright notice and this permission notice shall be included in all
13
+ * copies or substantial portions of the Software.
14
+ *
15
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ *
22
+ * SPDX-License-Identifier: MIT
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.SubscriptionManager = void 0;
26
+ const types_js_1 = require("./types.js"); // Removed unused Config, DataRequest, RequestArguments
27
+ const DEFAULT_POLLING_INTERVAL = 12000; // 12 seconds
28
+ class SubscriptionManager {
29
+ activeSubscriptions = new Map();
30
+ eventEmitter;
31
+ rpcCall;
32
+ config;
33
+ nextSubscriptionId = 0;
34
+ constructor(rpcCallCallback, eventEmitter, config = {}) {
35
+ this.rpcCall = rpcCallCallback;
36
+ this.eventEmitter = eventEmitter;
37
+ this.config = {
38
+ pollingInterval: DEFAULT_POLLING_INTERVAL,
39
+ ...config,
40
+ };
41
+ }
42
+ // New method to handle requests related to subscriptions and filters
43
+ handleRequest(method, paramsArray) {
44
+ switch (method) {
45
+ case 'eth_subscribe':
46
+ if (paramsArray.length < 1 || typeof paramsArray[0] !== 'string') {
47
+ return Promise.reject(new types_js_1.ProviderRpcError(-32602, 'Invalid params for eth_subscribe: missing subscription type.'));
48
+ }
49
+ return this.subscribe(paramsArray[0], paramsArray.slice(1));
50
+ case 'eth_unsubscribe':
51
+ if (paramsArray.length < 1 || typeof paramsArray[0] !== 'string') {
52
+ return Promise.reject(new types_js_1.ProviderRpcError(-32602, 'Invalid params for eth_unsubscribe: missing or invalid subscription ID.'));
53
+ }
54
+ return this.unsubscribe(paramsArray[0]);
55
+ case 'eth_newBlockFilter':
56
+ return this.newFilter('block');
57
+ case 'eth_newPendingTransactionFilter':
58
+ // This will internally throw, but we return the promise from newFilter
59
+ return this.newFilter('pendingTransaction');
60
+ case 'eth_newFilter': // For log filters
61
+ return this.newFilter('log', paramsArray);
62
+ case 'eth_getFilterChanges':
63
+ if (paramsArray.length < 1 || typeof paramsArray[0] !== 'string') {
64
+ return Promise.reject(new types_js_1.ProviderRpcError(-32602, 'Invalid params for eth_getFilterChanges: missing or invalid filter ID.'));
65
+ }
66
+ return this.getFilterChanges(paramsArray[0]);
67
+ case 'eth_uninstallFilter':
68
+ if (paramsArray.length < 1 || typeof paramsArray[0] !== 'string') {
69
+ return Promise.reject(new types_js_1.ProviderRpcError(-32602, 'Invalid params for eth_uninstallFilter: missing or invalid filter ID.'));
70
+ }
71
+ return this.uninstallFilter(paramsArray[0]);
72
+ default:
73
+ // Not a method handled by SubscriptionManager
74
+ return null;
75
+ }
76
+ }
77
+ generateSubscriptionId() {
78
+ this.nextSubscriptionId++;
79
+ // Simple hex string, similar to what nodes return
80
+ return `0x${this.nextSubscriptionId.toString(16)}`;
81
+ }
82
+ // For eth_subscribe
83
+ async subscribe(kind, params = []) {
84
+ if (this.config.debug) {
85
+ console.log(`[SubMan] eth_subscribe: kind=${kind}, params=`, params);
86
+ }
87
+ const subId = this.generateSubscriptionId();
88
+ let subscriptionDetails = {
89
+ id: subId,
90
+ subscriptionMethod: 'eth_subscribe',
91
+ kind: kind,
92
+ };
93
+ switch (kind) {
94
+ case 'newHeads':
95
+ // No specific params needed from client for newHeads
96
+ break;
97
+ case 'logs':
98
+ subscriptionDetails.params = params[0] || {};
99
+ break;
100
+ default:
101
+ // Should not happen if kind is correctly typed
102
+ throw new types_js_1.ProviderRpcError(4200, `Subscription kind "${kind}" is not supported by eth_subscribe.`);
103
+ }
104
+ const finalSubscription = subscriptionDetails;
105
+ this.activeSubscriptions.set(subId, finalSubscription);
106
+ this.schedulePoll(finalSubscription, 0); // Start active polling
107
+ if (this.config.debug) {
108
+ console.log(`[SubMan] Created eth_subscribe subscription: ${subId}`, finalSubscription);
109
+ }
110
+ return subId;
111
+ }
112
+ // For eth_newFilter, eth_newBlockFilter, eth_newPendingTransactionFilter
113
+ async newFilter(kind, params = []) {
114
+ if (this.config.debug) {
115
+ console.log(`[SubMan] eth_newFilter: kind=${kind}, params=`, params);
116
+ }
117
+ if (kind === 'pendingTransaction') {
118
+ if (this.config.debug)
119
+ console.warn('[SubMan] eth_newPendingTransactionFilter is not supported.');
120
+ throw new types_js_1.ProviderRpcError(4200, 'eth_newPendingTransactionFilter is not supported.');
121
+ }
122
+ const filterId = this.generateSubscriptionId();
123
+ const filterDetails = {
124
+ id: filterId,
125
+ subscriptionMethod: 'eth_newFilter',
126
+ kind: kind,
127
+ params: kind === 'log' ? (params[0] || {}) : undefined,
128
+ // No timeoutId as polling is manual via getFilterChanges
129
+ };
130
+ this.activeSubscriptions.set(filterId, filterDetails);
131
+ if (this.config.debug) {
132
+ console.log(`[SubMan] Created eth_newFilter filter: ${filterId}`, filterDetails);
133
+ }
134
+ return filterId;
135
+ }
136
+ // For eth_getFilterChanges
137
+ async getFilterChanges(filterId) {
138
+ const sub = this.activeSubscriptions.get(filterId);
139
+ if (!sub || sub.subscriptionMethod !== 'eth_newFilter') {
140
+ if (this.config.debug)
141
+ console.warn(`[SubMan] getFilterChanges: Filter not found or not a filter type for ID ${filterId}`);
142
+ throw new types_js_1.ProviderRpcError(-32000, 'Filter not found.'); // Standard Ethereum error for missing filter
143
+ }
144
+ if (this.config.debug) {
145
+ console.log(`[SubMan] getFilterChanges for filter: ${filterId}, kind: ${sub.kind}`);
146
+ }
147
+ // Perform a one-time poll based on the filter kind
148
+ return this.executePollLogic(sub, true); // Pass true to indicate results should be returned, not emitted
149
+ }
150
+ // For eth_uninstallFilter
151
+ async uninstallFilter(filterId) {
152
+ const sub = this.activeSubscriptions.get(filterId);
153
+ if (sub && sub.subscriptionMethod === 'eth_newFilter') {
154
+ this.activeSubscriptions.delete(filterId);
155
+ // No timeout to clear for filters
156
+ if (this.config.debug) {
157
+ console.log(`[SubMan] Uninstalled filter: ${filterId}`);
158
+ }
159
+ return true;
160
+ }
161
+ if (this.config.debug) {
162
+ console.warn(`[SubMan] UninstallFilter failed: Filter not found or not a filter type for ID ${filterId}`);
163
+ }
164
+ return false;
165
+ }
166
+ // Common polling logic, can return results or emit them via eventEmitter
167
+ async executePollLogic(sub, returnResults = false) {
168
+ let results = [];
169
+ let currentVerifiableBlockNumber = null;
170
+ try {
171
+ const blockNumberHex = await this.rpcCall('eth_blockNumber', []);
172
+ if (blockNumberHex)
173
+ currentVerifiableBlockNumber = BigInt(blockNumberHex);
174
+ }
175
+ catch (e) {
176
+ if (this.config.debug)
177
+ console.warn('[SubMan] Could not fetch eth_blockNumber for polling context:', e);
178
+ }
179
+ if (sub.kind === 'newHeads' || sub.kind === 'block') { // block is for eth_newBlockFilter
180
+ if (currentVerifiableBlockNumber) {
181
+ let fromBlock = sub.lastPolledBlockNumber ? sub.lastPolledBlockNumber + 1n : currentVerifiableBlockNumber;
182
+ if (sub.kind === 'newHeads') { // For newHeads, we only care if the latest is newer
183
+ if (!sub.lastPolledBlockNumber)
184
+ sub.lastPolledBlockNumber = currentVerifiableBlockNumber;
185
+ if (currentVerifiableBlockNumber <= sub.lastPolledBlockNumber)
186
+ return []; // No new head yet for eth_subscribe('newHeads')
187
+ }
188
+ for (let blockNum = fromBlock; blockNum <= currentVerifiableBlockNumber; blockNum++) {
189
+ try {
190
+ const block = await this.rpcCall('eth_getBlockByNumber', ['0x' + blockNum.toString(16), sub.kind === 'newHeads' /* full block for newHeads */]);
191
+ if (block && this.activeSubscriptions.has(sub.id)) {
192
+ if (sub.kind === 'newHeads')
193
+ results.push(block);
194
+ else if (sub.kind === 'block')
195
+ results.push(block.hash); // eth_newBlockFilter returns block hashes
196
+ }
197
+ }
198
+ catch (e) {
199
+ if (this.config.debug)
200
+ console.error(`[SubMan] Error fetching block ${blockNum} for sub/filter ${sub.id}:`, e);
201
+ if (sub.kind === 'block')
202
+ break; // For block filters, stop if a block in sequence fails
203
+ }
204
+ }
205
+ if (results.length > 0 && sub.kind === 'newHeads' && !returnResults && this.activeSubscriptions.has(sub.id)) {
206
+ // For newHeads subscription, emit each block individually or as an array?
207
+ // EIP-1193 usually implies one result per message for newHeads
208
+ results.forEach(blockResult => {
209
+ this.eventEmitter.emit('message', {
210
+ type: 'eth_subscription',
211
+ data: { subscription: sub.id, result: blockResult }
212
+ });
213
+ });
214
+ if (this.config.debug)
215
+ console.log(`[SubMan] Emitted ${results.length} newHead(s) for sub ${sub.id}`);
216
+ }
217
+ sub.lastPolledBlockNumber = currentVerifiableBlockNumber;
218
+ }
219
+ }
220
+ else if (sub.kind === 'logs' || sub.kind === 'log') { // 'log' is for eth_newFilter(logs)
221
+ const filterParams = { ...(sub.params || {}) };
222
+ if (sub.lastPolledBlockNumber) {
223
+ filterParams.fromBlock = '0x' + (sub.lastPolledBlockNumber + 1n).toString(16);
224
+ }
225
+ else if (!filterParams.fromBlock && currentVerifiableBlockNumber) {
226
+ filterParams.fromBlock = '0x' + currentVerifiableBlockNumber.toString(16);
227
+ }
228
+ if (currentVerifiableBlockNumber) {
229
+ filterParams.toBlock = '0x' + currentVerifiableBlockNumber.toString(16);
230
+ }
231
+ else if (!filterParams.toBlock) {
232
+ filterParams.toBlock = 'latest';
233
+ }
234
+ if (filterParams.fromBlock && filterParams.toBlock && filterParams.toBlock !== 'latest' && BigInt(filterParams.fromBlock) > BigInt(filterParams.toBlock)) {
235
+ // Skip if fromBlock is ahead
236
+ }
237
+ else {
238
+ try {
239
+ const logs = await this.rpcCall('eth_getLogs', [filterParams]);
240
+ if (logs && logs.length > 0 && this.activeSubscriptions.has(sub.id)) {
241
+ results = logs;
242
+ if (!returnResults) { // For eth_subscribe('logs')
243
+ this.eventEmitter.emit('message', {
244
+ type: 'eth_subscription',
245
+ data: { subscription: sub.id, result: logs } // Usually an array of logs
246
+ });
247
+ if (this.config.debug)
248
+ console.log(`[SubMan] Emitted ${logs.length} logs for sub ${sub.id}`);
249
+ }
250
+ }
251
+ if (filterParams.toBlock !== 'latest')
252
+ sub.lastPolledBlockNumber = BigInt(filterParams.toBlock);
253
+ else if (currentVerifiableBlockNumber)
254
+ sub.lastPolledBlockNumber = currentVerifiableBlockNumber;
255
+ }
256
+ catch (e) {
257
+ if (this.config.debug)
258
+ console.error(`[SubMan] Error fetching logs for sub/filter ${sub.id}:`, e);
259
+ }
260
+ }
261
+ }
262
+ return results;
263
+ }
264
+ schedulePoll(sub, delay) {
265
+ if (!this.activeSubscriptions.has(sub.id) || sub.subscriptionMethod === 'eth_newFilter')
266
+ return;
267
+ sub.timeoutId = setTimeout(async () => {
268
+ try {
269
+ if (!this.activeSubscriptions.has(sub.id))
270
+ return;
271
+ await this.pollSubscriptionAndEmit(sub); // Renamed from pollSubscription for clarity
272
+ }
273
+ catch (error) {
274
+ console.error(`[SubMan] Unexpected error in poll scheduling for subscription ${sub.id}:`, error);
275
+ }
276
+ }, delay);
277
+ }
278
+ // Wrapper for eth_subscribe polling that emits results and reschedules
279
+ async pollSubscriptionAndEmit(sub) {
280
+ if (!this.activeSubscriptions.has(sub.id)) {
281
+ if (this.config.debug)
282
+ console.log(`[SubMan] Subscription ${sub.id} was unsubscribed. Skipping poll.`);
283
+ if (sub.timeoutId)
284
+ clearTimeout(sub.timeoutId);
285
+ return;
286
+ }
287
+ if (this.config.debug) {
288
+ console.log(`[SubMan] Polling for eth_subscribe: ${sub.id}, kind: ${sub.kind}`);
289
+ }
290
+ await this.executePollLogic(sub, false); // false means emit results, don't return them
291
+ if (this.activeSubscriptions.has(sub.id)) {
292
+ this.schedulePoll(sub, this.config.pollingInterval || DEFAULT_POLLING_INTERVAL);
293
+ }
294
+ }
295
+ // For eth_unsubscribe (specific to eth_subscribe subscriptions)
296
+ async unsubscribe(subscriptionId) {
297
+ const sub = this.activeSubscriptions.get(subscriptionId);
298
+ if (sub && sub.subscriptionMethod === 'eth_subscribe') {
299
+ if (sub.timeoutId) {
300
+ clearTimeout(sub.timeoutId);
301
+ }
302
+ this.activeSubscriptions.delete(subscriptionId);
303
+ if (this.config.debug) {
304
+ console.log(`[SubMan] Unsubscribed (eth_subscribe): ${subscriptionId}`);
305
+ }
306
+ return true;
307
+ }
308
+ if (this.config.debug) {
309
+ console.warn(`[SubMan] Unsubscribe failed: Subscription not found or not an eth_subscribe type for ID ${subscriptionId}`);
310
+ }
311
+ return false;
312
+ }
313
+ }
314
+ exports.SubscriptionManager = SubscriptionManager;
@@ -0,0 +1,202 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2025 corpus.core
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ * this software and associated documentation files (the "Software"), to deal in
7
+ * the Software without restriction, including without limitation the rights to
8
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ * the Software, and to permit persons to whom the Software is furnished to do so,
10
+ * subject to the following conditions:
11
+ *
12
+ * The above copyright notice and this permission notice shall be included in all
13
+ * copies or substantial portions of the Software.
14
+ *
15
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ *
22
+ * SPDX-License-Identifier: MIT
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.TransactionUtils = exports.PrototypeProtection = exports.TransactionVerifier = void 0;
26
+ const types_js_1 = require("./types.js");
27
+ class TransactionVerifier {
28
+ /**
29
+ * Verifizierte Ausführung von eth_sendTransaction
30
+ * 1. Transaction über fallbackProvider signieren lassen
31
+ * 2. Signierte Transaction mit colibri_decodeTransaction dekodieren
32
+ * 3. Dekodierte Parameter mit Original vergleichen
33
+ * 4. eth_sendRawTransaction ausführen
34
+ */
35
+ static async verifyAndSendTransaction(txObject, config, rpcMethod, fetchRpc) {
36
+ if (config.debug)
37
+ console.log('[TransactionVerifier] Verifying transaction before sending:', txObject);
38
+ if (!config.fallback_provider) {
39
+ throw new types_js_1.ProviderRpcError(4203, 'Transaction verification requires fallback_provider to be configured');
40
+ }
41
+ try {
42
+ // 1. Transaction über fallbackProvider signieren lassen
43
+ const signedRawTx = await config.fallback_provider.request({
44
+ method: 'eth_signTransaction',
45
+ params: [txObject]
46
+ });
47
+ // 2. Signierte Transaction dekodieren
48
+ const decodedTx = await rpcMethod('colibri_decodeTransaction', [signedRawTx], types_js_1.MethodType.LOCAL);
49
+ // 3. Dekodierte Parameter mit Original vergleichen
50
+ const isValid = this.compareTransactionParameters(txObject, decodedTx, config);
51
+ if (!isValid) {
52
+ throw new types_js_1.ProviderRpcError(4201, 'Signed transaction does not match original parameters');
53
+ }
54
+ // 4. Verifizierte Transaction senden
55
+ return await fetchRpc(config.rpcs, {
56
+ method: 'eth_sendRawTransaction',
57
+ params: [signedRawTx]
58
+ }, false);
59
+ }
60
+ catch (error) {
61
+ if (config.debug)
62
+ console.error('[TransactionVerifier] Transaction verification failed:', error);
63
+ throw new types_js_1.ProviderRpcError(4202, `Transaction verification failed: ${error.message}`);
64
+ }
65
+ }
66
+ /**
67
+ * Vergleicht die Parameter einer ursprünglichen Transaction mit einer dekodierten signierten Transaction
68
+ */
69
+ static compareTransactionParameters(original, decoded, config) {
70
+ if (config.debug) {
71
+ console.log('[TransactionVerifier] Comparing transaction parameters:');
72
+ console.log('Original:', original);
73
+ console.log('Decoded:', decoded);
74
+ }
75
+ // Kritische Parameter vergleichen
76
+ const criticalFields = ['to', 'value', 'data', 'gas', 'gasPrice', 'maxFeePerGas', 'maxPriorityFeePerGas', 'nonce'];
77
+ for (const field of criticalFields) {
78
+ if (original[field] !== undefined) {
79
+ // Normalisierung für Hex-Werte (0x-Prefix, Leading Zeros)
80
+ const originalValue = this.normalizeHexValue(original[field]);
81
+ const decodedValue = this.normalizeHexValue(decoded[field]);
82
+ if (originalValue !== decodedValue) {
83
+ if (config.debug) {
84
+ console.warn(`[TransactionVerifier] Transaction parameter mismatch in '${field}': original='${originalValue}', decoded='${decodedValue}'`);
85
+ }
86
+ return false;
87
+ }
88
+ }
89
+ }
90
+ // ChainId prüfen
91
+ if (decoded.chainId && parseInt(decoded.chainId, 16) !== parseInt(config.chainId)) {
92
+ if (config.debug) {
93
+ console.warn(`[TransactionVerifier] ChainId mismatch: expected=${config.chainId}, decoded=${parseInt(decoded.chainId, 16)}`);
94
+ }
95
+ return false;
96
+ }
97
+ return true;
98
+ }
99
+ /**
100
+ * Normalisiert Hex-Werte für Vergleiche (entfernt führende Nullen, stellt 0x-Prefix sicher)
101
+ */
102
+ static normalizeHexValue(value) {
103
+ if (value === undefined || value === null)
104
+ return '';
105
+ if (typeof value === 'number')
106
+ return '0x' + value.toString(16);
107
+ const str = value.toString();
108
+ if (!str.startsWith('0x'))
109
+ return '0x' + str;
110
+ // Führende Nullen entfernen, aber mindestens eine Ziffer behalten
111
+ const withoutPrefix = str.slice(2);
112
+ const normalized = withoutPrefix.replace(/^0+/, '') || '0';
113
+ return '0x' + normalized;
114
+ }
115
+ }
116
+ exports.TransactionVerifier = TransactionVerifier;
117
+ /**
118
+ * Prototype Protection - Schutz vor NPM Supply-Chain-Attacks
119
+ *
120
+ * Friert kritische Prototypen und Methoden ein, um Manipulation zu verhindern
121
+ */
122
+ class PrototypeProtection {
123
+ /**
124
+ * Schützt eine Klasse vor Prototype Pollution
125
+ */
126
+ static protectClass(targetClass, criticalMethods = []) {
127
+ // Einfrieren des Prototypes
128
+ Object.freeze(targetClass.prototype);
129
+ // Kritische Methoden gegen Überschreibung schützen
130
+ criticalMethods.forEach(method => {
131
+ const descriptor = Object.getOwnPropertyDescriptor(targetClass.prototype, method);
132
+ if (descriptor) {
133
+ Object.defineProperty(targetClass.prototype, method, {
134
+ ...descriptor,
135
+ writable: false,
136
+ configurable: false
137
+ });
138
+ }
139
+ });
140
+ }
141
+ /**
142
+ * Schützt ein Konfigurationsobjekt vor Manipulation
143
+ */
144
+ static protectConfig(config, criticalArrays = []) {
145
+ // Kritische Arrays einfrieren
146
+ criticalArrays.forEach(arrayName => {
147
+ if (config[arrayName] && Array.isArray(config[arrayName])) {
148
+ Object.freeze(config[arrayName]);
149
+ }
150
+ });
151
+ }
152
+ }
153
+ exports.PrototypeProtection = PrototypeProtection;
154
+ /**
155
+ * Transaction Utilities - Hilfsfunktionen für Transaction-Handling
156
+ */
157
+ class TransactionUtils {
158
+ /**
159
+ * Validiert ein Transaction-Objekt
160
+ */
161
+ static validateTransactionObject(txObject) {
162
+ if (!txObject || typeof txObject !== 'object')
163
+ return false;
164
+ // Mindestanforderungen
165
+ if (!txObject.to && !txObject.data)
166
+ return false; // Entweder to-Adresse oder data für Contract-Creation
167
+ return true;
168
+ }
169
+ /**
170
+ * Bereinigt Transaction-Parameter für verschiedene Standards
171
+ */
172
+ static cleanupTransactionParams(txObject) {
173
+ const cleaned = { ...txObject };
174
+ // EIP-1559 vs Legacy Gas-Handling
175
+ if (cleaned.maxFeePerGas || cleaned.maxPriorityFeePerGas) {
176
+ // EIP-1559 - gasPrice entfernen
177
+ delete cleaned.gasPrice;
178
+ }
179
+ else if (cleaned.gasPrice) {
180
+ // Legacy - EIP-1559 Felder entfernen
181
+ delete cleaned.maxFeePerGas;
182
+ delete cleaned.maxPriorityFeePerGas;
183
+ }
184
+ return cleaned;
185
+ }
186
+ /**
187
+ * Erstellt eine Zusammenfassung einer Transaction für Logging
188
+ */
189
+ static summarizeTransaction(txObject) {
190
+ const to = txObject.to || 'Contract Creation';
191
+ const value = txObject.value ? `${parseInt(txObject.value, 16)} wei` : '0 wei';
192
+ const gas = txObject.gas ? parseInt(txObject.gas, 16) : 'auto';
193
+ return `Transaction(to: ${to}, value: ${value}, gas: ${gas})`;
194
+ }
195
+ }
196
+ exports.TransactionUtils = TransactionUtils;
197
+ // Export aller Klassen als Default
198
+ exports.default = {
199
+ TransactionVerifier,
200
+ PrototypeProtection,
201
+ TransactionUtils
202
+ };
package/cjs/types.js ADDED
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2025 corpus.core
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ * this software and associated documentation files (the "Software"), to deal in
7
+ * the Software without restriction, including without limitation the rights to
8
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ * the Software, and to permit persons to whom the Software is furnished to do so,
10
+ * subject to the following conditions:
11
+ *
12
+ * The above copyright notice and this permission notice shall be included in all
13
+ * copies or substantial portions of the Software.
14
+ *
15
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ *
22
+ * SPDX-License-Identifier: MIT
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.MethodType = exports.ProviderRpcError = void 0;
26
+ // --- EIP-1193 & C4Client Specific Types ---
27
+ // From EIP-1193: Error structure
28
+ class ProviderRpcError extends Error {
29
+ code;
30
+ data;
31
+ constructor(code, message, data) {
32
+ super(message);
33
+ this.name = 'ProviderRpcError';
34
+ this.code = code;
35
+ if (data !== undefined) {
36
+ this.data = data;
37
+ }
38
+ Object.setPrototypeOf(this, ProviderRpcError.prototype);
39
+ }
40
+ static createError(error, args) {
41
+ let providerError;
42
+ if (error instanceof ProviderRpcError) {
43
+ providerError = error;
44
+ }
45
+ else {
46
+ let code;
47
+ let message = (error?.message || 'An unknown error occurred');
48
+ const originalData = error?.data;
49
+ if (typeof message === 'string' && message.startsWith('Method ') && message.endsWith(' is not supported')) {
50
+ code = 4200;
51
+ }
52
+ else if (typeof message === 'string' && (message.includes('HTTP error!') || message.includes('All nodes failed') || message.includes('Failed to fetch'))) {
53
+ code = 4900;
54
+ }
55
+ else {
56
+ code = (typeof error?.code === 'number') ? error.code : -32603;
57
+ }
58
+ providerError = new ProviderRpcError(code, 'Error in rpc call ' + (args?.method || '') + JSON.stringify(args?.params || []) + ' : ' + message, originalData);
59
+ }
60
+ return providerError;
61
+ }
62
+ }
63
+ exports.ProviderRpcError = ProviderRpcError;
64
+ // Enum for RPC method types
65
+ var MethodType;
66
+ (function (MethodType) {
67
+ MethodType[MethodType["PROOFABLE"] = 1] = "PROOFABLE";
68
+ MethodType[MethodType["UNPROOFABLE"] = 2] = "UNPROOFABLE";
69
+ MethodType[MethodType["NOT_SUPPORTED"] = 3] = "NOT_SUPPORTED";
70
+ MethodType[MethodType["LOCAL"] = 4] = "LOCAL";
71
+ })(MethodType || (exports.MethodType = MethodType = {}));
package/cjs/wasm.js ADDED
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2025 corpus.core
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ * this software and associated documentation files (the "Software"), to deal in
7
+ * the Software without restriction, including without limitation the rights to
8
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ * the Software, and to permit persons to whom the Software is furnished to do so,
10
+ * subject to the following conditions:
11
+ *
12
+ * The above copyright notice and this permission notice shall be included in all
13
+ * copies or substantial portions of the Software.
14
+ *
15
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ *
22
+ * SPDX-License-Identifier: MIT
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.set_trusted_checkpoint = exports.get_prover_config_hex = exports.getC4w = exports.loadC4WModule = exports.set_wasm_url = exports.get_default_storage = exports.copy_to_c = exports.as_json = exports.as_char_ptr = exports.as_bytes = void 0;
26
+ // This file is used only for the CommonJS build output. It must not use
27
+ // `import.meta` because that is a parse error in CommonJS environments (e.g. Jest).
28
+ const node_path_1 = require("node:path");
29
+ const node_url_1 = require("node:url");
30
+ const wasm_shared_js_1 = require("./wasm_shared.js");
31
+ Object.defineProperty(exports, "as_bytes", { enumerable: true, get: function () { return wasm_shared_js_1.as_bytes; } });
32
+ Object.defineProperty(exports, "as_char_ptr", { enumerable: true, get: function () { return wasm_shared_js_1.as_char_ptr; } });
33
+ Object.defineProperty(exports, "as_json", { enumerable: true, get: function () { return wasm_shared_js_1.as_json; } });
34
+ Object.defineProperty(exports, "copy_to_c", { enumerable: true, get: function () { return wasm_shared_js_1.copy_to_c; } });
35
+ Object.defineProperty(exports, "get_default_storage", { enumerable: true, get: function () { return wasm_shared_js_1.get_default_storage; } });
36
+ async function dynamicImport(specifier) {
37
+ // Prevent TypeScript from transforming `import()` into `require()` in CJS output.
38
+ // Node supports dynamic import in CommonJS.
39
+ const importer = new Function('p', 'return import(p)');
40
+ return importer(specifier);
41
+ }
42
+ const api = (0, wasm_shared_js_1.createC4wApi)({
43
+ importC4wModule: async () => {
44
+ const pkgRoot = (0, node_path_1.join)(__dirname, '..');
45
+ const glueUrl = (0, node_url_1.pathToFileURL)((0, node_path_1.join)(pkgRoot, 'c4w.js')).href;
46
+ return await dynamicImport(glueUrl);
47
+ },
48
+ resolveWasmLocation: (override) => {
49
+ // In CJS/Node we provide wasmBinary, so we don't need locateFile here.
50
+ // Keep override support for browser-like loaders (if someone uses it).
51
+ if (override)
52
+ return override;
53
+ return null;
54
+ },
55
+ getWasmBinary: async (override) => {
56
+ const fs = await dynamicImport('node:fs');
57
+ const wasmPath = override ?? (0, node_path_1.join)(__dirname, '..', 'c4w.wasm');
58
+ return fs.readFileSync(wasmPath);
59
+ },
60
+ });
61
+ exports.set_wasm_url = api.set_wasm_url;
62
+ exports.loadC4WModule = api.loadC4WModule;
63
+ exports.getC4w = api.getC4w;
64
+ exports.get_prover_config_hex = api.get_prover_config_hex;
65
+ exports.set_trusted_checkpoint = api.set_trusted_checkpoint;