holosphere 2.0.0-alpha1 → 2.0.0-alpha4

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 (154) hide show
  1. package/dist/2019-D2OG2idw.js +6680 -0
  2. package/dist/2019-D2OG2idw.js.map +1 -0
  3. package/dist/2019-EION3wKo.cjs +8 -0
  4. package/dist/2019-EION3wKo.cjs.map +1 -0
  5. package/dist/_commonjsHelpers-C37NGDzP.cjs +2 -0
  6. package/dist/_commonjsHelpers-C37NGDzP.cjs.map +1 -0
  7. package/dist/_commonjsHelpers-CUmg6egw.js +7 -0
  8. package/dist/_commonjsHelpers-CUmg6egw.js.map +1 -0
  9. package/dist/browser-BSniCNqO.js +3058 -0
  10. package/dist/browser-BSniCNqO.js.map +1 -0
  11. package/dist/browser-Cq59Ij19.cjs +2 -0
  12. package/dist/browser-Cq59Ij19.cjs.map +1 -0
  13. package/dist/cjs/holosphere.cjs +2 -0
  14. package/dist/cjs/holosphere.cjs.map +1 -0
  15. package/dist/esm/holosphere.js +53 -0
  16. package/dist/esm/holosphere.js.map +1 -0
  17. package/dist/index-BB_vVJgv.cjs +5 -0
  18. package/dist/index-BB_vVJgv.cjs.map +1 -0
  19. package/dist/index-CBitK71M.cjs +12 -0
  20. package/dist/index-CBitK71M.cjs.map +1 -0
  21. package/dist/index-CV0eOogK.js +37423 -0
  22. package/dist/index-CV0eOogK.js.map +1 -0
  23. package/dist/index-Cz-PLCUR.js +15104 -0
  24. package/dist/index-Cz-PLCUR.js.map +1 -0
  25. package/dist/indexeddb-storage-CRsZyB2f.cjs +2 -0
  26. package/dist/indexeddb-storage-CRsZyB2f.cjs.map +1 -0
  27. package/dist/indexeddb-storage-DZaGlY_a.js +132 -0
  28. package/dist/indexeddb-storage-DZaGlY_a.js.map +1 -0
  29. package/dist/memory-storage-BkUi6sZG.js +51 -0
  30. package/dist/memory-storage-BkUi6sZG.js.map +1 -0
  31. package/dist/memory-storage-C0DuUsdY.cjs +2 -0
  32. package/dist/memory-storage-C0DuUsdY.cjs.map +1 -0
  33. package/dist/secp256k1-0kPdAVkK.cjs +12 -0
  34. package/dist/secp256k1-0kPdAVkK.cjs.map +1 -0
  35. package/dist/secp256k1-DN4FVXcv.js +1890 -0
  36. package/dist/secp256k1-DN4FVXcv.js.map +1 -0
  37. package/docs/CONTRACTS.md +797 -0
  38. package/docs/FOSDEM_PROPOSAL.md +388 -0
  39. package/docs/LOCALFIRST.md +266 -0
  40. package/docs/contracts/api-interface.md +793 -0
  41. package/docs/data-model.md +476 -0
  42. package/docs/gun-async-usage.md +338 -0
  43. package/docs/plan.md +349 -0
  44. package/docs/quickstart.md +674 -0
  45. package/docs/research.md +362 -0
  46. package/docs/spec.md +244 -0
  47. package/docs/storage-backends.md +326 -0
  48. package/docs/tasks.md +947 -0
  49. package/examples/demo.html +47 -0
  50. package/package.json +10 -5
  51. package/src/contracts/abis/Appreciative.json +1280 -0
  52. package/src/contracts/abis/AppreciativeFactory.json +101 -0
  53. package/src/contracts/abis/Bundle.json +1435 -0
  54. package/src/contracts/abis/BundleFactory.json +106 -0
  55. package/src/contracts/abis/Holon.json +881 -0
  56. package/src/contracts/abis/Holons.json +330 -0
  57. package/src/contracts/abis/Managed.json +1262 -0
  58. package/src/contracts/abis/ManagedFactory.json +149 -0
  59. package/src/contracts/abis/Membrane.json +261 -0
  60. package/src/contracts/abis/Splitter.json +1624 -0
  61. package/src/contracts/abis/SplitterFactory.json +220 -0
  62. package/src/contracts/abis/TestToken.json +321 -0
  63. package/src/contracts/abis/Zoned.json +1461 -0
  64. package/src/contracts/abis/ZonedFactory.json +154 -0
  65. package/src/contracts/chain-manager.js +375 -0
  66. package/src/contracts/deployer.js +443 -0
  67. package/src/contracts/event-listener.js +507 -0
  68. package/src/contracts/holon-contracts.js +344 -0
  69. package/src/contracts/index.js +83 -0
  70. package/src/contracts/networks.js +224 -0
  71. package/src/contracts/operations.js +670 -0
  72. package/src/contracts/queries.js +589 -0
  73. package/src/core/holosphere.js +453 -1
  74. package/src/crypto/nostr-utils.js +263 -0
  75. package/src/federation/handshake.js +455 -0
  76. package/src/federation/hologram.js +1 -1
  77. package/src/hierarchical/upcast.js +6 -5
  78. package/src/index.js +463 -1939
  79. package/src/lib/ai-methods.js +308 -0
  80. package/src/lib/contract-methods.js +293 -0
  81. package/src/lib/errors.js +23 -0
  82. package/src/lib/federation-methods.js +238 -0
  83. package/src/lib/index.js +26 -0
  84. package/src/spatial/h3-operations.js +2 -2
  85. package/src/storage/backends/gundb-backend.js +377 -46
  86. package/src/storage/global-tables.js +28 -1
  87. package/src/storage/gun-auth.js +303 -0
  88. package/src/storage/gun-federation.js +776 -0
  89. package/src/storage/gun-references.js +198 -0
  90. package/src/storage/gun-schema.js +291 -0
  91. package/src/storage/gun-wrapper.js +347 -31
  92. package/src/storage/indexeddb-storage.js +49 -11
  93. package/src/storage/memory-storage.js +5 -0
  94. package/src/storage/nostr-async.js +45 -23
  95. package/src/storage/nostr-client.js +11 -5
  96. package/src/storage/persistent-storage.js +6 -1
  97. package/src/storage/unified-storage.js +119 -0
  98. package/src/subscriptions/manager.js +1 -1
  99. package/types/index.d.ts +133 -0
  100. package/tests/unit/ai/aggregation.test.js +0 -295
  101. package/tests/unit/ai/breakdown.test.js +0 -446
  102. package/tests/unit/ai/classifier.test.js +0 -294
  103. package/tests/unit/ai/council.test.js +0 -262
  104. package/tests/unit/ai/embeddings.test.js +0 -384
  105. package/tests/unit/ai/federation-ai.test.js +0 -344
  106. package/tests/unit/ai/h3-ai.test.js +0 -458
  107. package/tests/unit/ai/index.test.js +0 -304
  108. package/tests/unit/ai/json-ops.test.js +0 -307
  109. package/tests/unit/ai/llm-service.test.js +0 -390
  110. package/tests/unit/ai/nl-query.test.js +0 -383
  111. package/tests/unit/ai/relationships.test.js +0 -311
  112. package/tests/unit/ai/schema-extractor.test.js +0 -384
  113. package/tests/unit/ai/spatial.test.js +0 -279
  114. package/tests/unit/ai/tts.test.js +0 -279
  115. package/tests/unit/content.test.js +0 -332
  116. package/tests/unit/contract/core.test.js +0 -88
  117. package/tests/unit/contract/crypto.test.js +0 -198
  118. package/tests/unit/contract/data.test.js +0 -223
  119. package/tests/unit/contract/federation.test.js +0 -181
  120. package/tests/unit/contract/hierarchical.test.js +0 -113
  121. package/tests/unit/contract/schema.test.js +0 -114
  122. package/tests/unit/contract/social.test.js +0 -217
  123. package/tests/unit/contract/spatial.test.js +0 -110
  124. package/tests/unit/contract/subscriptions.test.js +0 -128
  125. package/tests/unit/contract/utils.test.js +0 -159
  126. package/tests/unit/core.test.js +0 -152
  127. package/tests/unit/crypto.test.js +0 -328
  128. package/tests/unit/federation.test.js +0 -234
  129. package/tests/unit/gun-async.test.js +0 -252
  130. package/tests/unit/hierarchical.test.js +0 -399
  131. package/tests/unit/integration/scenario-01-geographic-storage.test.js +0 -74
  132. package/tests/unit/integration/scenario-02-federation.test.js +0 -76
  133. package/tests/unit/integration/scenario-03-subscriptions.test.js +0 -102
  134. package/tests/unit/integration/scenario-04-validation.test.js +0 -129
  135. package/tests/unit/integration/scenario-05-hierarchy.test.js +0 -125
  136. package/tests/unit/integration/scenario-06-social.test.js +0 -135
  137. package/tests/unit/integration/scenario-07-persistence.test.js +0 -130
  138. package/tests/unit/integration/scenario-08-authorization.test.js +0 -161
  139. package/tests/unit/integration/scenario-09-cross-dimensional.test.js +0 -139
  140. package/tests/unit/integration/scenario-10-cross-holosphere-capabilities.test.js +0 -357
  141. package/tests/unit/integration/scenario-11-cross-holosphere-federation.test.js +0 -410
  142. package/tests/unit/integration/scenario-12-capability-federated-read.test.js +0 -719
  143. package/tests/unit/performance/benchmark.test.js +0 -85
  144. package/tests/unit/schema.test.js +0 -213
  145. package/tests/unit/spatial.test.js +0 -158
  146. package/tests/unit/storage.test.js +0 -195
  147. package/tests/unit/subscriptions.test.js +0 -328
  148. package/tests/unit/test-data-permanence-debug.js +0 -197
  149. package/tests/unit/test-data-permanence.js +0 -340
  150. package/tests/unit/test-key-persistence-fixed.js +0 -148
  151. package/tests/unit/test-key-persistence.js +0 -172
  152. package/tests/unit/test-relay-permanence.js +0 -376
  153. package/tests/unit/test-second-node.js +0 -95
  154. package/tests/unit/test-simple-write.js +0 -89
