@lukso/transaction-decoder 1.0.1-dev.0f1bea5
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/LICENSE +201 -0
- package/README.md +486 -0
- package/dist/browser.cjs +6912 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.d.cts +6 -0
- package/dist/browser.d.ts +6 -0
- package/dist/browser.js +131 -0
- package/dist/browser.js.map +1 -0
- package/dist/cdn/transaction-decoder.global.js +296 -0
- package/dist/cdn/transaction-decoder.global.js.map +1 -0
- package/dist/chunk-GGBHTWJL.js +437 -0
- package/dist/chunk-GGBHTWJL.js.map +1 -0
- package/dist/chunk-GXZOF3QY.js +839 -0
- package/dist/chunk-GXZOF3QY.js.map +1 -0
- package/dist/chunk-LJ6ES5XF.js +776 -0
- package/dist/chunk-LJ6ES5XF.js.map +1 -0
- package/dist/chunk-XVHJWV5U.js +4925 -0
- package/dist/chunk-XVHJWV5U.js.map +1 -0
- package/dist/data.cjs +5518 -0
- package/dist/data.cjs.map +1 -0
- package/dist/data.d.cts +43 -0
- package/dist/data.d.ts +43 -0
- package/dist/data.js +55 -0
- package/dist/data.js.map +1 -0
- package/dist/index-BzXh7poJ.d.cts +524 -0
- package/dist/index-BzXh7poJ.d.ts +524 -0
- package/dist/index.cjs +6912 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +756 -0
- package/dist/index.d.ts +756 -0
- package/dist/index.js +131 -0
- package/dist/index.js.map +1 -0
- package/dist/server.cjs +5644 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +217 -0
- package/dist/server.d.ts +217 -0
- package/dist/server.js +644 -0
- package/dist/server.js.map +1 -0
- package/dist/utils-CBAkjQh3.d.cts +108 -0
- package/dist/utils-xT9-km0r.d.ts +108 -0
- package/package.json +101 -0
- package/src/browser.ts +13 -0
- package/src/client/resolveAddresses.ts +157 -0
- package/src/core/addressCollector.ts +153 -0
- package/src/core/addressResolver.ts +135 -0
- package/src/core/dataModel.ts +888 -0
- package/src/core/instance.ts +33 -0
- package/src/core/integrateDecoder.ts +325 -0
- package/src/data.ts +70 -0
- package/src/decoder/GENERATOR_PROPOSAL.md +182 -0
- package/src/decoder/THREE_PHASE_EXAMPLE.md +108 -0
- package/src/decoder/aggregation.ts +218 -0
- package/src/decoder/browserCache.ts +237 -0
- package/src/decoder/cache/README.md +126 -0
- package/src/decoder/cache/index.ts +44 -0
- package/src/decoder/cache.ts +139 -0
- package/src/decoder/constants.ts +125 -0
- package/src/decoder/decodeTransaction.ts +292 -0
- package/src/decoder/errors.ts +95 -0
- package/src/decoder/events.ts +192 -0
- package/src/decoder/functionSignature.ts +344 -0
- package/src/decoder/getDataFromExternalSources.ts +248 -0
- package/src/decoder/graphqlWS.ts +22 -0
- package/src/decoder/interfaces.ts +185 -0
- package/src/decoder/keyValue.ts +5 -0
- package/src/decoder/kvCache.ts +241 -0
- package/src/decoder/lruCache.ts +184 -0
- package/src/decoder/lsp7Mint.test.ts +179 -0
- package/src/decoder/lsp7TransferBatch.test.ts +105 -0
- package/src/decoder/plugins/RegistryAbi.ts +562 -0
- package/src/decoder/plugins/enhanceBurntPix.ts +132 -0
- package/src/decoder/plugins/enhanceGraffiti.ts +70 -0
- package/src/decoder/plugins/enhanceLSP0ERC725Account.ts +179 -0
- package/src/decoder/plugins/enhanceLSP26FollowerSystem.ts +88 -0
- package/src/decoder/plugins/enhanceLSP6KeyManager.ts +231 -0
- package/src/decoder/plugins/enhanceLSP7DigitalAsset.ts +165 -0
- package/src/decoder/plugins/enhanceLSP8IdentifiableDigitalAsset.ts +170 -0
- package/src/decoder/plugins/enhanceLSP9Vault.ts +57 -0
- package/src/decoder/plugins/enhanceRetrieveAbi.ts +85 -0
- package/src/decoder/plugins/enhanceSetData.ts +135 -0
- package/src/decoder/plugins/index.ts +99 -0
- package/src/decoder/plugins/schemaDefault.ts +318 -0
- package/src/decoder/plugins/standardPlugin.ts +202 -0
- package/src/decoder/registry.ts +322 -0
- package/src/decoder/singleGQL.ts +293 -0
- package/src/decoder/transaction.ts +198 -0
- package/src/decoder/types.ts +465 -0
- package/src/decoder/utils.ts +212 -0
- package/src/example/usage.ts +172 -0
- package/src/index.ts +174 -0
- package/src/server/addressResolver.ts +68 -0
- package/src/server/caches.ts +209 -0
- package/src/server/decodeTransactionSync.ts +156 -0
- package/src/server/decodeTransactionsBatch.ts +207 -0
- package/src/server/finishDecoding.ts +116 -0
- package/src/server/index.ts +81 -0
- package/src/server/lsp23Resolver.test.ts +46 -0
- package/src/server/lsp23Resolver.ts +419 -0
- package/src/server/types.ts +168 -0
- package/src/server.ts +22 -0
- package/src/shared/addressResolver.ts +651 -0
- package/src/shared/cache.ts +144 -0
- package/src/shared/constants.ts +21 -0
- package/src/stubs/tty.ts +13 -0
- package/src/stubs/util.ts +42 -0
- package/src/types/index.ts +154 -0
- package/src/types/provider.ts +46 -0
- package/src/umd.ts +13 -0
- package/src/utils/debug.ts +49 -0
- package/src/utils/json-bigint.ts +47 -0
|
@@ -0,0 +1,839 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__name,
|
|
3
|
+
collectDataKeys,
|
|
4
|
+
decodeTransaction,
|
|
5
|
+
isAsyncOperationEnabled,
|
|
6
|
+
pluginRegistry
|
|
7
|
+
} from "./chunk-XVHJWV5U.js";
|
|
8
|
+
|
|
9
|
+
// src/core/dataModel.ts
|
|
10
|
+
import { batch, effect, signal } from "@preact/signals-core";
|
|
11
|
+
import { hexToBigInt, isHex, size, slice } from "viem";
|
|
12
|
+
function deepFreeze(obj) {
|
|
13
|
+
if (obj === null || typeof obj !== "object" || Object.isFrozen(obj)) {
|
|
14
|
+
return obj;
|
|
15
|
+
}
|
|
16
|
+
Object.freeze(obj);
|
|
17
|
+
for (const prop of Object.getOwnPropertyNames(obj)) {
|
|
18
|
+
const value = obj[prop];
|
|
19
|
+
if (value !== null && typeof value === "object") {
|
|
20
|
+
deepFreeze(value);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return obj;
|
|
24
|
+
}
|
|
25
|
+
__name(deepFreeze, "deepFreeze");
|
|
26
|
+
var DataModel = class {
|
|
27
|
+
static {
|
|
28
|
+
__name(this, "DataModel");
|
|
29
|
+
}
|
|
30
|
+
// Nested map structure: Address -> (TokenId | null) -> Signal
|
|
31
|
+
dataSignals = /* @__PURE__ */ new Map();
|
|
32
|
+
// Transaction signals: Hash -> (DecoderIndex | null) -> Signal
|
|
33
|
+
transactionSignals = /* @__PURE__ */ new Map();
|
|
34
|
+
// Keep track of unique transaction hashes in order
|
|
35
|
+
transactionOrder = [];
|
|
36
|
+
options;
|
|
37
|
+
// Track pending resolved data updates
|
|
38
|
+
pendingResolvedUpdate = false;
|
|
39
|
+
constructor(options = {}) {
|
|
40
|
+
this.options = options;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Extract a 20-byte address from a potentially 32-byte hex value
|
|
44
|
+
* If the input is 32 bytes, validates it's zero-padded and takes the rightmost 20 bytes
|
|
45
|
+
* If the input is 20 bytes, returns it as-is
|
|
46
|
+
*/
|
|
47
|
+
extractAddress(hex) {
|
|
48
|
+
if (!isHex(hex)) {
|
|
49
|
+
throw new Error(`Invalid hex value: ${hex}`);
|
|
50
|
+
}
|
|
51
|
+
const bytes = size(hex);
|
|
52
|
+
if (bytes === 32) {
|
|
53
|
+
const first12Bytes = slice(hex, 0, 12);
|
|
54
|
+
if (hexToBigInt(first12Bytes) !== 0n) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`Invalid 32-byte address: first 12 bytes must be zero for a padded address, got ${first12Bytes}`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
return slice(hex, 12, 32);
|
|
60
|
+
}
|
|
61
|
+
if (bytes === 20) {
|
|
62
|
+
return hex;
|
|
63
|
+
}
|
|
64
|
+
throw new Error(`Invalid address length: ${bytes} bytes`);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get or create a signal for a specific key
|
|
68
|
+
*/
|
|
69
|
+
getOrCreateSignal(key) {
|
|
70
|
+
let sig = this.dataSignals.get(key);
|
|
71
|
+
if (!sig) {
|
|
72
|
+
sig = signal({
|
|
73
|
+
loading: false,
|
|
74
|
+
data: void 0,
|
|
75
|
+
error: void 0,
|
|
76
|
+
lastUpdated: void 0
|
|
77
|
+
});
|
|
78
|
+
this.dataSignals.set(key, sig);
|
|
79
|
+
if (this.options.onMissingKeys) {
|
|
80
|
+
setTimeout(() => {
|
|
81
|
+
const missing = this.getMissingKeys();
|
|
82
|
+
if (missing.length > 0) {
|
|
83
|
+
this.options.onMissingKeys?.(missing);
|
|
84
|
+
}
|
|
85
|
+
}, 0);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return sig;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Extract transaction key from transaction JSON
|
|
92
|
+
*/
|
|
93
|
+
getTransactionKey(transaction) {
|
|
94
|
+
if (!transaction || typeof transaction !== "object") {
|
|
95
|
+
throw new Error("Transaction must be an object");
|
|
96
|
+
}
|
|
97
|
+
const tx = transaction;
|
|
98
|
+
if (!tx.hash && !tx.transactionHash) {
|
|
99
|
+
throw new Error("Transaction must have hash or transactionHash");
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
hash: tx.hash || tx.transactionHash,
|
|
103
|
+
decoderIndex: tx.decoderIndex ? tx.decoderIndex : void 0
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get or create the inner map for a transaction hash
|
|
108
|
+
*/
|
|
109
|
+
getOrCreateTransactionMap(hash) {
|
|
110
|
+
const normalizedHash = hash.toLowerCase();
|
|
111
|
+
let map = this.transactionSignals.get(normalizedHash);
|
|
112
|
+
if (!map) {
|
|
113
|
+
map = /* @__PURE__ */ new Map();
|
|
114
|
+
this.transactionSignals.set(normalizedHash, map);
|
|
115
|
+
}
|
|
116
|
+
return map;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Add one or more transactions to the model
|
|
120
|
+
* Collects all addresses from all transactions and marks them as loading
|
|
121
|
+
*/
|
|
122
|
+
addTransactions(jsonTransactions) {
|
|
123
|
+
const transactions = Array.isArray(jsonTransactions) ? jsonTransactions : [jsonTransactions];
|
|
124
|
+
const isSingle = !Array.isArray(jsonTransactions);
|
|
125
|
+
const allAddresses = /* @__PURE__ */ new Set();
|
|
126
|
+
const transactionAddresses = [];
|
|
127
|
+
for (const tx of transactions) {
|
|
128
|
+
const addresses = collectDataKeys(tx);
|
|
129
|
+
transactionAddresses.push(addresses);
|
|
130
|
+
for (const addr of addresses) {
|
|
131
|
+
allAddresses.add(addr);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const uniqueAddresses = Array.from(allAddresses);
|
|
135
|
+
if (uniqueAddresses.length > 0) {
|
|
136
|
+
this.setLoading(uniqueAddresses);
|
|
137
|
+
}
|
|
138
|
+
const results = transactions.map((tx, index) => {
|
|
139
|
+
const key = this.getTransactionKey(tx);
|
|
140
|
+
const normalizedHash = key.hash.toLowerCase();
|
|
141
|
+
const decoderIndex = key.decoderIndex || null;
|
|
142
|
+
const isNewHash = !this.transactionSignals.has(normalizedHash);
|
|
143
|
+
const txMap = this.getOrCreateTransactionMap(normalizedHash);
|
|
144
|
+
if (txMap.has(decoderIndex)) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
`Transaction already exists: ${normalizedHash}:${decoderIndex || 0}`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
const frozenData = deepFreeze(
|
|
150
|
+
structuredClone(tx)
|
|
151
|
+
);
|
|
152
|
+
const sig = signal({
|
|
153
|
+
data: frozenData,
|
|
154
|
+
loading: false,
|
|
155
|
+
addresses: Object.freeze([...transactionAddresses[index]]),
|
|
156
|
+
error: void 0,
|
|
157
|
+
lastUpdated: Date.now(),
|
|
158
|
+
resolvedData: frozenData,
|
|
159
|
+
// Start with the same frozen data
|
|
160
|
+
addressesResolved: false
|
|
161
|
+
});
|
|
162
|
+
txMap.set(decoderIndex, sig);
|
|
163
|
+
if (isNewHash) {
|
|
164
|
+
this.transactionOrder.push(normalizedHash);
|
|
165
|
+
}
|
|
166
|
+
if (this.options.onNewTransaction) {
|
|
167
|
+
this.options.onNewTransaction(tx);
|
|
168
|
+
}
|
|
169
|
+
return sig;
|
|
170
|
+
});
|
|
171
|
+
return isSingle ? results[0] : results;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get an existing transaction signal (returns undefined if not found)
|
|
175
|
+
*/
|
|
176
|
+
getTransaction(jsonTransaction) {
|
|
177
|
+
const key = this.getTransactionKey(jsonTransaction);
|
|
178
|
+
const normalizedHash = key.hash.toLowerCase();
|
|
179
|
+
const decoderIndex = key.decoderIndex || null;
|
|
180
|
+
const txMap = this.transactionSignals.get(normalizedHash);
|
|
181
|
+
if (!txMap) {
|
|
182
|
+
return void 0;
|
|
183
|
+
}
|
|
184
|
+
return txMap.get(decoderIndex);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Get transaction by hash and decoder index
|
|
188
|
+
*/
|
|
189
|
+
getTransactionByKey(hash, decoderIndex) {
|
|
190
|
+
const normalizedHash = hash.toLowerCase();
|
|
191
|
+
const txMap = this.transactionSignals.get(normalizedHash);
|
|
192
|
+
if (!txMap) {
|
|
193
|
+
return void 0;
|
|
194
|
+
}
|
|
195
|
+
return txMap.get(decoderIndex || null);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Update a transaction's data and re-collect addresses
|
|
199
|
+
* Useful after async decoding completes
|
|
200
|
+
*/
|
|
201
|
+
updateTransactionData(hash, newData, decoderIndex) {
|
|
202
|
+
const normalizedHash = hash.toLowerCase();
|
|
203
|
+
const txMap = this.transactionSignals.get(normalizedHash);
|
|
204
|
+
if (!txMap) return;
|
|
205
|
+
const sig = txMap.get(decoderIndex ?? null);
|
|
206
|
+
if (!sig) return;
|
|
207
|
+
const newAddresses = collectDataKeys(newData, true, [
|
|
208
|
+
...sig.value.addresses
|
|
209
|
+
]);
|
|
210
|
+
const frozenData = deepFreeze(structuredClone(newData));
|
|
211
|
+
sig.value = {
|
|
212
|
+
...sig.value,
|
|
213
|
+
data: frozenData,
|
|
214
|
+
addresses: Object.freeze([...newAddresses]),
|
|
215
|
+
lastUpdated: Date.now(),
|
|
216
|
+
// Start with the current data, will be enhanced when addresses load
|
|
217
|
+
resolvedData: frozenData,
|
|
218
|
+
addressesResolved: false
|
|
219
|
+
};
|
|
220
|
+
this.setLoading(newAddresses);
|
|
221
|
+
queueMicrotask(() => {
|
|
222
|
+
this.updateResolvedTransactionData();
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Get all decoded transactions for a hash by index (insertion order)
|
|
227
|
+
*/
|
|
228
|
+
getTransactionsByIndex(index) {
|
|
229
|
+
if (index < 0 || index >= this.transactionOrder.length) {
|
|
230
|
+
return void 0;
|
|
231
|
+
}
|
|
232
|
+
const hash = this.transactionOrder[index];
|
|
233
|
+
return this.transactionSignals.get(hash);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Get transaction hash by index
|
|
237
|
+
*/
|
|
238
|
+
getTransactionHashByIndex(index) {
|
|
239
|
+
return this.transactionOrder[index];
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Get number of unique transaction hashes
|
|
243
|
+
*/
|
|
244
|
+
getTransactionCount() {
|
|
245
|
+
return this.transactionOrder.length;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Get number of decoded transactions for a specific hash
|
|
249
|
+
*/
|
|
250
|
+
getDecodedCount(hash) {
|
|
251
|
+
const normalizedHash = hash.toLowerCase();
|
|
252
|
+
const txMap = this.transactionSignals.get(normalizedHash);
|
|
253
|
+
return txMap ? txMap.size : 0;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Get all decoded transactions for a specific hash
|
|
257
|
+
*/
|
|
258
|
+
getDecodedTransactions(hash) {
|
|
259
|
+
const normalizedHash = hash.toLowerCase();
|
|
260
|
+
const txMap = this.transactionSignals.get(normalizedHash);
|
|
261
|
+
return txMap ? Array.from(txMap.values()) : [];
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Get address signal (alias for getSignal for clearer API)
|
|
265
|
+
*/
|
|
266
|
+
getAddress(address) {
|
|
267
|
+
return this.getSignal(address);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Inject data into the model
|
|
271
|
+
* Can be called multiple times to progressively add/update data
|
|
272
|
+
*/
|
|
273
|
+
injectData(dataList) {
|
|
274
|
+
batch(() => {
|
|
275
|
+
for (const data of dataList) {
|
|
276
|
+
const key = data.tokenId ? `${data.address}:${data.tokenId}` : data.address;
|
|
277
|
+
const sig = this.getOrCreateSignal(key);
|
|
278
|
+
sig.value = {
|
|
279
|
+
loading: false,
|
|
280
|
+
data: deepFreeze(structuredClone(data)),
|
|
281
|
+
error: void 0,
|
|
282
|
+
lastUpdated: Date.now()
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
this.updateResolvedTransactionData();
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Update a single item
|
|
290
|
+
*/
|
|
291
|
+
updateData(data) {
|
|
292
|
+
const key = data.tokenId ? `${data.address}:${data.tokenId}` : data.address;
|
|
293
|
+
const sig = this.getOrCreateSignal(key);
|
|
294
|
+
sig.value = {
|
|
295
|
+
loading: false,
|
|
296
|
+
data: deepFreeze(structuredClone(data)),
|
|
297
|
+
error: void 0,
|
|
298
|
+
lastUpdated: Date.now()
|
|
299
|
+
};
|
|
300
|
+
if (!this.pendingResolvedUpdate) {
|
|
301
|
+
this.pendingResolvedUpdate = true;
|
|
302
|
+
queueMicrotask(() => {
|
|
303
|
+
this.pendingResolvedUpdate = false;
|
|
304
|
+
this.updateResolvedTransactionData();
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Mark keys as loading
|
|
310
|
+
*/
|
|
311
|
+
setLoading(keys) {
|
|
312
|
+
batch(() => {
|
|
313
|
+
for (const key of keys) {
|
|
314
|
+
const sig = this.getOrCreateSignal(key);
|
|
315
|
+
sig.value = { ...sig.value, loading: true };
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Mark a key as errored
|
|
321
|
+
*/
|
|
322
|
+
setError(key, error) {
|
|
323
|
+
const sig = this.getOrCreateSignal(key);
|
|
324
|
+
sig.value = {
|
|
325
|
+
loading: false,
|
|
326
|
+
data: void 0,
|
|
327
|
+
error,
|
|
328
|
+
lastUpdated: Date.now()
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Get signal for a key (creates one if doesn't exist)
|
|
333
|
+
*/
|
|
334
|
+
getSignal(key) {
|
|
335
|
+
return this.getOrCreateSignal(key);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Subscribe to key updates
|
|
339
|
+
*/
|
|
340
|
+
subscribe(key, callback) {
|
|
341
|
+
const sig = this.getSignal(key);
|
|
342
|
+
return effect(() => callback(sig.value));
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Get current state of a key
|
|
346
|
+
*/
|
|
347
|
+
getState(key) {
|
|
348
|
+
return this.getSignal(key).value;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Get all keys that have no data
|
|
352
|
+
*/
|
|
353
|
+
getMissingKeys() {
|
|
354
|
+
const missingKeys = [];
|
|
355
|
+
for (const [key] of this.dataSignals) {
|
|
356
|
+
missingKeys.push(key);
|
|
357
|
+
}
|
|
358
|
+
return missingKeys;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Get all keys currently loading
|
|
362
|
+
*/
|
|
363
|
+
getLoadingKeys() {
|
|
364
|
+
const loadingKeys = [];
|
|
365
|
+
for (const [key, sig] of this.dataSignals) {
|
|
366
|
+
if (sig.value.loading) {
|
|
367
|
+
loadingKeys.push(key);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return loadingKeys;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Check if we have data for a key
|
|
374
|
+
*/
|
|
375
|
+
hasData(key) {
|
|
376
|
+
const sig = this.dataSignals.get(key);
|
|
377
|
+
return sig ? sig.value.data !== void 0 : false;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Clear all data
|
|
381
|
+
*/
|
|
382
|
+
clear() {
|
|
383
|
+
batch(() => {
|
|
384
|
+
for (const sig of this.dataSignals.values()) {
|
|
385
|
+
sig.value = {
|
|
386
|
+
loading: false,
|
|
387
|
+
data: void 0,
|
|
388
|
+
error: void 0,
|
|
389
|
+
lastUpdated: void 0
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
for (const txMap of this.transactionSignals.values()) {
|
|
393
|
+
for (const sig of txMap.values()) {
|
|
394
|
+
sig.value = {
|
|
395
|
+
...sig.value,
|
|
396
|
+
loading: false,
|
|
397
|
+
error: void 0
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Remove specific keys from cache
|
|
405
|
+
*/
|
|
406
|
+
remove(keys) {
|
|
407
|
+
for (const key of keys) {
|
|
408
|
+
this.dataSignals.delete(key);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Get all cached keys
|
|
413
|
+
*/
|
|
414
|
+
getAllKeys() {
|
|
415
|
+
const keys = [];
|
|
416
|
+
for (const [key] of this.dataSignals) {
|
|
417
|
+
keys.push(key);
|
|
418
|
+
}
|
|
419
|
+
return Object.freeze(keys);
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Get all data as a plain object (for debugging/serialization)
|
|
423
|
+
*/
|
|
424
|
+
getAllData() {
|
|
425
|
+
const result = {};
|
|
426
|
+
for (const [key, sig] of this.dataSignals) {
|
|
427
|
+
result[key] = sig.value;
|
|
428
|
+
}
|
|
429
|
+
return Object.freeze(result);
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Get all transaction data (for debugging)
|
|
433
|
+
*/
|
|
434
|
+
getAllTransactions() {
|
|
435
|
+
const result = {};
|
|
436
|
+
for (const [hash, txMap] of this.transactionSignals) {
|
|
437
|
+
for (const [decoderIndex, sig] of txMap) {
|
|
438
|
+
const key = decoderIndex === null ? hash : `${hash}:${decoderIndex}`;
|
|
439
|
+
result[key] = sig.value;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return Object.freeze(result);
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Get all transactions in order (returns all decoded transactions grouped by hash)
|
|
446
|
+
*/
|
|
447
|
+
getTransactionsInOrder() {
|
|
448
|
+
const result = [];
|
|
449
|
+
for (const hash of this.transactionOrder) {
|
|
450
|
+
const txMap = this.transactionSignals.get(hash);
|
|
451
|
+
if (txMap) {
|
|
452
|
+
result.push({
|
|
453
|
+
hash,
|
|
454
|
+
transactions: Array.from(txMap.values())
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return result;
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Create a resolved version of transaction data with all addresses populated
|
|
462
|
+
*/
|
|
463
|
+
createResolvedTransactionData(transactionData, addresses) {
|
|
464
|
+
const resolved = structuredClone(transactionData);
|
|
465
|
+
const addressMap = /* @__PURE__ */ new Map();
|
|
466
|
+
for (const key of addresses) {
|
|
467
|
+
const addressData = this.getData(key);
|
|
468
|
+
if (addressData) {
|
|
469
|
+
addressMap.set(key.toLowerCase(), addressData);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
this.replaceAddressesWithPaths(resolved, addressMap);
|
|
473
|
+
return deepFreeze(resolved);
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Replace addresses using path information (similar to collectAddressesWithPaths)
|
|
477
|
+
*/
|
|
478
|
+
replaceAddressesWithPaths(data, addressMap) {
|
|
479
|
+
function traverse(obj, path = [], parent) {
|
|
480
|
+
if (typeof obj === "string" && obj.startsWith("0x")) {
|
|
481
|
+
const isPaddedAddress = /^0x0*([a-fA-F0-9]{40})$/.test(obj);
|
|
482
|
+
if (isPaddedAddress || isHex(obj) && size(obj) === 20) {
|
|
483
|
+
const address = isPaddedAddress ? obj.replace(/^0x0*([a-fA-F0-9]{40})$/, "0x$1") : obj;
|
|
484
|
+
if (address.slice(2).split("").filter((c) => c === "0").length > 10) {
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
const currentKey = path[path.length - 1];
|
|
488
|
+
const tokenId = parent && !Array.isArray(parent) && "tokenId" in parent && parent.tokenId ? parent.tokenId : void 0;
|
|
489
|
+
let addressData;
|
|
490
|
+
if (tokenId) {
|
|
491
|
+
const compositeKey = `${address.toLowerCase()}_${tokenId.toLowerCase()}`;
|
|
492
|
+
addressData = addressMap.get(compositeKey);
|
|
493
|
+
} else {
|
|
494
|
+
addressData = addressMap.get(address.toLowerCase());
|
|
495
|
+
}
|
|
496
|
+
if (addressData && parent) {
|
|
497
|
+
if (Array.isArray(parent) && typeof currentKey === "number") {
|
|
498
|
+
parent[currentKey] = addressData;
|
|
499
|
+
} else if (!Array.isArray(parent) && typeof currentKey === "string") {
|
|
500
|
+
parent[currentKey] = addressData;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (!obj || typeof obj !== "object") return;
|
|
507
|
+
if (Array.isArray(obj)) {
|
|
508
|
+
for (let index = 0; index < obj.length; index++) {
|
|
509
|
+
traverse(obj[index], path.concat([index]), obj);
|
|
510
|
+
}
|
|
511
|
+
} else {
|
|
512
|
+
const record = obj;
|
|
513
|
+
for (const [key, value] of Object.entries(record)) {
|
|
514
|
+
traverse(value, path.concat([key]), record);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
__name(traverse, "traverse");
|
|
519
|
+
traverse(data);
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Update resolved transaction data for all transactions where addresses are loaded
|
|
523
|
+
*/
|
|
524
|
+
updateResolvedTransactionData() {
|
|
525
|
+
for (const [_hash, txMap] of this.transactionSignals) {
|
|
526
|
+
for (const [_decoderIndex, signal3] of txMap) {
|
|
527
|
+
const currentState = signal3.value;
|
|
528
|
+
if (currentState.addressesResolved || currentState.addresses.length === 0) {
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
let allAddressesLoaded = true;
|
|
532
|
+
for (const addressKey of currentState.addresses) {
|
|
533
|
+
const addressState = this.getState(addressKey);
|
|
534
|
+
if (addressState.loading || !addressState.data) {
|
|
535
|
+
allAddressesLoaded = false;
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (allAddressesLoaded) {
|
|
540
|
+
const resolvedData = this.createResolvedTransactionData(
|
|
541
|
+
currentState.data,
|
|
542
|
+
currentState.addresses
|
|
543
|
+
);
|
|
544
|
+
signal3.value = {
|
|
545
|
+
...currentState,
|
|
546
|
+
resolvedData,
|
|
547
|
+
addressesResolved: true,
|
|
548
|
+
lastUpdated: Date.now()
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Get data by key - convenience method that handles both 20 and 32 byte addresses
|
|
556
|
+
*/
|
|
557
|
+
getData(key) {
|
|
558
|
+
return this.getState(key).data;
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Check if a key is loading
|
|
562
|
+
*/
|
|
563
|
+
isLoading(key) {
|
|
564
|
+
return this.getState(key).loading;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Get error for a key
|
|
568
|
+
*/
|
|
569
|
+
getError(key) {
|
|
570
|
+
return this.getState(key).error;
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
function createConsumerProxy(model) {
|
|
574
|
+
const consumerMethods = [
|
|
575
|
+
// Transaction read methods
|
|
576
|
+
"getTransaction",
|
|
577
|
+
"getTransactionByKey",
|
|
578
|
+
"getTransactionsByIndex",
|
|
579
|
+
"getTransactionHashByIndex",
|
|
580
|
+
"getTransactionCount",
|
|
581
|
+
"getDecodedCount",
|
|
582
|
+
"getDecodedTransactions",
|
|
583
|
+
"getTransactionsInOrder",
|
|
584
|
+
// Address read methods
|
|
585
|
+
"getAddress",
|
|
586
|
+
"getSignal",
|
|
587
|
+
"subscribe",
|
|
588
|
+
"getState",
|
|
589
|
+
"hasData",
|
|
590
|
+
"getData",
|
|
591
|
+
"isLoading",
|
|
592
|
+
"getError",
|
|
593
|
+
// Collection read methods
|
|
594
|
+
"getAllKeys",
|
|
595
|
+
"getAllData",
|
|
596
|
+
"getAllTransactions"
|
|
597
|
+
];
|
|
598
|
+
const proxy = new Proxy(model, {
|
|
599
|
+
get(target, prop, receiver) {
|
|
600
|
+
if (typeof prop === "string" && consumerMethods.includes(prop)) {
|
|
601
|
+
const value = Reflect.get(target, prop, receiver);
|
|
602
|
+
if (typeof value === "function") {
|
|
603
|
+
return value.bind(target);
|
|
604
|
+
}
|
|
605
|
+
return value;
|
|
606
|
+
}
|
|
607
|
+
return void 0;
|
|
608
|
+
},
|
|
609
|
+
set() {
|
|
610
|
+
return false;
|
|
611
|
+
},
|
|
612
|
+
deleteProperty() {
|
|
613
|
+
return false;
|
|
614
|
+
},
|
|
615
|
+
defineProperty() {
|
|
616
|
+
return false;
|
|
617
|
+
},
|
|
618
|
+
setPrototypeOf() {
|
|
619
|
+
return false;
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
return proxy;
|
|
623
|
+
}
|
|
624
|
+
__name(createConsumerProxy, "createConsumerProxy");
|
|
625
|
+
function createDataModel(options) {
|
|
626
|
+
const model = new DataModel(options);
|
|
627
|
+
Object.freeze(DataModel.prototype);
|
|
628
|
+
Object.freeze(DataModel);
|
|
629
|
+
return model;
|
|
630
|
+
}
|
|
631
|
+
__name(createDataModel, "createDataModel");
|
|
632
|
+
|
|
633
|
+
// src/core/instance.ts
|
|
634
|
+
var dataModelInstance = new DataModel();
|
|
635
|
+
Object.freeze(DataModel.prototype);
|
|
636
|
+
Object.freeze(DataModel);
|
|
637
|
+
var dataModel = dataModelInstance;
|
|
638
|
+
var consumerModel = createConsumerProxy(dataModelInstance);
|
|
639
|
+
function createGlobalInstance() {
|
|
640
|
+
if (typeof window !== "undefined") {
|
|
641
|
+
const consumerProxy = createConsumerProxy(dataModelInstance);
|
|
642
|
+
Object.defineProperty(window, "TransactionDecoder", {
|
|
643
|
+
value: consumerProxy,
|
|
644
|
+
writable: false,
|
|
645
|
+
configurable: false,
|
|
646
|
+
enumerable: true
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
__name(createGlobalInstance, "createGlobalInstance");
|
|
651
|
+
|
|
652
|
+
// src/decoder/decodeTransaction.ts
|
|
653
|
+
import { signal as signal2 } from "@preact/signals-core";
|
|
654
|
+
|
|
655
|
+
// src/shared/constants.ts
|
|
656
|
+
var ERROR_CODES = {
|
|
657
|
+
INVALID_TRANSACTION: "INVALID_TRANSACTION",
|
|
658
|
+
DECODING_FAILED: "DECODING_FAILED",
|
|
659
|
+
NETWORK_ERROR: "NETWORK_ERROR",
|
|
660
|
+
RATE_LIMIT: "RATE_LIMIT",
|
|
661
|
+
UNAUTHORIZED: "UNAUTHORIZED",
|
|
662
|
+
NOT_FOUND: "NOT_FOUND"
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
// src/decoder/decodeTransaction.ts
|
|
666
|
+
async function decodeTransaction2(transaction, options) {
|
|
667
|
+
const isBatch = Array.isArray(transaction);
|
|
668
|
+
const transactions = isBatch ? transaction : [transaction];
|
|
669
|
+
const results = await decodeTransactionBatch(transactions, options);
|
|
670
|
+
if (isBatch) {
|
|
671
|
+
return results;
|
|
672
|
+
}
|
|
673
|
+
return results[0];
|
|
674
|
+
}
|
|
675
|
+
__name(decodeTransaction2, "decodeTransaction");
|
|
676
|
+
async function decodeTransactionBatch(transactions, options) {
|
|
677
|
+
const overridePluginNames = options.plugins?.map((p) => p.name).filter((name) => typeof name === "string") || [];
|
|
678
|
+
const overrideSchemaNames = options.schemaPlugins?.map((p) => p.name).filter((name) => typeof name === "string") || [];
|
|
679
|
+
const registeredPlugins = await pluginRegistry.getAll({
|
|
680
|
+
excludeNames: overridePluginNames
|
|
681
|
+
});
|
|
682
|
+
const registeredSchemaPlugins = pluginRegistry.getAllSchema(overrideSchemaNames);
|
|
683
|
+
const allPlugins = [...registeredPlugins, ...options.plugins || []];
|
|
684
|
+
const allSchemaPlugins = [
|
|
685
|
+
...registeredSchemaPlugins,
|
|
686
|
+
...options.schemaPlugins || []
|
|
687
|
+
];
|
|
688
|
+
const syncOptions = {
|
|
689
|
+
...options,
|
|
690
|
+
plugins: allPlugins.filter(({ usesAsync }) => !usesAsync),
|
|
691
|
+
schemaPlugins: allSchemaPlugins,
|
|
692
|
+
async: false
|
|
693
|
+
};
|
|
694
|
+
const results = await Promise.all(
|
|
695
|
+
transactions.map(async (tx) => {
|
|
696
|
+
const initialResult = await decodeTransaction(tx, syncOptions);
|
|
697
|
+
const initialData = deepFreeze(initialResult || createErrorResult(tx));
|
|
698
|
+
const txSignal = signal2({
|
|
699
|
+
loading: false,
|
|
700
|
+
data: initialData,
|
|
701
|
+
decodingStatus: initialResult ? "decoded" : "failed",
|
|
702
|
+
addresses: collectDataKeys(tx),
|
|
703
|
+
addressesResolved: false,
|
|
704
|
+
lastUpdated: Date.now()
|
|
705
|
+
});
|
|
706
|
+
return {
|
|
707
|
+
transaction: tx,
|
|
708
|
+
immediate: initialData,
|
|
709
|
+
signal: txSignal
|
|
710
|
+
};
|
|
711
|
+
})
|
|
712
|
+
);
|
|
713
|
+
const hasAsyncPlugins = true;
|
|
714
|
+
const pluginsEnabled = isAsyncOperationEnabled(
|
|
715
|
+
options.async,
|
|
716
|
+
1 /* ENABLE_PLUGINS */
|
|
717
|
+
);
|
|
718
|
+
if (hasAsyncPlugins && pluginsEnabled) {
|
|
719
|
+
const asyncOptions = {
|
|
720
|
+
...options,
|
|
721
|
+
plugins: allPlugins,
|
|
722
|
+
schemaPlugins: allSchemaPlugins,
|
|
723
|
+
async: true
|
|
724
|
+
};
|
|
725
|
+
for (const { transaction, signal: txSignal } of results) {
|
|
726
|
+
decodeTransaction(transaction, asyncOptions).then((enhanced) => {
|
|
727
|
+
if (enhanced) {
|
|
728
|
+
txSignal.value = {
|
|
729
|
+
...txSignal.value,
|
|
730
|
+
data: deepFreeze(enhanced),
|
|
731
|
+
decodingStatus: "enhanced",
|
|
732
|
+
addresses: collectDataKeys(enhanced),
|
|
733
|
+
lastUpdated: Date.now()
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
}).catch((error) => {
|
|
737
|
+
console.error("Async decode error:", error);
|
|
738
|
+
txSignal.value = {
|
|
739
|
+
...txSignal.value,
|
|
740
|
+
decodingStatus: "failed",
|
|
741
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
742
|
+
loading: false
|
|
743
|
+
};
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
const addressResolveEnabled = isAsyncOperationEnabled(
|
|
748
|
+
options.async,
|
|
749
|
+
2 /* ENABLE_ADDRESS_RESOLVE */
|
|
750
|
+
);
|
|
751
|
+
if (options.addressResolver && addressResolveEnabled) {
|
|
752
|
+
const allAddresses = /* @__PURE__ */ new Set();
|
|
753
|
+
for (const { signal: txSignal } of results) {
|
|
754
|
+
for (const addr of txSignal.value.addresses) {
|
|
755
|
+
allAddresses.add(addr);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
if (allAddresses.size > 0) {
|
|
759
|
+
options.addressResolver.resolveAddresses(Array.from(allAddresses)).then(() => {
|
|
760
|
+
for (const { signal: txSignal } of results) {
|
|
761
|
+
if (txSignal.value.decodingStatus !== "failed") {
|
|
762
|
+
txSignal.value = {
|
|
763
|
+
...txSignal.value,
|
|
764
|
+
addressesResolved: true,
|
|
765
|
+
decodingStatus: "complete",
|
|
766
|
+
lastUpdated: Date.now()
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}).catch((error) => {
|
|
771
|
+
console.error("Address resolution error:", error);
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
} else if (!hasAsyncPlugins || !pluginsEnabled && !addressResolveEnabled) {
|
|
775
|
+
for (const { signal: txSignal } of results) {
|
|
776
|
+
if (txSignal.value.decodingStatus !== "failed") {
|
|
777
|
+
txSignal.value = {
|
|
778
|
+
...txSignal.value,
|
|
779
|
+
decodingStatus: "complete"
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return results.map((r) => ({
|
|
785
|
+
immediate: r.immediate,
|
|
786
|
+
signal: r.signal
|
|
787
|
+
}));
|
|
788
|
+
}
|
|
789
|
+
__name(decodeTransactionBatch, "decodeTransactionBatch");
|
|
790
|
+
function createErrorResult(transaction) {
|
|
791
|
+
return {
|
|
792
|
+
...transaction,
|
|
793
|
+
resultType: "error",
|
|
794
|
+
isDecoded: false,
|
|
795
|
+
errorType: "ERROR" /* ERROR */,
|
|
796
|
+
sig: transaction.input?.slice(0, 10),
|
|
797
|
+
error: {
|
|
798
|
+
code: ERROR_CODES.DECODING_FAILED,
|
|
799
|
+
message: "Failed to decode transaction",
|
|
800
|
+
details: new Error("Decoder returned undefined")
|
|
801
|
+
}
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
__name(createErrorResult, "createErrorResult");
|
|
805
|
+
async function decodeTransactionAsync(transaction, options) {
|
|
806
|
+
const isBatch = Array.isArray(transaction);
|
|
807
|
+
const result = await decodeTransaction2(transaction, options);
|
|
808
|
+
const results = isBatch ? result : [result];
|
|
809
|
+
const finalResults = await Promise.all(
|
|
810
|
+
results.map(
|
|
811
|
+
({ signal: signal3 }) => new Promise((resolve) => {
|
|
812
|
+
const checkComplete = /* @__PURE__ */ __name(() => {
|
|
813
|
+
const state = signal3.value;
|
|
814
|
+
if (state.decodingStatus === "complete" || state.decodingStatus === "failed") {
|
|
815
|
+
resolve(state.data);
|
|
816
|
+
} else {
|
|
817
|
+
setTimeout(checkComplete, 10);
|
|
818
|
+
}
|
|
819
|
+
}, "checkComplete");
|
|
820
|
+
checkComplete();
|
|
821
|
+
})
|
|
822
|
+
)
|
|
823
|
+
);
|
|
824
|
+
if (isBatch) {
|
|
825
|
+
return finalResults;
|
|
826
|
+
}
|
|
827
|
+
return finalResults[0];
|
|
828
|
+
}
|
|
829
|
+
__name(decodeTransactionAsync, "decodeTransactionAsync");
|
|
830
|
+
|
|
831
|
+
export {
|
|
832
|
+
createDataModel,
|
|
833
|
+
dataModel,
|
|
834
|
+
consumerModel,
|
|
835
|
+
createGlobalInstance,
|
|
836
|
+
decodeTransaction2 as decodeTransaction,
|
|
837
|
+
decodeTransactionAsync
|
|
838
|
+
};
|
|
839
|
+
//# sourceMappingURL=chunk-GXZOF3QY.js.map
|