@hivemind-os/collective-core 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/.test-data/05a845c4-1682-41b9-97e5-65a5263156c0/spending.sqlite +0 -0
  2. package/.test-data/10e18ba5-98f0-42e0-8899-06a09459ae85/agents.sqlite +0 -0
  3. package/.test-data/20464456-23cb-4ff7-8df5-3c129fb95a90/agents.sqlite +0 -0
  4. package/.test-data/2e2ec66c-e8b4-43eb-945f-cca84ed0a0f6/agents.sqlite +0 -0
  5. package/.test-data/2f5ef9b7-01da-4ba0-a6fa-af8063a2567c/agents.sqlite +0 -0
  6. package/.test-data/3ac13cd5-84c9-4e71-8b89-6d3ef4dd819c/agents.sqlite +0 -0
  7. package/.test-data/40b7dd4a-fae0-4a5d-a6a0-64c9048af35e/agents.sqlite +0 -0
  8. package/.test-data/59511a04-42f8-4286-bb1e-733795e08749/agents.sqlite +0 -0
  9. package/.test-data/6778e8c5-6eb5-416d-8aca-9d559cdf83e4/agents.sqlite +0 -0
  10. package/.test-data/7648dc86-df90-4460-8f9c-55cdb481324b/agents.sqlite +0 -0
  11. package/.test-data/77162d98-6b22-41b1-a50f-536fab739f8a/agents.sqlite +0 -0
  12. package/.test-data/798dbdab-cbe7-4edd-8a5b-ae99c285fe17/agents.sqlite +0 -0
  13. package/.test-data/8033d6ac-8b85-454d-b708-00ac695b22f8/agents.sqlite +0 -0
  14. package/.test-data/9155a4f4-cda3-487c-9eed-921c82d7550f/agents.sqlite +0 -0
  15. package/.test-data/9bfeee53-c231-46c3-8c93-2180933f5d50/agents.sqlite +0 -0
  16. package/.test-data/a4c64287-79f6-46e9-847d-2803c63a74fd/agents.sqlite +0 -0
  17. package/.test-data/b8f58952-1ed8-46ff-abd7-21fb86e9457f/agents.sqlite +0 -0
  18. package/.test-data/c3060504-3187-41ed-8532-82332be48b0b/spending.sqlite +0 -0
  19. package/.test-data/cc471629-8006-4fc1-b8a1-399d2df2cc4e/agents.sqlite +0 -0
  20. package/.test-data/dbca3bef-397d-4bbc-bd4c-4f7b14103e04/spending.sqlite +0 -0
  21. package/.test-data/f1283dd1-6602-4de7-a050-16aac7abc288/agents.sqlite +0 -0
  22. package/.turbo/turbo-build.log +14 -0
  23. package/dist/index.d.ts +1675 -0
  24. package/dist/index.js +8006 -0
  25. package/dist/index.js.map +1 -0
  26. package/package.json +41 -0
  27. package/src/auth/device-flow.ts +108 -0
  28. package/src/auth/ed25519-provider.ts +43 -0
  29. package/src/auth/errors.ts +82 -0
  30. package/src/auth/evm-key.ts +55 -0
  31. package/src/auth/index.ts +8 -0
  32. package/src/auth/session-state.ts +25 -0
  33. package/src/auth/session-store.ts +510 -0
  34. package/src/auth/types.ts +81 -0
  35. package/src/auth/zklogin-provider.ts +902 -0
  36. package/src/blobstore/WALRUS_FINDINGS.md +284 -0
  37. package/src/blobstore/encrypted-store.ts +56 -0
  38. package/src/blobstore/fs-store.ts +91 -0
  39. package/src/blobstore/hybrid-store.ts +144 -0
  40. package/src/blobstore/index.ts +5 -0
  41. package/src/blobstore/interface.ts +33 -0
  42. package/src/blobstore/walrus-spike.ts +345 -0
  43. package/src/blobstore/walrus-store.ts +551 -0
  44. package/src/cache/agent-cache.ts +403 -0
  45. package/src/cache/index.ts +1 -0
  46. package/src/crypto/encryption.ts +152 -0
  47. package/src/crypto/index.ts +2 -0
  48. package/src/crypto/x25519.ts +41 -0
  49. package/src/dispute/client.ts +191 -0
  50. package/src/dispute/index.ts +1 -0
  51. package/src/events/index.ts +2 -0
  52. package/src/events/parser.ts +291 -0
  53. package/src/events/subscription.ts +131 -0
  54. package/src/evm/constants.ts +6 -0
  55. package/src/evm/index.ts +2 -0
  56. package/src/evm/wallet.ts +136 -0
  57. package/src/identity/did.ts +36 -0
  58. package/src/identity/index.ts +4 -0
  59. package/src/identity/keypair.ts +199 -0
  60. package/src/identity/signing.ts +28 -0
  61. package/src/index.ts +22 -0
  62. package/src/internal/parsing.ts +416 -0
  63. package/src/marketplace/client.ts +349 -0
  64. package/src/marketplace/index.ts +1 -0
  65. package/src/metering/hash-chain.ts +94 -0
  66. package/src/metering/index.ts +4 -0
  67. package/src/metering/meter.ts +80 -0
  68. package/src/metering/streaming.ts +196 -0
  69. package/src/metering/verification.ts +104 -0
  70. package/src/payment/index.ts +1 -0
  71. package/src/payment/rail-selector.ts +41 -0
  72. package/src/registry/client.ts +328 -0
  73. package/src/registry/index.ts +1 -0
  74. package/src/relay/consumer-client.ts +497 -0
  75. package/src/relay/index.ts +1 -0
  76. package/src/relay-registry/client.ts +295 -0
  77. package/src/relay-registry/discovery.ts +109 -0
  78. package/src/relay-registry/index.ts +2 -0
  79. package/src/reputation/anchor-client.ts +126 -0
  80. package/src/reputation/event-publisher.ts +67 -0
  81. package/src/reputation/index.ts +5 -0
  82. package/src/reputation/merkle.ts +79 -0
  83. package/src/reputation/score-calculator.ts +133 -0
  84. package/src/reputation/serialization.ts +37 -0
  85. package/src/reputation/store.ts +165 -0
  86. package/src/reputation/validation.ts +135 -0
  87. package/src/routing/circuit-breaker.ts +111 -0
  88. package/src/routing/fan-out.ts +266 -0
  89. package/src/routing/index.ts +4 -0
  90. package/src/routing/performance.ts +244 -0
  91. package/src/routing/selector.ts +225 -0
  92. package/src/spending/index.ts +1 -0
  93. package/src/spending/policy.ts +271 -0
  94. package/src/staking/client.ts +319 -0
  95. package/src/staking/index.ts +1 -0
  96. package/src/sui/client.ts +214 -0
  97. package/src/sui/index.ts +2 -0
  98. package/src/sui/tx-helpers.ts +1070 -0
  99. package/src/task/client.ts +215 -0
  100. package/src/task/index.ts +1 -0
  101. package/src/x402/client.ts +295 -0
  102. package/src/x402/index.ts +1 -0
  103. package/tests/auth/device-flow.test.ts +62 -0
  104. package/tests/auth/ed25519-provider.test.ts +24 -0
  105. package/tests/auth/evm-key.test.ts +31 -0
  106. package/tests/auth/session-store.test.ts +201 -0
  107. package/tests/auth/zklogin-provider.test.ts +366 -0
  108. package/tests/blobstore/encrypted-store.test.ts +78 -0
  109. package/tests/blobstore.test.ts +91 -0
  110. package/tests/cache.test.ts +124 -0
  111. package/tests/crypto/encryption.test.ts +70 -0
  112. package/tests/crypto/x25519.test.ts +47 -0
  113. package/tests/dispute/client.test.ts +238 -0
  114. package/tests/events.test.ts +202 -0
  115. package/tests/evm/wallet.test.ts +101 -0
  116. package/tests/hybrid-store.test.ts +121 -0
  117. package/tests/identity.test.ts +161 -0
  118. package/tests/marketplace.test.ts +308 -0
  119. package/tests/metering/hash-chain.test.ts +32 -0
  120. package/tests/metering/meter.test.ts +23 -0
  121. package/tests/metering/streaming.test.ts +52 -0
  122. package/tests/metering/verification.test.ts +27 -0
  123. package/tests/payment/rail-selector.test.ts +95 -0
  124. package/tests/registry.test.ts +183 -0
  125. package/tests/relay-consumer-client.test.ts +119 -0
  126. package/tests/relay-registry/client.test.ts +261 -0
  127. package/tests/reputation/event-publisher.test.ts +70 -0
  128. package/tests/reputation/merkle.test.ts +44 -0
  129. package/tests/reputation/score-calculator.test.ts +104 -0
  130. package/tests/reputation/store.test.ts +94 -0
  131. package/tests/routing/circuit-breaker.test.ts +45 -0
  132. package/tests/routing/fan-out.test.ts +123 -0
  133. package/tests/routing/performance.test.ts +49 -0
  134. package/tests/routing/selector.test.ts +114 -0
  135. package/tests/spending.test.ts +133 -0
  136. package/tests/staking/client.test.ts +286 -0
  137. package/tests/sui-client.test.ts +85 -0
  138. package/tests/task.test.ts +249 -0
  139. package/tests/tx-helpers.test.ts +70 -0
  140. package/tests/walrus-spike.test.ts +100 -0
  141. package/tests/walrus-store.test.ts +196 -0
  142. package/tests/x402/client.test.ts +116 -0
  143. package/tsconfig.json +9 -0
  144. package/tsup.config.ts +11 -0
  145. package/vitest.config.ts +8 -0