@@ -0,0 +1,507 @@
1
+ /**
2
+ * Event Listener for Sankey Flow Visualization
3
+ *
4
+ * Listens to contract events and transforms them into a format
5
+ * suitable for Sankey diagram visualization of fund flows.
6
+ */
7
+
8
+ /**
9
+ * Standard Sankey event types emitted by our contracts
10
+ */
11
+ export const SANKEY_EVENTS = {
12
+ // Fund flow events
13
+ FundsReceived: 'FundsReceived',
14
+ FundsCascaded: 'FundsCascaded',
15
+ FundsAllocated: 'FundsAllocated',
16
+ FundsClaimed: 'FundsClaimed',
17
+ FundsTransferred: 'FundsTransferred',
18
+
19
+ // Distribution summary
20
+ DistributionCompleted: 'DistributionCompleted',
21
+
22
+ // Structure events
23
+ ChildContractCreated: 'ChildContractCreated',
24
+ HolonCreated: 'HolonCreated',
25
+
26
+ // Membership events
27
+ MemberAdded: 'MemberAdded',
28
+ MemberRemoved: 'MemberRemoved',
29
+
30
+ // Weight/Zone events
31
+ WeightChanged: 'WeightChanged',
32
+ ZoneAssigned: 'ZoneAssigned',
33
+ AppreciationGiven: 'AppreciationGiven',
34
+
35
+ // Configuration events
36
+ SplitConfigured: 'SplitConfigured',
37
+ ContractSplitConfigured: 'ContractSplitConfigured'
38
+ };
39
+
40
+ /**
41
+ * EventListener class for tracking contract events
42
+ */
43
+ export class EventListener {
44
+ constructor(chainManager) {
45
+ this.chainManager = chainManager;
46
+ this.listeners = new Map();
47
+ this.eventHistory = [];
48
+ this.sankeyNodes = new Map(); // address/userId -> node data
49
+ this.sankeyLinks = []; // {source, target, value, ...}
50
+ }
51
+
52
+ /**
53
+ * Start listening to all Sankey-relevant events on a contract
54
+ * @param {string} contractAddress - The contract address
55
+ * @param {string} holonId - Human-readable holon identifier
56
+ * @param {Object} contract - ethers.js contract instance
57
+ */
58
+ async listenToContract(contractAddress, holonId, contract) {
59
+ const eventFilters = [
60
+ 'FundsReceived',
61
+ 'FundsCascaded',
62
+ 'FundsAllocated',
63
+ 'FundsClaimed',
64
+ 'FundsTransferred',
65
+ 'DistributionCompleted',
66
+ 'MemberAdded',
67
+ 'WeightChanged',
68
+ 'ZoneAssigned',
69
+ 'AppreciationGiven'
70
+ ];
71
+
72
+ for (const eventName of eventFilters) {
73
+ try {
74
+ const filter = contract.filters[eventName]?.();
75
+ if (filter) {
76
+ contract.on(eventName, (...args) => {
77
+ this._handleEvent(eventName, args, contractAddress, holonId);
78
+ });
79
+ }
80
+ } catch (e) {
81
+ // Event not found on this contract, skip
82
+ }
83
+ }
84
+
85
+ this.listeners.set(contractAddress, { contract, holonId });
86
+ }
87
+
88
+ /**
89
+ * Stop listening to a contract
90
+ */
91
+ stopListening(contractAddress) {
92
+ const listener = this.listeners.get(contractAddress);
93
+ if (listener) {
94
+ listener.contract.removeAllListeners();
95
+ this.listeners.delete(contractAddress);
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Stop all listeners
101
+ */
102
+ stopAll() {
103
+ for (const [address, listener] of this.listeners) {
104
+ listener.contract.removeAllListeners();
105
+ }
106
+ this.listeners.clear();
107
+ }
108
+
109
+ /**
110
+ * Handle incoming event and transform to Sankey data
111
+ */
112
+ _handleEvent(eventName, args, contractAddress, holonId) {
113
+ const event = args[args.length - 1]; // Last arg is the event object
114
+ const timestamp = Date.now();
115
+
116
+ const parsed = {
117
+ type: eventName,
118
+ contractAddress,
119
+ holonId,
120
+ timestamp,
121
+ blockNumber: event?.blockNumber,
122
+ transactionHash: event?.transactionHash,
123
+ data: this._parseEventData(eventName, args)
124
+ };
125
+
126
+ this.eventHistory.push(parsed);
127
+ this._updateSankeyData(parsed);
128
+
129
+ // Emit custom event for real-time updates
130
+ if (typeof window !== 'undefined') {
131
+ window.dispatchEvent(new CustomEvent('sankey-event', { detail: parsed }));
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Parse event data based on event type
137
+ */
138
+ _parseEventData(eventName, args) {
139
+ switch (eventName) {
140
+ case 'FundsReceived':
141
+ return {
142
+ contractAddress: args[0],
143
+ sender: args[1],
144
+ tokenAddress: args[2],
145
+ amount: args[3]?.toString()
146
+ };
147
+
148
+ case 'FundsCascaded':
149
+ return {
150
+ fromContract: args[0],
151
+ toContract: args[1],
152
+ fromHolonId: args[2],
153
+ toHolonId: args[3],
154
+ tokenAddress: args[4],
155
+ amount: args[5]?.toString(),
156
+ splitType: args[6]
157
+ };
158
+
159
+ case 'FundsAllocated':
160
+ return {
161
+ contractAddress: args[0],
162
+ holonId: args[1],
163
+ userId: args[2],
164
+ tokenAddress: args[3],
165
+ amount: args[4]?.toString(),
166
+ distributionType: args[5]
167
+ };
168
+
169
+ case 'FundsClaimed':
170
+ return {
171
+ contractAddress: args[0],
172
+ holonId: args[1],
173
+ userId: args[2],
174
+ beneficiary: args[3],
175
+ tokenAddress: args[4],
176
+ amount: args[5]?.toString()
177
+ };
178
+
179
+ case 'FundsTransferred':
180
+ return {
181
+ contractAddress: args[0],
182
+ holonId: args[1],
183
+ userId: args[2],
184
+ recipient: args[3],
185
+ tokenAddress: args[4],
186
+ amount: args[5]?.toString()
187
+ };
188
+
189
+ case 'DistributionCompleted':
190
+ return {
191
+ contractAddress: args[0],
192
+ holonId: args[1],
193
+ tokenAddress: args[2],
194
+ totalAmount: args[3]?.toString(),
195
+ recipientCount: Number(args[4]),
196
+ cascadeCount: Number(args[5])
197
+ };
198
+
199
+ case 'MemberAdded':
200
+ return {
201
+ contractAddress: args[0],
202
+ holonId: args[1],
203
+ userId: args[2],
204
+ addedBy: args[3]
205
+ };
206
+
207
+ case 'WeightChanged':
208
+ return {
209
+ contractAddress: args[0],
210
+ holonId: args[1],
211
+ userId: args[2],
212
+ oldWeight: args[3]?.toString(),
213
+ newWeight: args[4]?.toString()
214
+ };
215
+
216
+ case 'ZoneAssigned':
217
+ return {
218
+ contractAddress: args[0],
219
+ holonId: args[1],
220
+ userId: args[2],
221
+ oldZone: args[3]?.toString(),
222
+ newZone: args[4]?.toString()
223
+ };
224
+
225
+ case 'AppreciationGiven':
226
+ return {
227
+ contractAddress: args[0],
228
+ holonId: args[1],
229
+ fromUserId: args[2],
230
+ toUserId: args[3],
231
+ amount: args[4]?.toString()
232
+ };
233
+
234
+ default:
235
+ return { raw: args.slice(0, -1) };
236
+ }
237
+ }
238
+
239
+ /**
240
+ * Update Sankey nodes and links based on event
241
+ */
242
+ _updateSankeyData(event) {
243
+ const { type, data } = event;
244
+
245
+ switch (type) {
246
+ case 'FundsReceived':
247
+ // Add external source node
248
+ this._ensureNode(data.sender, 'external', 'External');
249
+ this._ensureNode(data.contractAddress, 'contract', event.holonId);
250
+ this.sankeyLinks.push({
251
+ source: data.sender,
252
+ target: data.contractAddress,
253
+ value: BigInt(data.amount),
254
+ token: data.tokenAddress,
255
+ type: 'external_inflow'
256
+ });
257
+ break;
258
+
259
+ case 'FundsCascaded':
260
+ this._ensureNode(data.fromContract, 'contract', data.fromHolonId);
261
+ this._ensureNode(data.toContract, 'contract', data.toHolonId);
262
+ this.sankeyLinks.push({
263
+ source: data.fromContract,
264
+ target: data.toContract,
265
+ value: BigInt(data.amount),
266
+ token: data.tokenAddress,
267
+ type: 'cascade',
268
+ splitType: data.splitType
269
+ });
270
+ break;
271
+
272
+ case 'FundsAllocated':
273
+ this._ensureNode(data.contractAddress, 'contract', data.holonId);
274
+ this._ensureNode(data.userId, 'user', data.userId);
275
+ this.sankeyLinks.push({
276
+ source: data.contractAddress,
277
+ target: data.userId,
278
+ value: BigInt(data.amount),
279
+ token: data.tokenAddress,
280
+ type: 'allocation',
281
+ distributionType: data.distributionType
282
+ });
283
+ break;
284
+
285
+ case 'FundsClaimed':
286
+ case 'FundsTransferred':
287
+ this._ensureNode(data.userId, 'user', data.userId);
288
+ this._ensureNode(data.beneficiary || data.recipient, 'wallet',
289
+ (data.beneficiary || data.recipient).slice(0, 8) + '...');
290
+ this.sankeyLinks.push({
291
+ source: data.userId,
292
+ target: data.beneficiary || data.recipient,
293
+ value: BigInt(data.amount),
294
+ token: data.tokenAddress,
295
+ type: type === 'FundsClaimed' ? 'claim' : 'transfer'
296
+ });
297
+ break;
298
+
299
+ case 'MemberAdded':
300
+ this._ensureNode(data.userId, 'user', data.userId);
301
+ break;
302
+ }
303
+ }
304
+
305
+ /**
306
+ * Ensure a node exists in the Sankey graph
307
+ */
308
+ _ensureNode(id, type, label) {
309
+ if (!this.sankeyNodes.has(id)) {
310
+ this.sankeyNodes.set(id, { id, type, label });
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Get Sankey-compatible data for visualization
316
+ * @param {Object} options - Filter options
317
+ * @returns {Object} { nodes: [], links: [] }
318
+ */
319
+ getSankeyData(options = {}) {
320
+ const { tokenAddress, fromBlock, toBlock, minValue } = options;
321
+
322
+ let links = [...this.sankeyLinks];
323
+
324
+ // Filter by token
325
+ if (tokenAddress) {
326
+ links = links.filter(l => l.token === tokenAddress);
327
+ }
328
+
329
+ // Filter by minimum value
330
+ if (minValue) {
331
+ const minBigInt = BigInt(minValue);
332
+ links = links.filter(l => l.value >= minBigInt);
333
+ }
334
+
335
+ // Get unique node IDs from filtered links
336
+ const nodeIds = new Set();
337
+ links.forEach(l => {
338
+ nodeIds.add(l.source);
339
+ nodeIds.add(l.target);
340
+ });
341
+
342
+ // Convert nodes to array format
343
+ const nodeArray = [...nodeIds].map(id => {
344
+ const node = this.sankeyNodes.get(id) || { id, type: 'unknown', label: id };
345
+ return {
346
+ id: node.id,
347
+ name: node.label,
348
+ type: node.type
349
+ };
350
+ });
351
+
352
+ // Create node index for links
353
+ const nodeIndex = new Map();
354
+ nodeArray.forEach((n, i) => nodeIndex.set(n.id, i));
355
+
356
+ // Convert links to Sankey format with numeric indices
357
+ const sankeyLinks = links.map(l => ({
358
+ source: nodeIndex.get(l.source),
359
+ target: nodeIndex.get(l.target),
360
+ value: Number(l.value) / 1e18, // Convert from wei to ETH
361
+ type: l.type,
362
+ splitType: l.splitType,
363
+ distributionType: l.distributionType,
364
+ token: l.token
365
+ }));
366
+
367
+ return {
368
+ nodes: nodeArray,
369
+ links: sankeyLinks
370
+ };
371
+ }
372
+
373
+ /**
374
+ * Get aggregated flow summary
375
+ * @returns {Object} Summary statistics
376
+ */
377
+ getFlowSummary() {
378
+ const summary = {
379
+ totalInflow: 0n,
380
+ totalOutflow: 0n,
381
+ byHolon: {},
382
+ byUser: {},
383
+ byDistributionType: {
384
+ appreciation: 0n,
385
+ equal: 0n,
386
+ zone: 0n,
387
+ percentage: 0n,
388
+ peer_appreciation: 0n
389
+ }
390
+ };
391
+
392
+ for (const link of this.sankeyLinks) {
393
+ if (link.type === 'external_inflow') {
394
+ summary.totalInflow += link.value;
395
+ }
396
+ if (link.type === 'claim' || link.type === 'transfer') {
397
+ summary.totalOutflow += link.value;
398
+ }
399
+ if (link.distributionType && summary.byDistributionType[link.distributionType] !== undefined) {
400
+ summary.byDistributionType[link.distributionType] += link.value;
401
+ }
402
+ }
403
+
404
+ // Convert BigInt to strings for JSON serialization
405
+ return {
406
+ totalInflow: summary.totalInflow.toString(),
407
+ totalOutflow: summary.totalOutflow.toString(),
408
+ totalInflowEth: Number(summary.totalInflow) / 1e18,
409
+ totalOutflowEth: Number(summary.totalOutflow) / 1e18,
410
+ byDistributionType: Object.fromEntries(
411
+ Object.entries(summary.byDistributionType).map(([k, v]) => [k, v.toString()])
412
+ )
413
+ };
414
+ }
415
+
416
+ /**
417
+ * Query historical events from blockchain
418
+ * @param {Object} contract - ethers.js contract instance
419
+ * @param {string} eventName - Event name to query
420
+ * @param {Object} options - { fromBlock, toBlock }
421
+ */
422
+ async queryPastEvents(contract, eventName, options = {}) {
423
+ const { fromBlock = 0, toBlock = 'latest' } = options;
424
+
425
+ try {
426
+ const filter = contract.filters[eventName]?.();
427
+ if (!filter) return [];
428
+
429
+ const events = await contract.queryFilter(filter, fromBlock, toBlock);
430
+ return events.map(e => ({
431
+ type: eventName,
432
+ contractAddress: e.address,
433
+ blockNumber: e.blockNumber,
434
+ transactionHash: e.transactionHash,
435
+ data: this._parseEventData(eventName, e.args)
436
+ }));
437
+ } catch (e) {
438
+ console.error(`Error querying ${eventName}:`, e);
439
+ return [];
440
+ }
441
+ }
442
+
443
+ /**
444
+ * Build Sankey data from historical events
445
+ * @param {Object} contract - ethers.js contract instance
446
+ * @param {string} holonId - Holon identifier
447
+ * @param {Object} options - Query options
448
+ */
449
+ async buildFromHistory(contract, holonId, options = {}) {
450
+ const eventTypes = [
451
+ 'FundsReceived',
452
+ 'FundsCascaded',
453
+ 'FundsAllocated',
454
+ 'FundsClaimed',
455
+ 'FundsTransferred',
456
+ 'MemberAdded'
457
+ ];
458
+
459
+ for (const eventName of eventTypes) {
460
+ const events = await this.queryPastEvents(contract, eventName, options);
461
+ for (const event of events) {
462
+ event.holonId = holonId;
463
+ this.eventHistory.push(event);
464
+ this._updateSankeyData(event);
465
+ }
466
+ }
467
+
468
+ return this.getSankeyData();
469
+ }
470
+
471
+ /**
472
+ * Clear all stored data
473
+ */
474
+ clear() {
475
+ this.eventHistory = [];
476
+ this.sankeyNodes.clear();
477
+ this.sankeyLinks = [];
478
+ }
479
+
480
+ /**
481
+ * Export data for persistence
482
+ */
483
+ export() {
484
+ return {
485
+ events: this.eventHistory,
486
+ nodes: Object.fromEntries(this.sankeyNodes),
487
+ links: this.sankeyLinks.map(l => ({
488
+ ...l,
489
+ value: l.value.toString()
490
+ }))
491
+ };
492
+ }
493
+
494
+ /**
495
+ * Import previously exported data
496
+ */
497
+ import(data) {
498
+ this.eventHistory = data.events || [];
499
+ this.sankeyNodes = new Map(Object.entries(data.nodes || {}));
500
+ this.sankeyLinks = (data.links || []).map(l => ({
501
+ ...l,
502
+ value: BigInt(l.value)
503
+ }));
504
+ }
505
+ }
506
+
507
+ export default EventListener;