@the-situation/indexer 0.9.1 → 0.11.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 (163) hide show
  1. package/dist/api/app.d.ts +4 -0
  2. package/dist/api/app.d.ts.map +1 -1
  3. package/dist/api/app.js +3 -3
  4. package/dist/api/app.js.map +1 -1
  5. package/dist/api/middleware/admin-auth.d.ts.map +1 -1
  6. package/dist/api/middleware/admin-auth.js +11 -3
  7. package/dist/api/middleware/admin-auth.js.map +1 -1
  8. package/dist/api/middleware/error-handler.d.ts.map +1 -1
  9. package/dist/api/middleware/error-handler.js +2 -2
  10. package/dist/api/middleware/error-handler.js.map +1 -1
  11. package/dist/api/routes/admin.d.ts +2 -0
  12. package/dist/api/routes/admin.d.ts.map +1 -1
  13. package/dist/api/routes/admin.js +29 -5
  14. package/dist/api/routes/admin.js.map +1 -1
  15. package/dist/api/routes/health.d.ts +3 -2
  16. package/dist/api/routes/health.d.ts.map +1 -1
  17. package/dist/api/routes/health.js +11 -4
  18. package/dist/api/routes/health.js.map +1 -1
  19. package/dist/api/routes/index.d.ts +3 -3
  20. package/dist/api/routes/index.d.ts.map +1 -1
  21. package/dist/api/routes/index.js +3 -3
  22. package/dist/api/routes/index.js.map +1 -1
  23. package/dist/api/routes/market-events.d.ts.map +1 -1
  24. package/dist/api/routes/market-events.js +14 -8
  25. package/dist/api/routes/market-events.js.map +1 -1
  26. package/dist/api/routes/market-traders.d.ts.map +1 -1
  27. package/dist/api/routes/market-traders.js +1 -1
  28. package/dist/api/routes/market-traders.js.map +1 -1
  29. package/dist/api/routes/markets.d.ts.map +1 -1
  30. package/dist/api/routes/markets.js +83 -5
  31. package/dist/api/routes/markets.js.map +1 -1
  32. package/dist/api/routes/rankings.d.ts.map +1 -1
  33. package/dist/api/routes/rankings.js +2 -1
  34. package/dist/api/routes/rankings.js.map +1 -1
  35. package/dist/api/routes/trader-events.d.ts +1 -1
  36. package/dist/api/routes/trader-events.d.ts.map +1 -1
  37. package/dist/api/routes/trader-events.js +11 -6
  38. package/dist/api/routes/trader-events.js.map +1 -1
  39. package/dist/api/routes/ws.d.ts.map +1 -1
  40. package/dist/api/routes/ws.js +4 -3
  41. package/dist/api/routes/ws.js.map +1 -1
  42. package/dist/client/IndexerClient.d.ts +10 -1
  43. package/dist/client/IndexerClient.d.ts.map +1 -1
  44. package/dist/client/IndexerClient.js.map +1 -1
  45. package/dist/client/IndexerClientLive.d.ts +1 -0
  46. package/dist/client/IndexerClientLive.d.ts.map +1 -1
  47. package/dist/client/IndexerClientLive.js +54 -8
  48. package/dist/client/IndexerClientLive.js.map +1 -1
  49. package/dist/client/convenience.d.ts +11 -2
  50. package/dist/client/convenience.d.ts.map +1 -1
  51. package/dist/client/convenience.js +4 -0
  52. package/dist/client/convenience.js.map +1 -1
  53. package/dist/client/index.d.ts +6 -5
  54. package/dist/client/index.d.ts.map +1 -1
  55. package/dist/client/index.js +4 -4
  56. package/dist/client/index.js.map +1 -1
  57. package/dist/client/trader-stats.d.ts +2 -2
  58. package/dist/client/trader-stats.d.ts.map +1 -1
  59. package/dist/client/trader-stats.js +1 -1
  60. package/dist/client/trader-stats.js.map +1 -1
  61. package/dist/config.d.ts +3 -0
  62. package/dist/config.d.ts.map +1 -1
  63. package/dist/config.js +19 -6
  64. package/dist/config.js.map +1 -1
  65. package/dist/db/connection.d.ts.map +1 -1
  66. package/dist/db/connection.js +4 -1
  67. package/dist/db/connection.js.map +1 -1
  68. package/dist/db/repositories/event.d.ts +1 -1
  69. package/dist/db/repositories/event.d.ts.map +1 -1
  70. package/dist/db/repositories/event.js +11 -9
  71. package/dist/db/repositories/event.js.map +1 -1
  72. package/dist/db/repositories/index.d.ts +3 -2
  73. package/dist/db/repositories/index.d.ts.map +1 -1
  74. package/dist/db/repositories/index.js +3 -2
  75. package/dist/db/repositories/index.js.map +1 -1
  76. package/dist/db/repositories/market-state.d.ts +1 -0
  77. package/dist/db/repositories/market-state.d.ts.map +1 -1
  78. package/dist/db/repositories/market-state.js +8 -0
  79. package/dist/db/repositories/market-state.js.map +1 -1
  80. package/dist/db/repositories/market.d.ts +1 -1
  81. package/dist/db/repositories/market.d.ts.map +1 -1
  82. package/dist/db/repositories/market.js +39 -17
  83. package/dist/db/repositories/market.js.map +1 -1
  84. package/dist/db/repositories/multinoulli-market-state.d.ts +30 -0
  85. package/dist/db/repositories/multinoulli-market-state.d.ts.map +1 -0
  86. package/dist/db/repositories/multinoulli-market-state.js +48 -0
  87. package/dist/db/repositories/multinoulli-market-state.js.map +1 -0
  88. package/dist/db/repositories/position.d.ts +2 -1
  89. package/dist/db/repositories/position.d.ts.map +1 -1
  90. package/dist/db/repositories/position.js +14 -3
  91. package/dist/db/repositories/position.js.map +1 -1
  92. package/dist/db/schema.d.ts.map +1 -1
  93. package/dist/db/schema.js +68 -1
  94. package/dist/db/schema.js.map +1 -1
  95. package/dist/etl/decoder.d.ts +4 -3
  96. package/dist/etl/decoder.d.ts.map +1 -1
  97. package/dist/etl/decoder.js +149 -31
  98. package/dist/etl/decoder.js.map +1 -1
  99. package/dist/etl/event-indexer.d.ts +3 -2
  100. package/dist/etl/event-indexer.d.ts.map +1 -1
  101. package/dist/etl/event-indexer.js +140 -37
  102. package/dist/etl/event-indexer.js.map +1 -1
  103. package/dist/etl/position-refresher.d.ts +4 -3
  104. package/dist/etl/position-refresher.d.ts.map +1 -1
  105. package/dist/etl/position-refresher.js +54 -8
  106. package/dist/etl/position-refresher.js.map +1 -1
  107. package/dist/etl/scheduler.d.ts +2 -2
  108. package/dist/etl/scheduler.d.ts.map +1 -1
  109. package/dist/etl/scheduler.js +19 -23
  110. package/dist/etl/scheduler.js.map +1 -1
  111. package/dist/etl/state-refresher.d.ts +4 -3
  112. package/dist/etl/state-refresher.d.ts.map +1 -1
  113. package/dist/etl/state-refresher.js +39 -9
  114. package/dist/etl/state-refresher.js.map +1 -1
  115. package/dist/index.d.ts +3 -0
  116. package/dist/index.d.ts.map +1 -1
  117. package/dist/index.js +129 -31
  118. package/dist/index.js.map +1 -1
  119. package/dist/layers/ChainReaderLive.d.ts.map +1 -1
  120. package/dist/layers/ChainReaderLive.js +96 -6
  121. package/dist/layers/ChainReaderLive.js.map +1 -1
  122. package/dist/layers/DatabaseLive.d.ts +1 -1
  123. package/dist/layers/DatabaseLive.d.ts.map +1 -1
  124. package/dist/layers/DatabaseLive.js +2 -1
  125. package/dist/layers/DatabaseLive.js.map +1 -1
  126. package/dist/layers/EventBusLive.d.ts.map +1 -1
  127. package/dist/layers/EventBusLive.js +2 -2
  128. package/dist/layers/EventBusLive.js.map +1 -1
  129. package/dist/layers/VoyagerClientLive.d.ts +3 -2
  130. package/dist/layers/VoyagerClientLive.d.ts.map +1 -1
  131. package/dist/layers/VoyagerClientLive.js +29 -10
  132. package/dist/layers/VoyagerClientLive.js.map +1 -1
  133. package/dist/layers/index.d.ts +2 -2
  134. package/dist/layers/index.d.ts.map +1 -1
  135. package/dist/layers/index.js +2 -2
  136. package/dist/layers/index.js.map +1 -1
  137. package/dist/services/ChainReader.d.ts +3 -0
  138. package/dist/services/ChainReader.d.ts.map +1 -1
  139. package/dist/services/ChainReader.js.map +1 -1
  140. package/dist/services/Database.d.ts +3 -2
  141. package/dist/services/Database.d.ts.map +1 -1
  142. package/dist/services/Database.js.map +1 -1
  143. package/dist/services/EventBus.d.ts.map +1 -1
  144. package/dist/services/EventBus.js.map +1 -1
  145. package/dist/services/VoyagerRateLimit.d.ts +40 -0
  146. package/dist/services/VoyagerRateLimit.d.ts.map +1 -0
  147. package/dist/services/VoyagerRateLimit.js +91 -0
  148. package/dist/services/VoyagerRateLimit.js.map +1 -0
  149. package/dist/services/index.d.ts +4 -4
  150. package/dist/services/index.d.ts.map +1 -1
  151. package/dist/services/index.js +2 -2
  152. package/dist/services/index.js.map +1 -1
  153. package/dist/types/api.d.ts +48 -1
  154. package/dist/types/api.d.ts.map +1 -1
  155. package/dist/types/event.d.ts +43 -2
  156. package/dist/types/event.d.ts.map +1 -1
  157. package/dist/types/index.d.ts +4 -4
  158. package/dist/types/index.d.ts.map +1 -1
  159. package/dist/types/market.d.ts +64 -0
  160. package/dist/types/market.d.ts.map +1 -1
  161. package/dist/types/position.d.ts +8 -0
  162. package/dist/types/position.d.ts.map +1 -1
  163. package/package.json +6 -6