@@ -0,0 +1,416 @@
1
+ import { BidStatus, PaymentRail, PaymentScheme, RelayNodeStatus, TaskStatus, type AgentCard, type Bid, type Capability, type Dispute, type RelayNode, type StakePosition, type Task } from '@hivemind-os/collective-types';
2
+
3
+ const encoder = new TextEncoder();
4
+ const decoder = new TextDecoder();
5
+
6
+ export function isRecord(value: unknown): value is Record<string, unknown> {
7
+ return typeof value === 'object' && value !== null;
8
+ }
9
+
10
+ export function stringToBytes(value: string): Uint8Array {
11
+ return encoder.encode(value);
12
+ }
13
+
14
+ export function bytesToString(value: unknown): string {
15
+ if (typeof value === 'string') {
16
+ return value;
17
+ }
18
+
19
+ if (value instanceof Uint8Array) {
20
+ return decoder.decode(value);
21
+ }
22
+
23
+ if (Array.isArray(value) && value.every((entry) => typeof entry === 'number')) {
24
+ return decoder.decode(new Uint8Array(value));
25
+ }
26
+
27
+ return '';
28
+ }
29
+
30
+ export function bytesToHex(value: Uint8Array | number[]): string {
31
+ return Buffer.from(value).toString('hex');
32
+ }
33
+
34
+ export function normalizeMoveValue(value: unknown): unknown {
35
+ if (Array.isArray(value)) {
36
+ return value.map((entry) => normalizeMoveValue(entry));
37
+ }
38
+
39
+ if (!isRecord(value)) {
40
+ return value;
41
+ }
42
+
43
+ if (typeof value.id === 'string' && Object.keys(value).length === 1) {
44
+ return value.id;
45
+ }
46
+
47
+ if (isRecord(value.fields)) {
48
+ return normalizeMoveValue(value.fields);
49
+ }
50
+
51
+ return Object.fromEntries(
52
+ Object.entries(value).map(([key, entry]) => [key, normalizeMoveValue(entry)]),
53
+ );
54
+ }
55
+
56
+ export function normalizeObjectOwner(owner: unknown): string | undefined {
57
+ if (!isRecord(owner)) {
58
+ return undefined;
59
+ }
60
+
61
+ if (typeof owner.AddressOwner === 'string') {
62
+ return owner.AddressOwner;
63
+ }
64
+
65
+ if (typeof owner.ObjectOwner === 'string') {
66
+ return owner.ObjectOwner;
67
+ }
68
+
69
+ return undefined;
70
+ }
71
+
72
+ function getValue(record: Record<string, unknown>, ...keys: string[]): unknown {
73
+ for (const key of keys) {
74
+ if (key in record) {
75
+ return record[key];
76
+ }
77
+ }
78
+
79
+ return undefined;
80
+ }
81
+
82
+ function asString(value: unknown, fallback = ''): string {
83
+ return typeof value === 'string' ? value : fallback;
84
+ }
85
+
86
+ function asNumber(value: unknown, fallback = 0): number {
87
+ if (typeof value === 'number') {
88
+ return value;
89
+ }
90
+
91
+ if (typeof value === 'bigint') {
92
+ return Number(value);
93
+ }
94
+
95
+ if (typeof value === 'string' && value.length > 0) {
96
+ return Number(value);
97
+ }
98
+
99
+ return fallback;
100
+ }
101
+
102
+ function asBigInt(value: unknown, fallback = 0n): bigint {
103
+ if (typeof value === 'bigint') {
104
+ return value;
105
+ }
106
+
107
+ if (typeof value === 'number') {
108
+ return BigInt(value);
109
+ }
110
+
111
+ if (typeof value === 'string' && value.length > 0) {
112
+ return BigInt(value);
113
+ }
114
+
115
+ return fallback;
116
+ }
117
+
118
+ function asBoolean(value: unknown, fallback = false): boolean {
119
+ return typeof value === 'boolean' ? value : fallback;
120
+ }
121
+
122
+ function normalizeCapability(raw: unknown): Capability {
123
+ const record = isRecord(raw) ? (normalizeMoveValue(raw) as Record<string, unknown>) : {};
124
+ const amount = asBigInt(getValue(record, 'price_mist', 'priceMist', 'amount'));
125
+ const railsValue = getValue(record, 'paymentRails', 'payment_rails');
126
+ const paymentRails = Array.isArray(railsValue)
127
+ ? railsValue.filter((entry): entry is PaymentRail => typeof entry === 'string' && Object.values(PaymentRail).includes(entry as PaymentRail))
128
+ : undefined;
129
+
130
+ return {
131
+ name: asString(getValue(record, 'name')),
132
+ description: asString(getValue(record, 'description')),
133
+ version: asString(getValue(record, 'version')),
134
+ pricing: {
135
+ rail: PaymentRail.SUI_ESCROW,
136
+ amount,
137
+ currency: asString(getValue(record, 'currency'), 'MIST'),
138
+ },
139
+ executionMode: asExecutionMode(getValue(record, 'executionMode', 'execution_mode')),
140
+ paymentRails,
141
+ };
142
+ }
143
+
144
+ export function parseAgentCardFields(raw: unknown, fallbackId?: string): AgentCard {
145
+ const record = isRecord(raw) ? (normalizeMoveValue(raw) as Record<string, unknown>) : {};
146
+ const capabilitiesValue = getValue(record, 'capabilities');
147
+ const capabilities = Array.isArray(capabilitiesValue)
148
+ ? capabilitiesValue.map((entry) => normalizeCapability(entry))
149
+ : [];
150
+
151
+ return {
152
+ id: asString(getValue(record, 'id', 'card_id', 'cardId', 'objectId'), fallbackId ?? ''),
153
+ owner: asString(getValue(record, 'owner', 'agent', 'objectOwner')),
154
+ did: asString(getValue(record, 'did')) as AgentCard['did'],
155
+ name: asString(getValue(record, 'name')),
156
+ description: asString(getValue(record, 'description')),
157
+ capabilities,
158
+ endpoint: asString(getValue(record, 'endpoint')) || undefined,
159
+ relayEndpoints: normalizeRelayEndpoints(getValue(record, 'relayEndpoints', 'relay_endpoints')),
160
+ encryptionPublicKey: normalizeOptionalHex(getValue(record, 'encryption_public_key', 'encryptionPublicKey')),
161
+ active: asBoolean(getValue(record, 'active'), true),
162
+ version: asNumber(getValue(record, 'version'), 1),
163
+ registeredAt: asNumber(getValue(record, 'registered_at', 'registeredAt')),
164
+ updatedAt: asNumber(getValue(record, 'updated_at', 'updatedAt')),
165
+ totalTasksCompleted: normalizeOptionalNumber(getValue(record, 'total_tasks_completed', 'totalTasksCompleted')),
166
+ totalTasksFailed: normalizeOptionalNumber(getValue(record, 'total_tasks_failed', 'totalTasksFailed')),
167
+ totalTasksDisputed: normalizeOptionalNumber(getValue(record, 'total_tasks_disputed', 'totalTasksDisputed')),
168
+ totalEarningsMist: normalizeOptionalBigInt(getValue(record, 'total_earnings_mist', 'totalEarningsMist')),
169
+ hasStake: normalizeOptionalBoolean(getValue(record, 'has_stake', 'hasStake')),
170
+ stakeMist: normalizeOptionalBigInt(getValue(record, 'stake_mist', 'stakeMist')),
171
+ stakeType: normalizeStakeType(getValue(record, 'stake_type', 'stakeType')),
172
+ };
173
+ }
174
+
175
+ function normalizeOptionalAddress(value: unknown): string | undefined {
176
+ const address = asString(value);
177
+ return address.length === 0 || /^0x0+$/i.test(address) ? undefined : address;
178
+ }
179
+
180
+ function normalizeOptionalString(value: unknown): string | undefined {
181
+ const normalized = bytesToString(value);
182
+ return normalized.length > 0 ? normalized : undefined;
183
+ }
184
+
185
+ function normalizeOptionalNumber(value: unknown): number | undefined {
186
+ if (value === undefined || value === null || value === '') {
187
+ return undefined;
188
+ }
189
+ return asNumber(value);
190
+ }
191
+
192
+ function normalizeOptionalBigInt(value: unknown): bigint | undefined {
193
+ if (value === undefined || value === null || value === '') {
194
+ return undefined;
195
+ }
196
+ return asBigInt(value);
197
+ }
198
+
199
+ function normalizeOptionalHex(value: unknown): string | undefined {
200
+ const normalized = bytesToOptionalHex(value);
201
+ return normalized.length > 0 ? normalized : undefined;
202
+ }
203
+
204
+ function normalizeOptionalBoolean(value: unknown): boolean | undefined {
205
+ return typeof value === 'boolean' ? value : undefined;
206
+ }
207
+
208
+ function normalizePaymentScheme(value: unknown): PaymentScheme | undefined {
209
+ if (value === 0 || value === '0' || value === PaymentScheme.EXACT) {
210
+ return PaymentScheme.EXACT;
211
+ }
212
+ if (value === 1 || value === '1' || value === PaymentScheme.UPTO) {
213
+ return PaymentScheme.UPTO;
214
+ }
215
+ if (value === 2 || value === '2' || value === PaymentScheme.STREAM) {
216
+ return PaymentScheme.STREAM;
217
+ }
218
+ return undefined;
219
+ }
220
+
221
+ function normalizeRelayEndpoints(value: unknown): AgentCard['relayEndpoints'] {
222
+ if (!Array.isArray(value)) {
223
+ return undefined;
224
+ }
225
+
226
+ const relayEndpoints = value
227
+ .map((entry) => {
228
+ const record = isRecord(entry) ? (normalizeMoveValue(entry) as Record<string, unknown>) : null;
229
+ const endpoint = record ? asString(getValue(record, 'endpoint', 'url', 'serviceEndpoint')) : '';
230
+ if (!endpoint) {
231
+ return null;
232
+ }
233
+
234
+ return {
235
+ relayDid: asString(getValue(record, 'relayDid', 'relay_did')) as AgentCard['did'] | undefined,
236
+ endpoint,
237
+ modes: Array.isArray(record?.modes)
238
+ ? record.modes.filter(
239
+ (mode): mode is NonNullable<NonNullable<AgentCard['relayEndpoints']>[number]['modes']>[number] =>
240
+ typeof mode === 'string',
241
+ )
242
+ : undefined,
243
+ };
244
+ })
245
+ .filter((entry): entry is NonNullable<AgentCard['relayEndpoints']>[number] => Boolean(entry));
246
+
247
+ return relayEndpoints.length > 0 ? relayEndpoints : undefined;
248
+ }
249
+
250
+ function bytesToOptionalHex(value: unknown): string {
251
+ if (typeof value === 'string' && /^[a-f0-9]+$/i.test(value) && value.length % 2 === 0) {
252
+ return value;
253
+ }
254
+
255
+ if (value instanceof Uint8Array) {
256
+ return bytesToHex(value);
257
+ }
258
+
259
+ if (Array.isArray(value) && value.every((entry) => typeof entry === 'number')) {
260
+ return bytesToHex(value);
261
+ }
262
+
263
+ return '';
264
+ }
265
+
266
+ function asExecutionMode(value: unknown): Capability['executionMode'] {
267
+ return value === 'sync' || value === 'async' ? value : undefined;
268
+ }
269
+
270
+ function normalizeStakeType(value: unknown): StakePosition['stakeType'] | AgentCard['stakeType'] | undefined {
271
+ if (value === 0 || value === '0' || value === 'agent') {
272
+ return 'agent';
273
+ }
274
+ if (value === 1 || value === '1' || value === 'relay') {
275
+ return 'relay';
276
+ }
277
+ return undefined;
278
+ }
279
+
280
+ function normalizeRelayStatus(value: unknown): RelayNodeStatus {
281
+ if (value === RelayNodeStatus.ACTIVE || value === '0' || value === 0 || value === 'ACTIVE') {
282
+ return RelayNodeStatus.ACTIVE;
283
+ }
284
+ if (value === RelayNodeStatus.INACTIVE || value === '1' || value === 1 || value === 'INACTIVE') {
285
+ return RelayNodeStatus.INACTIVE;
286
+ }
287
+ if (value === RelayNodeStatus.SLASHED || value === '2' || value === 2 || value === 'SLASHED') {
288
+ return RelayNodeStatus.SLASHED;
289
+ }
290
+ return RelayNodeStatus.ACTIVE;
291
+ }
292
+
293
+ function normalizeBalanceValue(value: unknown): bigint {
294
+ if (isRecord(value)) {
295
+ return asBigInt(getValue(value, 'value'));
296
+ }
297
+ return asBigInt(value);
298
+ }
299
+
300
+ export function parseStakePositionFields(raw: unknown, fallbackId?: string): StakePosition {
301
+ const record = isRecord(raw) ? (normalizeMoveValue(raw) as Record<string, unknown>) : {};
302
+ const balanceValue = getValue(record, 'balance', 'balanceMist', 'balance_mist');
303
+ const deactivatedAt = asNumber(getValue(record, 'deactivated_at', 'deactivatedAt'));
304
+ const balanceMist = normalizeBalanceValue(balanceValue);
305
+ const stakeType = normalizeStakeType(getValue(record, 'stake_type', 'stakeType')) ?? 'agent';
306
+ const meetsMinimum = stakeType === 'relay' ? balanceMist >= 100_000_000_000n : balanceMist >= 10_000_000_000n;
307
+ const isActive = deactivatedAt === 0 && meetsMinimum;
308
+
309
+ return {
310
+ id: asString(getValue(record, 'id', 'stake_id', 'stakeId', 'objectId'), fallbackId ?? ''),
311
+ owner: asString(getValue(record, 'owner', 'objectOwner')),
312
+ stakeType,
313
+ balanceMist,
314
+ stakedAt: asNumber(getValue(record, 'staked_at', 'stakedAt')),
315
+ deactivatedAt,
316
+ slashedAmount: asBigInt(getValue(record, 'slashed_amount', 'slashedAmount')),
317
+ isActive,
318
+ meetsMinium: meetsMinimum,
319
+ meetsMinimum,
320
+ };
321
+ }
322
+
323
+ export function parseRelayNodeFields(raw: unknown, fallbackId?: string): RelayNode {
324
+ const record = isRecord(raw) ? (normalizeMoveValue(raw) as Record<string, unknown>) : {};
325
+ const capabilitiesValue = getValue(record, 'capabilities');
326
+ const capabilities = Array.isArray(capabilitiesValue)
327
+ ? capabilitiesValue.map((entry) => bytesToString(entry)).filter(Boolean)
328
+ : [];
329
+
330
+ return {
331
+ id: asString(getValue(record, 'id', 'relay_id', 'relayId', 'objectId'), fallbackId ?? ''),
332
+ operator: asString(getValue(record, 'operator', 'owner', 'objectOwner')),
333
+ endpoint: asString(getValue(record, 'endpoint')),
334
+ stakePositionId: asString(getValue(record, 'stake_position_id', 'stakePositionId')),
335
+ capabilities,
336
+ region: asString(getValue(record, 'region')),
337
+ status: normalizeRelayStatus(getValue(record, 'status')),
338
+ registeredAt: asNumber(getValue(record, 'registered_at', 'registeredAt')),
339
+ lastHeartbeat: asNumber(getValue(record, 'last_heartbeat', 'lastHeartbeat')),
340
+ routingFeeBps: asNumber(getValue(record, 'routing_fee_bps', 'routingFeeBps')),
341
+ totalRouted: asNumber(getValue(record, 'total_routed', 'totalRouted')),
342
+ totalFeesEarnedMist: asBigInt(getValue(record, 'total_fees_earned', 'totalFeesEarnedMist')),
343
+ };
344
+ }
345
+
346
+ export function parseTaskFields(raw: unknown, fallbackId?: string): Task {
347
+ const record = isRecord(raw) ? (normalizeMoveValue(raw) as Record<string, unknown>) : {};
348
+ const status = asNumber(getValue(record, 'status'), TaskStatus.OPEN) as TaskStatus;
349
+ const acceptedAt = asNumber(getValue(record, 'accepted_at', 'acceptedAt'));
350
+ const completedAt = asNumber(getValue(record, 'completed_at', 'completedAt'));
351
+ const paymentScheme = normalizePaymentScheme(getValue(record, 'payment_scheme', 'paymentScheme'));
352
+
353
+ return {
354
+ id: asString(getValue(record, 'id', 'task_id', 'taskId', 'objectId'), fallbackId ?? ''),
355
+ requester: asString(getValue(record, 'requester')),
356
+ provider: normalizeOptionalAddress(getValue(record, 'provider')),
357
+ capability: asString(getValue(record, 'capability')),
358
+ category: asString(getValue(record, 'category'), 'general'),
359
+ inputBlobId: bytesToString(getValue(record, 'input_blob_id', 'inputBlobId')),
360
+ resultBlobId: normalizeOptionalString(getValue(record, 'result_blob_id', 'resultBlobId')),
361
+ price: asBigInt(getValue(record, 'price')),
362
+ paymentScheme,
363
+ maxPrice: normalizeOptionalBigInt(getValue(record, 'max_price', 'maxPrice')),
364
+ meteredUnits: normalizeOptionalNumber(getValue(record, 'metered_units', 'meteredUnits')),
365
+ unitPrice: normalizeOptionalBigInt(getValue(record, 'unit_price', 'unitPrice')),
366
+ verificationHash: normalizeOptionalHex(getValue(record, 'verification_hash', 'verificationHash')),
367
+ status,
368
+ disputeWindowMs: asNumber(getValue(record, 'dispute_window_ms', 'disputeWindowMs')),
369
+ createdAt: asNumber(getValue(record, 'created_at', 'createdAt')),
370
+ acceptedAt: acceptedAt > 0 ? acceptedAt : undefined,
371
+ completedAt: completedAt > 0 ? completedAt : undefined,
372
+ expiresAt: asNumber(getValue(record, 'expires_at', 'expiresAt')),
373
+ agreementHash: normalizeOptionalString(getValue(record, 'agreement_hash', 'agreementHash')),
374
+ };
375
+ }
376
+
377
+ export function parseBidFields(raw: unknown, fallbackId?: string): Bid {
378
+ const record = isRecord(raw) ? (normalizeMoveValue(raw) as Record<string, unknown>) : {};
379
+ const evidenceBlob = normalizeOptionalString(getValue(record, 'evidence_blob', 'evidenceBlob'));
380
+
381
+ return {
382
+ id: asString(getValue(record, 'id', 'bid_id', 'bidId', 'objectId'), fallbackId ?? ''),
383
+ taskId: asString(getValue(record, 'task_id', 'taskId')),
384
+ bidder: asString(getValue(record, 'bidder')),
385
+ bidPrice: asBigInt(getValue(record, 'bid_price', 'bidPrice')),
386
+ reputationScore: asBigInt(getValue(record, 'reputation_score', 'reputationScore')),
387
+ evidenceBlob,
388
+ createdAt: asNumber(getValue(record, 'created_at', 'createdAt')),
389
+ status: asNumber(getValue(record, 'status'), BidStatus.ACTIVE) as BidStatus,
390
+ };
391
+ }
392
+
393
+ export function parseDisputeFields(raw: unknown, fallbackId?: string): Dispute {
394
+ const record = isRecord(raw) ? (normalizeMoveValue(raw) as Record<string, unknown>) : {};
395
+ const respondedAt = asNumber(getValue(record, 'responded_at', 'respondedAt'));
396
+ const resolvedAt = asNumber(getValue(record, 'resolved_at', 'resolvedAt'));
397
+
398
+ return {
399
+ id: asString(getValue(record, 'id', 'dispute_id', 'disputeId', 'objectId'), fallbackId ?? ''),
400
+ taskId: asString(getValue(record, 'task_id', 'taskId')),
401
+ requester: asString(getValue(record, 'requester')),
402
+ provider: asString(getValue(record, 'provider')),
403
+ escrowAmount: asBigInt(getValue(record, 'escrow_amount', 'escrowAmount')),
404
+ status: asNumber(getValue(record, 'status')) as Dispute['status'],
405
+ requesterEvidenceBlob: bytesToString(getValue(record, 'requester_evidence_blob', 'requesterEvidenceBlob')),
406
+ providerEvidenceBlob: normalizeOptionalString(getValue(record, 'provider_evidence_blob', 'providerEvidenceBlob')),
407
+ requesterProposedSplit: asBigInt(getValue(record, 'requester_proposed_split', 'requesterProposedSplit')),
408
+ providerProposedSplit: asBigInt(getValue(record, 'provider_proposed_split', 'providerProposedSplit')),
409
+ arbitrator: normalizeOptionalAddress(getValue(record, 'arbitrator')),
410
+ rulingSplit: asBigInt(getValue(record, 'ruling_split', 'rulingSplit')),
411
+ openedAt: asNumber(getValue(record, 'opened_at', 'openedAt')),
412
+ respondedAt: respondedAt > 0 ? respondedAt : undefined,
413
+ resolvedAt: resolvedAt > 0 ? resolvedAt : undefined,
414
+ resolutionDeadline: asNumber(getValue(record, 'resolution_deadline', 'resolutionDeadline')),
415
+ };
416
+ }