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,670 @@
1
+ /**
2
+ * ContractOperations - All contract functions exposed with clear parameters
3
+ * Provides a unified interface for interacting with all Holosphere contract types
4
+ */
5
+
6
+ export class ContractOperations {
7
+ constructor(contract, type, chainManager) {
8
+ this.contract = contract;
9
+ this.type = type;
10
+ this.chainManager = chainManager;
11
+ this.ethers = null;
12
+ }
13
+
14
+ /**
15
+ * Load ethers dynamically
16
+ * @private
17
+ */
18
+ async _loadEthers() {
19
+ if (!this.ethers) {
20
+ this.ethers = await import('ethers');
21
+ }
22
+ return this.ethers;
23
+ }
24
+
25
+ // ========================================================================
26
+ // MEMBER MANAGEMENT
27
+ // ========================================================================
28
+
29
+ /**
30
+ * Add a single member to the contract
31
+ * @param {string} userId - User identifier (string-based, e.g., 'alice', 'telegram_12345')
32
+ * @returns {Promise<{txHash, receipt}>}
33
+ */
34
+ async addMember(userId) {
35
+ const tx = await this.contract.addMember(userId);
36
+ const receipt = await tx.wait();
37
+ return { txHash: receipt.hash, receipt };
38
+ }
39
+
40
+ /**
41
+ * Add multiple members at once (batch operation)
42
+ * @param {string[]} userIds - Array of user identifiers
43
+ * @returns {Promise<{txHash, receipt}>}
44
+ */
45
+ async addMembers(userIds) {
46
+ const tx = await this.contract.addMembers(userIds);
47
+ const receipt = await tx.wait();
48
+ return { txHash: receipt.hash, receipt };
49
+ }
50
+
51
+ /**
52
+ * Get all member user IDs
53
+ * @returns {Promise<string[]>}
54
+ */
55
+ async getMembers() {
56
+ const userIds = await this.contract.userIds();
57
+ // userIds is typically a getter for the array - try different approaches
58
+ try {
59
+ // Try to get all members by iterating
60
+ const members = [];
61
+ let i = 0;
62
+ while (true) {
63
+ try {
64
+ const userId = await this.contract.userIds(i);
65
+ members.push(userId);
66
+ i++;
67
+ } catch {
68
+ break;
69
+ }
70
+ }
71
+ return members;
72
+ } catch {
73
+ return [];
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Check if a user is a member
79
+ * @param {string} userId - User identifier
80
+ * @returns {Promise<boolean>}
81
+ */
82
+ async isMember(userId) {
83
+ try {
84
+ // Different contracts use different naming conventions
85
+ if (this.type === 'Splitter') {
86
+ return await this.contract.isSplitterMember(userId);
87
+ }
88
+ return await this.contract.isMember(userId);
89
+ } catch {
90
+ return false;
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Get the address associated with a user ID
96
+ * @param {string} userId - User identifier
97
+ * @returns {Promise<string>} Ethereum address (or zero address if not set)
98
+ */
99
+ async getUserAddress(userId) {
100
+ return this.contract.userIdToAddress(userId);
101
+ }
102
+
103
+ // ========================================================================
104
+ // FUND DISTRIBUTION
105
+ // ========================================================================
106
+
107
+ /**
108
+ * Trigger reward distribution for ERC20 tokens
109
+ * @param {string} tokenAddress - ERC20 token contract address
110
+ * @param {string|bigint} amount - Amount in wei (or token's smallest unit)
111
+ * @returns {Promise<{txHash, receipt}>}
112
+ */
113
+ async reward(tokenAddress, amount) {
114
+ const ethers = await this._loadEthers();
115
+ const amountBigInt = typeof amount === 'string' ? ethers.parseUnits(amount, 18) : amount;
116
+ const tx = await this.contract.reward(tokenAddress, amountBigInt);
117
+ const receipt = await tx.wait();
118
+ return { txHash: receipt.hash, receipt };
119
+ }
120
+
121
+ /**
122
+ * Trigger reward distribution for ETH
123
+ * @param {string} amount - Amount in ETH (e.g., '1.5' for 1.5 ETH)
124
+ * @returns {Promise<{txHash, receipt}>}
125
+ */
126
+ async rewardEth(amount) {
127
+ const ethers = await this._loadEthers();
128
+ const amountWei = ethers.parseEther(amount);
129
+ const tx = await this.contract.reward(
130
+ '0x0000000000000000000000000000000000000000',
131
+ amountWei,
132
+ { value: amountWei }
133
+ );
134
+ const receipt = await tx.wait();
135
+ return { txHash: receipt.hash, receipt };
136
+ }
137
+
138
+ /**
139
+ * Send ETH directly to contract (triggers receive/fallback)
140
+ * @param {string} amount - Amount in ETH
141
+ * @returns {Promise<{txHash, receipt}>}
142
+ */
143
+ async sendEth(amount) {
144
+ const ethers = await this._loadEthers();
145
+ const signer = this.chainManager.getSigner();
146
+ const amountWei = ethers.parseEther(amount);
147
+ const tx = await signer.sendTransaction({
148
+ to: await this.contract.getAddress(),
149
+ value: amountWei
150
+ });
151
+ const receipt = await tx.wait();
152
+ return { txHash: receipt.hash, receipt };
153
+ }
154
+
155
+ /**
156
+ * Claim accumulated rewards for a user
157
+ * @param {string} userId - User identifier
158
+ * @param {string} beneficiaryAddress - Ethereum address to receive funds
159
+ * @returns {Promise<{txHash, receipt}>}
160
+ */
161
+ async claim(userId, beneficiaryAddress) {
162
+ const tx = await this.contract.claim(userId, beneficiaryAddress);
163
+ const receipt = await tx.wait();
164
+ return { txHash: receipt.hash, receipt };
165
+ }
166
+
167
+ /**
168
+ * Get ETH balance for a user
169
+ * @param {string} userId - User identifier
170
+ * @returns {Promise<string>} Balance in ETH
171
+ */
172
+ async getEthBalance(userId) {
173
+ const ethers = await this._loadEthers();
174
+ const balance = await this.contract.etherBalance(userId);
175
+ return ethers.formatEther(balance);
176
+ }
177
+
178
+ /**
179
+ * Get ERC20 token balance for a user
180
+ * @param {string} userId - User identifier
181
+ * @param {string} tokenAddress - ERC20 token address
182
+ * @returns {Promise<string>} Balance in tokens (18 decimals assumed)
183
+ */
184
+ async getTokenBalance(userId, tokenAddress) {
185
+ const ethers = await this._loadEthers();
186
+ const balance = await this.contract.tokenBalance(userId, tokenAddress);
187
+ return ethers.formatUnits(balance, 18);
188
+ }
189
+
190
+ /**
191
+ * Check if user has already claimed
192
+ * @param {string} userId - User identifier
193
+ * @returns {Promise<boolean>}
194
+ */
195
+ async hasClaimed(userId) {
196
+ return this.contract.hasClaimed(userId);
197
+ }
198
+
199
+ /**
200
+ * Get total deposited amount for a token
201
+ * @param {string} tokenAddress - Token address (use zero address for ETH)
202
+ * @returns {Promise<string>}
203
+ */
204
+ async getTotalDeposited(tokenAddress) {
205
+ const ethers = await this._loadEthers();
206
+ const total = await this.contract.totalDeposited(tokenAddress);
207
+ return ethers.formatUnits(total, 18);
208
+ }
209
+
210
+ // ========================================================================
211
+ // SPLITTER OPERATIONS
212
+ // ========================================================================
213
+
214
+ /**
215
+ * Set the internal/external split percentages (Splitter only)
216
+ * @param {number} internalPct - Internal percentage (0-100)
217
+ * @param {number} externalPct - External percentage (0-100, must sum to 100 with internal)
218
+ * @returns {Promise<{txHash, receipt}>}
219
+ */
220
+ async setContractSplit(internalPct, externalPct) {
221
+ this._requireType(['Splitter']);
222
+ if (internalPct + externalPct !== 100) {
223
+ throw new Error('Percentages must sum to 100');
224
+ }
225
+ const tx = await this.contract.setContractSplit(internalPct, externalPct);
226
+ const receipt = await tx.wait();
227
+ return { txHash: receipt.hash, receipt };
228
+ }
229
+
230
+ /**
231
+ * Get current split percentages (Splitter only)
232
+ * @returns {Promise<{internal: number, external: number}>}
233
+ */
234
+ async getContractSplit() {
235
+ this._requireType(['Splitter']);
236
+ const internal = await this.contract.internalContractSplitPercentage();
237
+ const external = await this.contract.externalContractSplitPercentage();
238
+ return {
239
+ internal: Number(internal),
240
+ external: Number(external)
241
+ };
242
+ }
243
+
244
+ /**
245
+ * Create a Managed child contract (Splitter only)
246
+ * @param {string} creatorUserId - Creator's user ID
247
+ * @param {string} name - Contract name
248
+ * @param {number} [parameter=0] - Optional parameter
249
+ * @returns {Promise<{address, txHash, receipt}>}
250
+ */
251
+ async createManagedContract(creatorUserId, name, parameter = 0) {
252
+ this._requireType(['Splitter']);
253
+ const tx = await this.contract.createManagedContract(creatorUserId, name, parameter);
254
+ const receipt = await tx.wait();
255
+ const address = await this.contract.contractsByType(`${name}_managed`);
256
+ return { address, txHash: receipt.hash, receipt };
257
+ }
258
+
259
+ /**
260
+ * Create a Zoned child contract (Splitter only)
261
+ * @param {string} creatorUserId - Creator's user ID
262
+ * @param {string} name - Contract name
263
+ * @param {number} [nZones=6] - Number of zones
264
+ * @returns {Promise<{address, txHash, receipt}>}
265
+ */
266
+ async createZonedContract(creatorUserId, name, nZones = 6) {
267
+ this._requireType(['Splitter']);
268
+ const tx = await this.contract.createZonedContract(creatorUserId, name, nZones);
269
+ const receipt = await tx.wait();
270
+ const address = await this.contract.contractsByType(`${name}_zoned`);
271
+ return { address, txHash: receipt.hash, receipt };
272
+ }
273
+
274
+ /**
275
+ * Get all child contract keys (Splitter only)
276
+ * @returns {Promise<string[]>}
277
+ */
278
+ async getChildContractKeys() {
279
+ this._requireType(['Splitter']);
280
+ return this.contract.getContractKeys();
281
+ }
282
+
283
+ /**
284
+ * Get child contract addresses (Splitter only)
285
+ * @returns {Promise<{keys: string[], addresses: string[]}>}
286
+ */
287
+ async getChildContracts() {
288
+ this._requireType(['Splitter']);
289
+ const [keys, addresses] = await this.contract.getContractAddresses();
290
+ return { keys, addresses };
291
+ }
292
+
293
+ /**
294
+ * Get child contract address by key (Splitter only)
295
+ * @param {string} key - Contract key (e.g., 'MyHolon_managed')
296
+ * @returns {Promise<string>}
297
+ */
298
+ async getChildContract(key) {
299
+ this._requireType(['Splitter']);
300
+ return this.contract.contractsByType(key);
301
+ }
302
+
303
+ // ========================================================================
304
+ // MANAGED OPERATIONS (Appreciation-Based)
305
+ // ========================================================================
306
+
307
+ /**
308
+ * Set appreciation values for members (Managed only)
309
+ * Appreciation determines reward distribution proportionally
310
+ * @param {string[]} userIds - Array of user identifiers
311
+ * @param {number[]} amounts - Array of appreciation amounts (integers)
312
+ * @returns {Promise<{txHash, receipt}>}
313
+ * @example
314
+ * // Alice gets 60%, Bob gets 40%
315
+ * await ops.setAppreciation(['alice', 'bob'], [300, 200]);
316
+ */
317
+ async setAppreciation(userIds, amounts) {
318
+ this._requireType(['Managed']);
319
+ if (userIds.length !== amounts.length) {
320
+ throw new Error('userIds and amounts arrays must have same length');
321
+ }
322
+ const tx = await this.contract.setAppreciation(userIds, amounts);
323
+ const receipt = await tx.wait();
324
+ return { txHash: receipt.hash, receipt };
325
+ }
326
+
327
+ /**
328
+ * Get appreciation value for a user (Managed only)
329
+ * @param {string} userId - User identifier
330
+ * @returns {Promise<number>}
331
+ */
332
+ async getAppreciation(userId) {
333
+ this._requireType(['Managed']);
334
+ const appreciation = await this.contract.appreciation(userId);
335
+ return Number(appreciation);
336
+ }
337
+
338
+ /**
339
+ * Get total appreciation across all members (Managed only)
340
+ * @returns {Promise<number>}
341
+ */
342
+ async getTotalAppreciation() {
343
+ this._requireType(['Managed']);
344
+ const total = await this.contract.totalappreciation();
345
+ return Number(total);
346
+ }
347
+
348
+ // ========================================================================
349
+ // ZONED OPERATIONS (Tier-Based)
350
+ // ========================================================================
351
+
352
+ /**
353
+ * Add a user to a zone (Zoned only)
354
+ * Higher zones receive more rewards based on quadratic formula
355
+ * @param {string} senderUserId - Sender's user ID (for authorization)
356
+ * @param {string} userId - User to add to zone
357
+ * @param {number} zone - Zone number (0 = unassigned, 1-N = contribution tiers)
358
+ * @returns {Promise<{txHash, receipt}>}
359
+ */
360
+ async addToZone(senderUserId, userId, zone) {
361
+ this._requireType(['Zoned']);
362
+ const tx = await this.contract.addToZone(senderUserId, userId, zone);
363
+ const receipt = await tx.wait();
364
+ return { txHash: receipt.hash, receipt };
365
+ }
366
+
367
+ /**
368
+ * Remove a user from their zone (Zoned only)
369
+ * @param {string} senderUserId - Sender's user ID
370
+ * @param {string} userId - User to remove
371
+ * @returns {Promise<{txHash, receipt}>}
372
+ */
373
+ async removeFromZone(senderUserId, userId) {
374
+ this._requireType(['Zoned']);
375
+ const tx = await this.contract.removeFromZone(senderUserId, userId);
376
+ const receipt = await tx.wait();
377
+ return { txHash: receipt.hash, receipt };
378
+ }
379
+
380
+ /**
381
+ * Get a user's zone (Zoned only)
382
+ * @param {string} userId - User identifier
383
+ * @returns {Promise<number>}
384
+ */
385
+ async getZone(userId) {
386
+ this._requireType(['Zoned']);
387
+ const zone = await this.contract.zones(userId);
388
+ return Number(zone);
389
+ }
390
+
391
+ /**
392
+ * Get number of zones (Zoned only)
393
+ * @returns {Promise<number>}
394
+ */
395
+ async getNumZones() {
396
+ this._requireType(['Zoned']);
397
+ const nzones = await this.contract.nzones();
398
+ return Number(nzones);
399
+ }
400
+
401
+ /**
402
+ * Set the reward function parameters (Zoned only)
403
+ * Formula: reward_units = a*zone² + b*zone + c
404
+ * @param {string} senderUserId - Sender's user ID
405
+ * @param {number} a - Quadratic coefficient
406
+ * @param {number} b - Linear coefficient
407
+ * @param {number} c - Constant
408
+ * @returns {Promise<{txHash, receipt}>}
409
+ */
410
+ async setZoneParameters(senderUserId, a, b, c) {
411
+ this._requireType(['Zoned']);
412
+ const tx = await this.contract.setRewardFunction(senderUserId, a, b, c);
413
+ const receipt = await tx.wait();
414
+ return { txHash: receipt.hash, receipt };
415
+ }
416
+
417
+ // ========================================================================
418
+ // APPRECIATIVE OPERATIONS (Peer-to-Peer)
419
+ // ========================================================================
420
+
421
+ /**
422
+ * Give appreciation from one user to another (Appreciative only)
423
+ * Each member has a limited appreciation pool to distribute
424
+ * @param {string} fromUserId - Giver's user ID
425
+ * @param {string} toUserId - Receiver's user ID
426
+ * @param {number} amount - Amount of appreciation to give
427
+ * @returns {Promise<{txHash, receipt}>}
428
+ */
429
+ async appreciate(fromUserId, toUserId, amount) {
430
+ this._requireType(['Appreciative']);
431
+ const tx = await this.contract.appreciate(fromUserId, toUserId, amount);
432
+ const receipt = await tx.wait();
433
+ return { txHash: receipt.hash, receipt };
434
+ }
435
+
436
+ /**
437
+ * Get appreciation given by a user (Appreciative only)
438
+ * @param {string} userId - User identifier
439
+ * @returns {Promise<number>}
440
+ */
441
+ async getAppreciationGiven(userId) {
442
+ this._requireType(['Appreciative']);
443
+ const given = await this.contract.appreciationGiven(userId);
444
+ return Number(given);
445
+ }
446
+
447
+ /**
448
+ * Get appreciation received by a user (Appreciative only)
449
+ * @param {string} userId - User identifier
450
+ * @returns {Promise<number>}
451
+ */
452
+ async getAppreciationReceived(userId) {
453
+ this._requireType(['Appreciative']);
454
+ const received = await this.contract.appreciationReceived(userId);
455
+ return Number(received);
456
+ }
457
+
458
+ /**
459
+ * Get remaining appreciation pool for a user (Appreciative only)
460
+ * @param {string} userId - User identifier
461
+ * @returns {Promise<number>}
462
+ */
463
+ async getAppreciationRemaining(userId) {
464
+ this._requireType(['Appreciative']);
465
+ const remaining = await this.contract.appreciationRemaining(userId);
466
+ return Number(remaining);
467
+ }
468
+
469
+ // ========================================================================
470
+ // BUNDLE OPERATIONS (2-Way Split)
471
+ // ========================================================================
472
+
473
+ /**
474
+ * Set steepness for zone decay (Bundle only)
475
+ * Higher steepness = faster decay = more concentration in higher zones
476
+ * @param {string|bigint} steepness - Steepness value (0 < s < 1e18, e.g., 0.5e18 for 50% decay)
477
+ * @returns {Promise<{txHash, receipt}>}
478
+ */
479
+ async setSteepness(steepness) {
480
+ this._requireType(['Bundle']);
481
+ const ethers = await this._loadEthers();
482
+ const steepnessValue = typeof steepness === 'string' ? ethers.parseEther(steepness) : steepness;
483
+ const tx = await this.contract.setSteepness(steepnessValue);
484
+ const receipt = await tx.wait();
485
+ return { txHash: receipt.hash, receipt };
486
+ }
487
+
488
+ /**
489
+ * Get current steepness (Bundle only)
490
+ * @returns {Promise<string>} Steepness as decimal string (e.g., '0.5')
491
+ */
492
+ async getSteepness() {
493
+ this._requireType(['Bundle']);
494
+ const ethers = await this._loadEthers();
495
+ const steepness = await this.contract.steepness();
496
+ return ethers.formatEther(steepness);
497
+ }
498
+
499
+ /**
500
+ * Set interior split percentages (Bundle only)
501
+ * @param {string[]} userIds - User identifiers
502
+ * @param {number[]} percentages - Percentages (must sum to 100)
503
+ * @returns {Promise<{txHash, receipt}>}
504
+ */
505
+ async setInteriorSplit(userIds, percentages) {
506
+ this._requireType(['Bundle']);
507
+ const total = percentages.reduce((a, b) => a + b, 0);
508
+ if (total !== 100) {
509
+ throw new Error('Percentages must sum to 100');
510
+ }
511
+ const tx = await this.contract.setInteriorSplit(userIds, percentages);
512
+ const receipt = await tx.wait();
513
+ return { txHash: receipt.hash, receipt };
514
+ }
515
+
516
+ // ========================================================================
517
+ // GOVERNANCE (Bundle)
518
+ // ========================================================================
519
+
520
+ /**
521
+ * Start an election for new owner (Bundle only)
522
+ * @returns {Promise<{txHash, receipt}>}
523
+ */
524
+ async startElection() {
525
+ this._requireType(['Bundle']);
526
+ const tx = await this.contract.startElection();
527
+ const receipt = await tx.wait();
528
+ return { txHash: receipt.hash, receipt };
529
+ }
530
+
531
+ /**
532
+ * Nominate yourself as candidate (Bundle only)
533
+ * @returns {Promise<{txHash, receipt}>}
534
+ */
535
+ async nominateSelf() {
536
+ this._requireType(['Bundle']);
537
+ const tx = await this.contract.nominateSelf();
538
+ const receipt = await tx.wait();
539
+ return { txHash: receipt.hash, receipt };
540
+ }
541
+
542
+ /**
543
+ * Vote for a nominee (Bundle only)
544
+ * @param {string} nomineeAddress - Address of the nominee
545
+ * @returns {Promise<{txHash, receipt}>}
546
+ */
547
+ async vote(nomineeAddress) {
548
+ this._requireType(['Bundle']);
549
+ const tx = await this.contract.vote(nomineeAddress);
550
+ const receipt = await tx.wait();
551
+ return { txHash: receipt.hash, receipt };
552
+ }
553
+
554
+ /**
555
+ * Finalize election and transfer ownership to winner (Bundle only)
556
+ * @returns {Promise<{txHash, receipt}>}
557
+ */
558
+ async finalizeElection() {
559
+ this._requireType(['Bundle']);
560
+ const tx = await this.contract.finalizeElection();
561
+ const receipt = await tx.wait();
562
+ return { txHash: receipt.hash, receipt };
563
+ }
564
+
565
+ // ========================================================================
566
+ // READ-ONLY QUERIES
567
+ // ========================================================================
568
+
569
+ /**
570
+ * Get contract name
571
+ * @returns {Promise<string>}
572
+ */
573
+ async getName() {
574
+ return this.contract.name();
575
+ }
576
+
577
+ /**
578
+ * Get contract owner address
579
+ * @returns {Promise<string>}
580
+ */
581
+ async getOwner() {
582
+ return this.contract.owner();
583
+ }
584
+
585
+ /**
586
+ * Get bot address (for Managed/Zoned)
587
+ * @returns {Promise<string>}
588
+ */
589
+ async getBotAddress() {
590
+ try {
591
+ return await this.contract.botAddress();
592
+ } catch {
593
+ return null;
594
+ }
595
+ }
596
+
597
+ /**
598
+ * Get creator user ID
599
+ * @returns {Promise<string>}
600
+ */
601
+ async getCreatorUserId() {
602
+ try {
603
+ return await this.contract.creatorUserId();
604
+ } catch {
605
+ return null;
606
+ }
607
+ }
608
+
609
+ /**
610
+ * Get contract address
611
+ * @returns {Promise<string>}
612
+ */
613
+ async getAddress() {
614
+ return this.contract.getAddress();
615
+ }
616
+
617
+ /**
618
+ * Get contract type
619
+ * @returns {string}
620
+ */
621
+ getType() {
622
+ return this.type;
623
+ }
624
+
625
+ // ========================================================================
626
+ // UTILITIES
627
+ // ========================================================================
628
+
629
+ /**
630
+ * Require specific contract type(s)
631
+ * @private
632
+ */
633
+ _requireType(allowedTypes) {
634
+ if (!allowedTypes.includes(this.type)) {
635
+ throw new Error(`Operation only available for ${allowedTypes.join('/')} contracts. This is a ${this.type} contract.`);
636
+ }
637
+ }
638
+
639
+ /**
640
+ * Get raw contract instance for advanced usage
641
+ * @returns {Contract}
642
+ */
643
+ getRawContract() {
644
+ return this.contract;
645
+ }
646
+
647
+ /**
648
+ * Execute a raw contract call
649
+ * @param {string} method - Method name
650
+ * @param {Array} args - Method arguments
651
+ * @returns {Promise<any>}
652
+ */
653
+ async call(method, ...args) {
654
+ return this.contract[method](...args);
655
+ }
656
+
657
+ /**
658
+ * Execute a raw contract transaction
659
+ * @param {string} method - Method name
660
+ * @param {Array} args - Method arguments
661
+ * @returns {Promise<{txHash, receipt}>}
662
+ */
663
+ async send(method, ...args) {
664
+ const tx = await this.contract[method](...args);
665
+ const receipt = await tx.wait();
666
+ return { txHash: receipt.hash, receipt };
667
+ }
668
+ }
669
+
670
+ export default ContractOperations;