@@ -2,16 +2,46 @@
2
2
  * Position refresher — reads trader positions from chain.
3
3
  */
4
4
  import { Effect } from 'effect';
5
- import { DatabaseTag } from '../services/Database';
6
5
  import { ChainReaderTag } from '../services/ChainReader';
6
+ import { DatabaseTag } from '../services/Database';
7
7
  import { EventBusTag } from '../services/EventBus';
8
- export const refreshPosition = (marketAddress, trader) => Effect.gen(function* () {
8
+ export const refreshPosition = (marketAddress, trader, marketType = 'normal') => Effect.gen(function* () {
9
9
  const db = yield* DatabaseTag;
10
10
  const chain = yield* ChainReaderTag;
11
11
  const bus = yield* EventBusTag;
12
+ if (marketType === 'multinoulli') {
13
+ const posResult = yield* Effect.either(chain.readMultinoulliPosition(marketAddress, trader));
14
+ if (posResult._tag === 'Left') {
15
+ return {
16
+ marketAddress,
17
+ trader,
18
+ success: false,
19
+ error: posResult.left.message,
20
+ };
21
+ }
22
+ const pos = posResult.right;
23
+ db.positions.upsert({
24
+ market_address: marketAddress,
25
+ trader,
26
+ has_position: pos.exists,
27
+ collateral_locked: pos.totalCollateralLocked,
28
+ settlement_state: pos.settlementState,
29
+ claimed: pos.claimed,
30
+ position_type: 'multinoulli',
31
+ outcome_count: pos.outcomeCount,
32
+ original_probs_json: JSON.stringify(pos.originalProbs),
33
+ collateral_locked_raw: pos.totalCollateralLocked,
34
+ });
35
+ bus.broadcast({
36
+ type: 'position_update',
37
+ marketAddress,
38
+ data: { trader, ...pos },
39
+ });
40
+ return { marketAddress, trader, success: true };
41
+ }
42
+ // Normal market
12
43
  const posResult = yield* Effect.either(chain.readPosition(marketAddress, trader));
13
44
  if (posResult._tag === 'Left') {
14
- console.error(`[PositionRefresher] Error for ${trader} in ${marketAddress}:`, posResult.left.message);
15
45
  return {
16
46
  marketAddress,
17
47
  trader,
@@ -31,6 +61,7 @@ export const refreshPosition = (marketAddress, trader) => Effect.gen(function* (
31
61
  settlement_state: pos.settlementState,
32
62
  delta_count: pos.deltaCount,
33
63
  claimed: pos.claimed,
64
+ position_type: 'normal',
34
65
  });
35
66
  bus.broadcast({
36
67
  type: 'position_update',
@@ -39,14 +70,29 @@ export const refreshPosition = (marketAddress, trader) => Effect.gen(function* (
39
70
  });
40
71
  return { marketAddress, trader, success: true };
41
72
  });
73
+ const RPC_CONCURRENCY = 5;
42
74
  export const refreshAllPositions = Effect.gen(function* () {
43
75
  const db = yield* DatabaseTag;
44
76
  const allPositions = db.positions.findAll();
45
- const results = [];
46
- for (const pos of allPositions) {
47
- const result = yield* refreshPosition(pos.market_address, pos.trader);
48
- results.push(result);
77
+ const markets = db.markets.findAll(true);
78
+ // Build market type lookup from active markets only
79
+ const marketTypeMap = new Map();
80
+ for (const m of markets) {
81
+ marketTypeMap.set(m.address, (m.market_type ?? 'normal'));
49
82
  }
50
- return results;
83
+ // Only refresh positions for markets that are still active in the indexer.
84
+ // Positions for deleted/inactive markets are skipped to avoid calling
85
+ // the wrong contract entrypoint (e.g. normal ABI on a multinoulli contract).
86
+ const activePositions = allPositions.filter((pos) => {
87
+ if (marketTypeMap.has(pos.market_address)) {
88
+ return true;
89
+ }
90
+ return false;
91
+ });
92
+ const effects = activePositions.map((pos) => {
93
+ const marketType = marketTypeMap.get(pos.market_address);
94
+ return refreshPosition(pos.market_address, pos.trader, marketType);
95
+ });
96
+ return yield* Effect.all(effects, { concurrency: RPC_CONCURRENCY });
51
97
  });
52
98
  //# sourceMappingURL=position-refresher.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"position-refresher.js","sourceRoot":"","sources":["../../src/etl/position-refresher.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGnD,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,aAAqB,EACrB,MAAc,EAKd,EAAE,CACF,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC;IAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC;IACpC,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC;IAE/B,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CACpC,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAC1C,CAAC;IAEF,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC9B,OAAO,CAAC,KAAK,CACX,iCAAiC,MAAM,OAAO,aAAa,GAAG,EAC9D,SAAS,CAAC,IAAI,CAAC,OAAO,CACvB,CAAC;QACF,OAAO;YACL,aAAa;YACb,MAAM;YACN,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,OAAO;SAC9B,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,CAAC;IAC5B,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC;QAClB,cAAc,EAAE,aAAa;QAC7B,MAAM;QACN,YAAY,EAAE,GAAG,CAAC,WAAW;QAC7B,iBAAiB,EAAE,GAAG,CAAC,gBAAgB;QACvC,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,gBAAgB,EAAE,GAAG,CAAC,eAA+F;QACrH,WAAW,EAAE,GAAG,CAAC,UAAU;QAC3B,OAAO,EAAE,GAAG,CAAC,OAAO;KACrB,CAAC,CAAC;IAEH,GAAG,CAAC,SAAS,CAAC;QACZ,IAAI,EAAE,iBAAiB;QACvB,aAAa;QACb,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,GAAG,EAAE;KACzB,CAAC,CAAC;IAEH,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAClD,CAAC,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACrD,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC;IAC9B,MAAM,YAAY,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;IAC5C,MAAM,OAAO,GAA4B,EAAE,CAAC;IAE5C,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;QACtE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACvB,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"position-refresher.js","sourceRoot":"","sources":["../../src/etl/position-refresher.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAInD,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,aAAqB,EACrB,MAAc,EACd,aAAyB,QAAQ,EACwD,EAAE,CAC3F,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC;IAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC;IACpC,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC;IAE/B,IAAI,UAAU,KAAK,aAAa,EAAE,CAAC;QACjC,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;QAE7F,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC9B,OAAO;gBACL,aAAa;gBACb,MAAM;gBACN,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,OAAO;aAC9B,CAAC;QACJ,CAAC;QAED,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,CAAC;QAC5B,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC;YAClB,cAAc,EAAE,aAAa;YAC7B,MAAM;YACN,YAAY,EAAE,GAAG,CAAC,MAAM;YACxB,iBAAiB,EAAE,GAAG,CAAC,qBAAqB;YAC5C,gBAAgB,EAAE,GAAG,CAAC,eAMT;YACb,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,aAAa,EAAE,aAAa;YAC5B,aAAa,EAAE,GAAG,CAAC,YAAY;YAC/B,mBAAmB,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,aAAa,CAAC;YACtD,qBAAqB,EAAE,GAAG,CAAC,qBAAqB;SACjD,CAAC,CAAC;QAEH,GAAG,CAAC,SAAS,CAAC;YACZ,IAAI,EAAE,iBAAiB;YACvB,aAAa;YACb,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,GAAG,EAAE;SACzB,CAAC,CAAC;QAEH,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAClD,CAAC;IAED,gBAAgB;IAChB,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;IAElF,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC9B,OAAO;YACL,aAAa;YACb,MAAM;YACN,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,OAAO;SAC9B,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,CAAC;IAC5B,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC;QAClB,cAAc,EAAE,aAAa;QAC7B,MAAM;QACN,YAAY,EAAE,GAAG,CAAC,WAAW;QAC7B,iBAAiB,EAAE,GAAG,CAAC,gBAAgB;QACvC,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,gBAAgB,EAAE,GAAG,CAAC,eAMT;QACb,WAAW,EAAE,GAAG,CAAC,UAAU;QAC3B,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,aAAa,EAAE,QAAQ;KACxB,CAAC,CAAC;IAEH,GAAG,CAAC,SAAS,CAAC;QACZ,IAAI,EAAE,iBAAiB;QACvB,aAAa;QACb,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,GAAG,EAAE;KACzB,CAAC,CAAC;IAEH,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAClD,CAAC,CAAC,CAAC;AAEL,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACrD,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC;IAC9B,MAAM,YAAY,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;IAC5C,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzC,oDAAoD;IACpD,MAAM,aAAa,GAAG,IAAI,GAAG,EAAsB,CAAC;IACpD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,WAAW,IAAI,QAAQ,CAAe,CAAC,CAAC;IAC1E,CAAC;IAED,2EAA2E;IAC3E,sEAAsE;IACtE,6EAA6E;IAC7E,MAAM,eAAe,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;QAClD,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;YAC1C,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QAC1C,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,CAAE,CAAC;QAC1D,OAAO,eAAe,CAAC,GAAG,CAAC,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC,CAAC;AACtE,CAAC,CAAC,CAAC"}
@@ -1,8 +1,8 @@
1
1
  import type { IndexerConfig } from '../config';
2
- import type { DatabaseService } from '../services/Database';
3
- import { type VoyagerClientService } from '../services/VoyagerClient';
4
2
  import { type ChainReaderService } from '../services/ChainReader';
3
+ import type { DatabaseService } from '../services/Database';
5
4
  import { type EventBusService } from '../services/EventBus';
5
+ import { type VoyagerClientService } from '../services/VoyagerClient';
6
6
  export interface SchedulerHandles {
7
7
  readonly stop: () => void;
8
8
  }
@@ -1 +1 @@
1
- {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/etl/scheduler.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,OAAO,EAAoB,KAAK,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACxF,OAAO,EAAkB,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAAe,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAKzE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC;CAC3B;AA8BD,wBAAgB,cAAc,CAC5B,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE;IACR,QAAQ,EAAE,eAAe,CAAC;IAC1B,aAAa,EAAE,oBAAoB,CAAC;IACpC,WAAW,EAAE,kBAAkB,CAAC;IAChC,QAAQ,EAAE,eAAe,CAAC;CAC3B,GACA,gBAAgB,CAmElB"}
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/etl/scheduler.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,KAAK,kBAAkB,EAAkB,MAAM,yBAAyB,CAAC;AAClF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,OAAO,EAAE,KAAK,eAAe,EAAe,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,KAAK,oBAAoB,EAAoB,MAAM,2BAA2B,CAAC;AAKxF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC;CAC3B;AAiCD,wBAAgB,cAAc,CAC5B,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE;IACR,QAAQ,EAAE,eAAe,CAAC;IAC1B,aAAa,EAAE,oBAAoB,CAAC;IACpC,WAAW,EAAE,kBAAkB,CAAC;IAChC,QAAQ,EAAE,eAAe,CAAC;CAC3B,GACA,gBAAgB,CA0DlB"}
@@ -5,13 +5,13 @@
5
5
  * the previous one finishes, preventing concurrent retry storms.
6
6
  */
7
7
  import { Effect, Layer } from 'effect';
8
- import { DatabaseTag } from '../services/Database';
9
- import { VoyagerClientTag } from '../services/VoyagerClient';
10
8
  import { ChainReaderTag } from '../services/ChainReader';
9
+ import { DatabaseTag } from '../services/Database';
11
10
  import { EventBusTag } from '../services/EventBus';
11
+ import { VoyagerClientTag } from '../services/VoyagerClient';
12
12
  import { indexAllMarkets } from './event-indexer';
13
- import { refreshAllMarketStates } from './state-refresher';
14
13
  import { refreshAllPositions } from './position-refresher';
14
+ import { refreshAllMarketStates } from './state-refresher';
15
15
  /**
16
16
  * Start a non-overlapping loop: run `fn`, wait `intervalMs`, repeat.
17
17
  * The next cycle only starts after the previous one completes.
@@ -21,19 +21,22 @@ function startNonOverlappingLoop(fn, intervalMs) {
21
21
  let stopped = false;
22
22
  let timer = null;
23
23
  async function loop() {
24
- if (stopped)
24
+ if (stopped) {
25
25
  return;
26
+ }
26
27
  await fn();
27
- if (stopped)
28
+ if (stopped) {
28
29
  return;
30
+ }
29
31
  timer = setTimeout(loop, intervalMs);
30
32
  }
31
33
  // Start the first cycle after the initial delay
32
34
  timer = setTimeout(loop, intervalMs);
33
35
  return () => {
34
36
  stopped = true;
35
- if (timer !== null)
37
+ if (timer !== null) {
36
38
  clearTimeout(timer);
39
+ }
37
40
  };
38
41
  }
39
42
  export function startScheduler(config, services) {
@@ -44,44 +47,38 @@ export function startScheduler(config, services) {
44
47
  const results = await run(indexAllMarkets);
45
48
  const total = results.reduce((sum, r) => sum + r.newEvents, 0);
46
49
  if (total > 0) {
47
- console.log(`[Scheduler] Indexed ${total} new events across ${results.length} markets`);
50
+ // Event refresh completed and detected new events.
48
51
  }
49
52
  }
50
- catch (err) {
51
- console.error('[Scheduler] Event indexer error:', err);
53
+ catch (_err) {
54
+ // Keep scheduler loop alive; downstream monitoring handles surfaced failures.
52
55
  }
53
56
  }, config.eventPollIntervalMs);
54
57
  const cancelState = startNonOverlappingLoop(async () => {
55
58
  try {
56
59
  await run(refreshAllMarketStates);
57
60
  }
58
- catch (err) {
59
- console.error('[Scheduler] State refresher error:', err);
61
+ catch (_err) {
62
+ // Keep scheduler loop alive; downstream monitoring handles surfaced failures.
60
63
  }
61
64
  }, config.statePollIntervalMs);
62
65
  const cancelPositions = startNonOverlappingLoop(async () => {
63
66
  try {
64
67
  await run(refreshAllPositions);
65
68
  }
66
- catch (err) {
67
- console.error('[Scheduler] Position refresher error:', err);
69
+ catch (_err) {
70
+ // Keep scheduler loop alive; downstream monitoring handles surfaced failures.
68
71
  }
69
72
  }, config.positionPollIntervalMs);
70
73
  // Run immediately on start
71
74
  void (async () => {
72
75
  try {
73
- console.log('[Scheduler] Initial event indexing...');
74
- const results = await run(indexAllMarkets);
75
- const total = results.reduce((sum, r) => sum + r.newEvents, 0);
76
- console.log(`[Scheduler] Initial: ${total} events across ${results.length} markets`);
77
- console.log('[Scheduler] Initial state refresh...');
76
+ await run(indexAllMarkets);
78
77
  await run(refreshAllMarketStates);
79
- console.log('[Scheduler] Initial position refresh...');
80
78
  await run(refreshAllPositions);
81
- console.log('[Scheduler] Initialization complete');
82
79
  }
83
- catch (err) {
84
- console.error('[Scheduler] Initialization error:', err);
80
+ catch (_err) {
81
+ // Keep scheduler boot sequence non-fatal; loops continue independently.
85
82
  }
86
83
  })();
87
84
  return {
@@ -89,7 +86,6 @@ export function startScheduler(config, services) {
89
86
  cancelEvents();
90
87
  cancelState();
91
88
  cancelPositions();
92
- console.log('[Scheduler] Stopped');
93
89
  },
94
90
  };
95
91
  }
@@ -1 +1 @@
1
- {"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/etl/scheduler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAGvC,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAA6B,MAAM,2BAA2B,CAAC;AACxF,OAAO,EAAE,cAAc,EAA2B,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAAE,WAAW,EAAwB,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAM3D;;;;GAIG;AACH,SAAS,uBAAuB,CAC9B,EAAuB,EACvB,UAAkB;IAElB,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,KAAK,GAAyC,IAAI,CAAC;IAEvD,KAAK,UAAU,IAAI;QACjB,IAAI,OAAO;YAAE,OAAO;QACpB,MAAM,EAAE,EAAE,CAAC;QACX,IAAI,OAAO;YAAE,OAAO;QACpB,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACvC,CAAC;IAED,gDAAgD;IAChD,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAErC,OAAO,GAAG,EAAE;QACV,OAAO,GAAG,IAAI,CAAC;QACf,IAAI,KAAK,KAAK,IAAI;YAAE,YAAY,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,MAAqB,EACrB,QAKC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAC1B,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAC7D,KAAK,CAAC,OAAO,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,EAC5E,KAAK,CAAC,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,EACtE,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAC9D,CAAC;IAEF,MAAM,GAAG,GAAG,CAAI,MAA8F,EAAE,EAAE,CAChH,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;IAEnD,MAAM,YAAY,GAAG,uBAAuB,CAAC,KAAK,IAAI,EAAE;QACtD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,eAAe,CAAC,CAAC;YAC3C,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;YAC/D,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACd,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,sBAAsB,OAAO,CAAC,MAAM,UAAU,CAAC,CAAC;YAC1F,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,GAAG,CAAC,CAAC;QACzD,CAAC;IACH,CAAC,EAAE,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAE/B,MAAM,WAAW,GAAG,uBAAuB,CAAC,KAAK,IAAI,EAAE;QACrD,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,sBAAsB,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,oCAAoC,EAAE,GAAG,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC,EAAE,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAE/B,MAAM,eAAe,GAAG,uBAAuB,CAAC,KAAK,IAAI,EAAE;QACzD,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,mBAAmB,CAAC,CAAC;QACjC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,GAAG,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC,EAAE,MAAM,CAAC,sBAAsB,CAAC,CAAC;IAElC,2BAA2B;IAC3B,KAAK,CAAC,KAAK,IAAI,EAAE;QACf,IAAI,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;YACrD,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,eAAe,CAAC,CAAC;YAC3C,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;YAC/D,OAAO,CAAC,GAAG,CAAC,wBAAwB,KAAK,kBAAkB,OAAO,CAAC,MAAM,UAAU,CAAC,CAAC;YAErF,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;YACpD,MAAM,GAAG,CAAC,sBAAsB,CAAC,CAAC;YAElC,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC;YACvD,MAAM,GAAG,CAAC,mBAAmB,CAAC,CAAC;YAE/B,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;QACrD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,GAAG,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,CAAC,EAAE,CAAC;IAEL,OAAO;QACL,IAAI,EAAE,GAAG,EAAE;YACT,YAAY,EAAE,CAAC;YACf,WAAW,EAAE,CAAC;YACd,eAAe,EAAE,CAAC;YAClB,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QACrC,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/etl/scheduler.ts"],"names":[],"mappings":"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"}
@@ -2,10 +2,11 @@
2
2
  * State refresher — periodic RPC reads for market state.
3
3
  */
4
4
  import { Effect } from 'effect';
5
- import { DatabaseTag } from '../services/Database';
6
5
  import { ChainReaderTag } from '../services/ChainReader';
6
+ import { DatabaseTag } from '../services/Database';
7
7
  import { EventBusTag } from '../services/EventBus';
8
+ import type { MarketType } from '../types';
8
9
  import type { StateRefreshResult } from './types';
9
- export declare const refreshMarketState: (marketAddress: string) => Effect.Effect<StateRefreshResult, never, DatabaseTag | ChainReaderTag | EventBusTag>;
10
- export declare const refreshAllMarketStates: Effect.Effect<StateRefreshResult[], never, DatabaseTag | ChainReaderTag | EventBusTag>;
10
+ export declare const refreshMarketState: (marketAddress: string, marketType?: MarketType) => Effect.Effect<StateRefreshResult, never, DatabaseTag | ChainReaderTag | EventBusTag>;
11
+ export declare const refreshAllMarketStates: Effect.Effect<StateRefreshResult[], never, DatabaseTag | EventBusTag | ChainReaderTag>;
11
12
  //# sourceMappingURL=state-refresher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"state-refresher.d.ts","sourceRoot":"","sources":["../../src/etl/state-refresher.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD,eAAO,MAAM,kBAAkB,GAC7B,eAAe,MAAM,KACpB,MAAM,CAAC,MAAM,CACd,kBAAkB,EAClB,KAAK,EACL,WAAW,GAAG,cAAc,GAAG,WAAW,CA2CxC,CAAC;AAEL,eAAO,MAAM,sBAAsB,wFAWjC,CAAC"}
1
+ {"version":3,"file":"state-refresher.d.ts","sourceRoot":"","sources":["../../src/etl/state-refresher.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD,eAAO,MAAM,kBAAkB,GAC7B,eAAe,MAAM,EACrB,aAAY,UAAqB,KAChC,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE,KAAK,EAAE,WAAW,GAAG,cAAc,GAAG,WAAW,CAwElF,CAAC;AAIL,eAAO,MAAM,sBAAsB,wFAUjC,CAAC"}
@@ -2,16 +2,46 @@
2
2
  * State refresher — periodic RPC reads for market state.
3
3
  */
4
4
  import { Effect } from 'effect';
5
- import { DatabaseTag } from '../services/Database';
6
5
  import { ChainReaderTag } from '../services/ChainReader';
6
+ import { DatabaseTag } from '../services/Database';
7
7
  import { EventBusTag } from '../services/EventBus';
8
- export const refreshMarketState = (marketAddress) => Effect.gen(function* () {
8
+ export const refreshMarketState = (marketAddress, marketType = 'normal') => Effect.gen(function* () {
9
9
  const db = yield* DatabaseTag;
10
10
  const chain = yield* ChainReaderTag;
11
11
  const bus = yield* EventBusTag;
12
+ if (marketType === 'multinoulli') {
13
+ const stateResult = yield* Effect.either(chain.readMultinoulliMarketState(marketAddress));
14
+ if (stateResult._tag === 'Left') {
15
+ return { marketAddress, success: false, error: stateResult.left.message };
16
+ }
17
+ const state = stateResult.right;
18
+ db.multinoulliMarketState.upsert({
19
+ market_address: marketAddress,
20
+ outcome_count: state.outcomeCount,
21
+ probs_json: JSON.stringify(state.probs),
22
+ k: state.k,
23
+ effective_k: state.effectiveK,
24
+ total_shares: state.totalShares,
25
+ total_backing: state.totalBacking,
26
+ is_initialized: state.isInitialized,
27
+ is_paused: state.isPaused,
28
+ is_settled: state.isSettled,
29
+ settlement_outcomes_json: JSON.stringify(state.settlementOutcomes),
30
+ matrix_enabled: state.matrixEnabled,
31
+ matrix_rows: state.matrixRows,
32
+ matrix_cols: state.matrixCols,
33
+ matrix_enforce_full_row: state.matrixEnforceFullRow,
34
+ });
35
+ bus.broadcast({
36
+ type: 'state_update',
37
+ marketAddress,
38
+ data: state,
39
+ });
40
+ return { marketAddress, success: true };
41
+ }
42
+ // Normal market
12
43
  const stateResult = yield* Effect.either(chain.readMarketState(marketAddress));
13
44
  if (stateResult._tag === 'Left') {
14
- console.error(`[StateRefresher] Error for ${marketAddress}:`, stateResult.left.message);
15
45
  return { marketAddress, success: false, error: stateResult.left.message };
16
46
  }
17
47
  const state = stateResult.right;
@@ -37,14 +67,14 @@ export const refreshMarketState = (marketAddress) => Effect.gen(function* () {
37
67
  });
38
68
  return { marketAddress, success: true };
39
69
  });
70
+ const RPC_CONCURRENCY = 5;
40
71
  export const refreshAllMarketStates = Effect.gen(function* () {
41
72
  const db = yield* DatabaseTag;
42
73
  const markets = db.markets.findAll(true);
43
- const results = [];
44
- for (const market of markets) {
45
- const result = yield* refreshMarketState(market.address);
46
- results.push(result);
47
- }
48
- return results;
74
+ const effects = markets.map((market) => {
75
+ const marketType = (market.market_type ?? 'normal');
76
+ return refreshMarketState(market.address, marketType);
77
+ });
78
+ return yield* Effect.all(effects, { concurrency: RPC_CONCURRENCY });
49
79
  });
50
80
  //# sourceMappingURL=state-refresher.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"state-refresher.js","sourceRoot":"","sources":["../../src/etl/state-refresher.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGnD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,aAAqB,EAKrB,EAAE,CACF,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC;IAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC;IACpC,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC;IAE/B,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CACtC,KAAK,CAAC,eAAe,CAAC,aAAa,CAAC,CACrC,CAAC;IAEF,IAAI,WAAW,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAChC,OAAO,CAAC,KAAK,CACX,8BAA8B,aAAa,GAAG,EAC9C,WAAW,CAAC,IAAI,CAAC,OAAO,CACzB,CAAC;QACF,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;IAC5E,CAAC;IAED,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC;IAChC,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC;QACpB,cAAc,EAAE,aAAa;QAC7B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,cAAc,EAAE,KAAK,CAAC,aAAa;QACnC,SAAS,EAAE,KAAK,CAAC,QAAQ;QACzB,UAAU,EAAE,KAAK,CAAC,SAAS;QAC3B,gBAAgB,EAAE,KAAK,CAAC,eAAe;QACvC,YAAY,EAAE,KAAK,CAAC,WAAW;QAC/B,aAAa,EAAE,KAAK,CAAC,YAAY;QACjC,QAAQ,EAAE,KAAK,CAAC,OAAO;QACvB,SAAS,EAAE,KAAK,CAAC,QAAQ;QACzB,YAAY,EAAE,KAAK,CAAC,WAAW;KAChC,CAAC,CAAC;IAEH,GAAG,CAAC,SAAS,CAAC;QACZ,IAAI,EAAE,cAAc;QACpB,aAAa;QACb,IAAI,EAAE,KAAK;KACZ,CAAC,CAAC;IAEH,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC1C,CAAC,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACxD,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC;IAC9B,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,MAAM,OAAO,GAAyB,EAAE,CAAC;IAEzC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACzD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACvB,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"state-refresher.js","sourceRoot":"","sources":["../../src/etl/state-refresher.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,5 @@
1
+ /**
2
+ * Indexer entry point — boots DB, ETL scheduler, and Elysia server.
3
+ */
1
4
  export {};
2
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG"}
package/dist/index.js CHANGED
@@ -1,20 +1,20 @@
1
1
  /**
2
2
  * Indexer entry point — boots DB, ETL scheduler, and Elysia server.
3
3
  */
4
+ import { AMM_ABI, MULTINOULLI_AMM_ABI } from '@the-situation/artifacts';
5
+ import { SQ128x128 } from '@the-situation/core';
6
+ import { AMMContract, createProvider, MultinoulliAMMContract } from '@the-situation/starknet';
7
+ import { Duration, Effect, Schedule } from 'effect';
8
+ import { createApp } from './api/app';
4
9
  import { loadConfig } from './config';
5
10
  import { createDatabase } from './db/connection';
11
+ import { CursorRepository, EventRepository, MarketRepository, MarketStateRepository, MultinoulliMarketStateRepository, PositionRepository, RankingRepository, } from './db/repositories';
6
12
  import { initializeSchema } from './db/schema';
7
- import { MarketRepository, EventRepository, MarketStateRepository, PositionRepository, CursorRepository, RankingRepository, } from './db/repositories';
8
- import { VoyagerApiError, isRetryableVoyagerError } from './services/VoyagerClient';
9
- import { ChainReadError } from './services/ChainReader';
10
13
  import { startScheduler } from './etl/scheduler';
11
- import { createApp } from './api/app';
12
- import { Duration, Effect, Schedule } from 'effect';
13
- import { AMM_ABI } from '@the-situation/artifacts';
14
- import { AMMContract, createProvider } from '@the-situation/starknet';
14
+ import { ChainReadError } from './services/ChainReader';
15
+ import { isRetryableVoyagerError, VoyagerApiError } from './services/VoyagerClient';
16
+ import { VoyagerRateLimitState } from './services/VoyagerRateLimit';
15
17
  const config = loadConfig();
16
- // Database
17
- console.log(`[Indexer] Opening database at ${config.dbPath}`);
18
18
  const db = createDatabase(config.dbPath);
19
19
  initializeSchema(db);
20
20
  const databaseService = {
@@ -22,19 +22,25 @@ const databaseService = {
22
22
  markets: new MarketRepository(db),
23
23
  events: new EventRepository(db),
24
24
  marketState: new MarketStateRepository(db),
25
+ multinoulliMarketState: new MultinoulliMarketStateRepository(db),
25
26
  positions: new PositionRepository(db),
26
27
  cursors: new CursorRepository(db),
27
28
  rankings: new RankingRepository(db),
28
29
  };
29
- // Voyager client (with exponential backoff for 429/503)
30
+ // ── Voyager rate-limit state (shared, observable) ───────────────────────────
31
+ const voyagerRateLimit = new VoyagerRateLimitState(config.voyagerMinRequestIntervalMs);
32
+ // ── Voyager client ──────────────────────────────────────────────────────────
30
33
  const VOYAGER_BASE_URL = 'https://sepolia-api.voyager.online/beta';
31
- const VOYAGER_MAX_RETRIES = 3;
32
- const VOYAGER_BASE_DELAY_MS = 2000;
33
- const voyagerRetrySchedule = Schedule.compose(Schedule.jittered(Schedule.exponential(Duration.millis(VOYAGER_BASE_DELAY_MS))), Schedule.recurs(VOYAGER_MAX_RETRIES));
34
+ const voyagerRetrySchedule = Schedule.compose(Schedule.jittered(Schedule.exponential(Duration.millis(config.voyagerBaseDelayMs))), Schedule.recurs(config.voyagerMaxRetries));
34
35
  const voyagerClientService = {
35
36
  fetchEvents: (contractAddress, page, pageSize) => {
36
37
  const fetchOnce = Effect.tryPromise({
37
38
  try: async () => {
39
+ if (voyagerRateLimit.isLimited()) {
40
+ const status = voyagerRateLimit.getStatus();
41
+ throw new VoyagerApiError(`Rate limited — ${status.message}`, 429);
42
+ }
43
+ await voyagerRateLimit.throttle();
38
44
  const url = new URL(`${VOYAGER_BASE_URL}/events`);
39
45
  url.searchParams.set('contract', contractAddress);
40
46
  url.searchParams.set('p', page.toString());
@@ -50,37 +56,60 @@ const voyagerClientService = {
50
56
  const retryAfterMs = retryAfter && Number.isFinite(Number(retryAfter)) && Number(retryAfter) > 0
51
57
  ? Number(retryAfter) * 1000
52
58
  : undefined;
59
+ if (response.status === 429 || response.status === 503) {
60
+ voyagerRateLimit.recordRateLimit();
61
+ }
53
62
  throw new VoyagerApiError(`Voyager API ${response.status}: ${response.statusText}`, response.status, retryAfterMs);
54
63
  }
64
+ voyagerRateLimit.recordSuccess();
55
65
  const data = (await response.json());
56
66
  return { events: data.items, lastPage: data.lastPage };
57
67
  },
58
68
  catch: (e) => {
59
- if (e instanceof VoyagerApiError)
69
+ if (e instanceof VoyagerApiError) {
60
70
  return e;
71
+ }
61
72
  return new VoyagerApiError(e instanceof Error ? e.message : 'Unknown Voyager API error');
62
73
  },
63
74
  });
64
75
  return fetchOnce.pipe(Effect.retry({
65
76
  schedule: voyagerRetrySchedule,
66
77
  while: (error) => {
78
+ if (voyagerRateLimit.isLimited()) {
79
+ return false;
80
+ }
67
81
  const shouldRetry = isRetryableVoyagerError(error);
68
82
  if (shouldRetry) {
69
- console.warn(`[VoyagerClient] Retrying after ${error.status} for contract=${contractAddress} page=${page}`);
83
+ // Retry continues according to configured schedule.
70
84
  }
71
85
  return shouldRetry;
72
86
  },
73
87
  }));
74
88
  },
75
89
  };
76
- // Chain reader
90
+ // ── Chain reader ────────────────────────────────────────────────────────────
77
91
  const provider = createProvider({
78
92
  name: 'custom',
79
93
  chainId: 'SN_SEPOLIA',
80
94
  rpcUrl: config.starknetRpcUrl,
81
95
  });
96
+ const ammCache = new Map();
97
+ const multinoulliAmmCache = new Map();
82
98
  function getAmm(address) {
83
- return new AMMContract({ address, abi: AMM_ABI, provider });
99
+ let amm = ammCache.get(address);
100
+ if (!amm) {
101
+ amm = new AMMContract({ address, abi: AMM_ABI, provider });
102
+ ammCache.set(address, amm);
103
+ }
104
+ return amm;
105
+ }
106
+ function getMultinoulliAmm(address) {
107
+ let amm = multinoulliAmmCache.get(address);
108
+ if (!amm) {
109
+ amm = new MultinoulliAMMContract({ address, abi: MULTINOULLI_AMM_ABI, provider });
110
+ multinoulliAmmCache.set(address, amm);
111
+ }
112
+ return amm;
84
113
  }
85
114
  const chainReaderService = {
86
115
  readMarketState: (marketAddress) => Effect.tryPromise({
@@ -130,7 +159,7 @@ const chainReaderService = {
130
159
  amm.getPositionCompact(trader),
131
160
  amm.getPositionSummary(trader),
132
161
  ]);
133
- if (!position || !extended) {
162
+ if (!(position && extended)) {
134
163
  return {
135
164
  hasPosition: false,
136
165
  collateralLocked: null,
@@ -148,15 +177,89 @@ const chainReaderService = {
148
177
  mean: position.originalMean?.toNumber() ?? null,
149
178
  sigma: position.originalSigma?.toNumber() ?? null,
150
179
  variance: position.originalVariance?.toNumber() ?? null,
151
- settlementState: extended.claimed ? 'claimed' : extended.tracksSettlementClaim ? 'pending_claim' : 'active',
180
+ settlementState: extended.claimed
181
+ ? 'claimed'
182
+ : extended.tracksSettlementClaim
183
+ ? 'pending_claim'
184
+ : 'active',
152
185
  deltaCount: 0,
153
186
  claimed: extended.claimed,
154
187
  };
155
188
  },
156
189
  catch: (e) => new ChainReadError(`Failed to read position: ${e instanceof Error ? e.message : 'Unknown'}`, e),
157
190
  }),
191
+ readMultinoulliMarketState: (marketAddress) => Effect.tryPromise({
192
+ try: async () => {
193
+ const amm = getMultinoulliAmm(marketAddress);
194
+ const [distribution, status, params, lpInfo, settlements, matrix] = await Promise.all([
195
+ amm.getDistribution(),
196
+ amm.getMarketStatus(),
197
+ amm.getParams(),
198
+ amm.getLPInfo(),
199
+ amm.getSettlementOutcomes(),
200
+ amm.getMatrixConstraints(),
201
+ ]);
202
+ const probs = distribution.probs.map((raw) => {
203
+ const parsed = SQ128x128.fromRaw(raw);
204
+ return parsed?.toNumber() ?? 0;
205
+ });
206
+ return {
207
+ outcomeCount: probs.length,
208
+ probs,
209
+ k: params?.k.toNumber() ?? null,
210
+ effectiveK: params?.backing.toNumber() ?? null,
211
+ totalShares: lpInfo?.totalShares.toRaw().limb0.toString() ?? null,
212
+ totalBacking: lpInfo?.totalBackingDeposited.toRaw().limb0.toString() ?? null,
213
+ isInitialized: status.isInitialized,
214
+ isPaused: status.isPaused,
215
+ isSettled: status.isSettled,
216
+ settlementOutcomes: settlements,
217
+ matrixEnabled: matrix.enabled,
218
+ matrixRows: matrix.rowCount,
219
+ matrixCols: matrix.colCount,
220
+ matrixEnforceFullRow: matrix.enforceFullRowAssignmentSettlement,
221
+ };
222
+ },
223
+ catch: (e) => new ChainReadError(`Failed to read multinoulli market state: ${e instanceof Error ? e.message : 'Unknown'}`, e),
224
+ }),
225
+ readMultinoulliPosition: (marketAddress, trader) => Effect.tryPromise({
226
+ try: async () => {
227
+ const amm = getMultinoulliAmm(marketAddress);
228
+ const [summary, compact] = await Promise.all([
229
+ amm.getPositionSummary(trader),
230
+ amm.getPositionCompact(trader),
231
+ ]);
232
+ if (!(summary && compact)) {
233
+ return {
234
+ exists: false,
235
+ claimed: false,
236
+ totalCollateralLocked: null,
237
+ outcomeCount: 0,
238
+ originalProbs: [],
239
+ settlementState: 'never_traded',
240
+ };
241
+ }
242
+ const originalProbs = compact.originalDistribution.probs.map((raw) => {
243
+ const parsed = SQ128x128.fromRaw(raw);
244
+ return parsed?.toNumber() ?? 0;
245
+ });
246
+ return {
247
+ exists: summary.exists,
248
+ claimed: summary.claimed,
249
+ totalCollateralLocked: summary.totalCollateralLocked.toRaw().limb0.toString(),
250
+ outcomeCount: summary.outcomeCount,
251
+ originalProbs,
252
+ settlementState: summary.claimed
253
+ ? 'claimed'
254
+ : summary.tracksSettlementClaim
255
+ ? 'pending_claim'
256
+ : 'active',
257
+ };
258
+ },
259
+ catch: (e) => new ChainReadError(`Failed to read multinoulli position: ${e instanceof Error ? e.message : 'Unknown'}`, e),
260
+ }),
158
261
  };
159
- // Event bus
262
+ // ── Event bus ───────────────────────────────────────────────────────────────
160
263
  const busHandlers = new Set();
161
264
  const eventBusService = {
162
265
  broadcast: (event) => {
@@ -164,8 +267,8 @@ const eventBusService = {
164
267
  try {
165
268
  handler(event);
166
269
  }
167
- catch (err) {
168
- console.error('[EventBus] handler error:', err);
270
+ catch (_err) {
271
+ busHandlers.delete(handler);
169
272
  }
170
273
  }
171
274
  },
@@ -176,33 +279,28 @@ const eventBusService = {
176
279
  };
177
280
  },
178
281
  };
179
- // Start ETL scheduler
282
+ // ── Start ETL scheduler ────────────────────────────────────────────────────
180
283
  const scheduler = startScheduler(config, {
181
284
  database: databaseService,
182
285
  voyagerClient: voyagerClientService,
183
286
  chainReader: chainReaderService,
184
287
  eventBus: eventBusService,
185
288
  });
186
- // Start Elysia server
289
+ // ── Start Elysia server ────────────────────────────────────────────────────
187
290
  const app = createApp({
188
291
  db: databaseService,
189
292
  eventBus: eventBusService,
190
293
  adminApiKey: config.adminApiKey,
294
+ rateLimit: voyagerRateLimit,
191
295
  });
192
296
  app.listen({ port: config.port, hostname: '0.0.0.0' });
193
- console.log(`[Indexer] Server running at http://localhost:${config.port}`);
194
- console.log(`[Indexer] Event poll: ${config.eventPollIntervalMs}ms`);
195
- console.log(`[Indexer] State poll: ${config.statePollIntervalMs}ms`);
196
- console.log(`[Indexer] Position poll: ${config.positionPollIntervalMs}ms`);
197
- // Graceful shutdown
297
+ // ── Graceful shutdown ──────────────────────────────────────────────────────
198
298
  process.on('SIGTERM', () => {
199
- console.log('[Indexer] SIGTERM received, shutting down...');
200
299
  scheduler.stop();
201
300
  db.close();
202
301
  process.exit(0);
203
302
  });
204
303
  process.on('SIGINT', () => {
205
- console.log('[Indexer] SIGINT received, shutting down...');
206
304
  scheduler.stop();
207
305
  db.close();
208
306
  process.exit(0);