sorokeep 1.0.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 (275) hide show
  1. package/CONTRIBUTING.md +296 -0
  2. package/LICENSE +21 -0
  3. package/README.md +677 -0
  4. package/dist/alerts/alerts.d.ts +21 -0
  5. package/dist/alerts/alerts.d.ts.map +1 -0
  6. package/dist/alerts/alerts.js +18 -0
  7. package/dist/alerts/alerts.js.map +1 -0
  8. package/dist/alerts/discord.d.ts +3 -0
  9. package/dist/alerts/discord.d.ts.map +1 -0
  10. package/dist/alerts/discord.js +199 -0
  11. package/dist/alerts/discord.js.map +1 -0
  12. package/dist/alerts/dispatcher.d.ts +13 -0
  13. package/dist/alerts/dispatcher.d.ts.map +1 -0
  14. package/dist/alerts/dispatcher.js +91 -0
  15. package/dist/alerts/dispatcher.js.map +1 -0
  16. package/dist/alerts/keys.d.ts +24 -0
  17. package/dist/alerts/keys.d.ts.map +1 -0
  18. package/dist/alerts/keys.js +44 -0
  19. package/dist/alerts/keys.js.map +1 -0
  20. package/dist/alerts/pagerduty.d.ts +8 -0
  21. package/dist/alerts/pagerduty.d.ts.map +1 -0
  22. package/dist/alerts/pagerduty.js +164 -0
  23. package/dist/alerts/pagerduty.js.map +1 -0
  24. package/dist/alerts/resource.d.ts +23 -0
  25. package/dist/alerts/resource.d.ts.map +1 -0
  26. package/dist/alerts/resource.js +167 -0
  27. package/dist/alerts/resource.js.map +1 -0
  28. package/dist/alerts/slack.d.ts +7 -0
  29. package/dist/alerts/slack.d.ts.map +1 -0
  30. package/dist/alerts/slack.js +184 -0
  31. package/dist/alerts/slack.js.map +1 -0
  32. package/dist/alerts/telegram.d.ts +9 -0
  33. package/dist/alerts/telegram.d.ts.map +1 -0
  34. package/dist/alerts/telegram.js +133 -0
  35. package/dist/alerts/telegram.js.map +1 -0
  36. package/dist/alerts/templates.d.ts +109 -0
  37. package/dist/alerts/templates.d.ts.map +1 -0
  38. package/dist/alerts/templates.js +139 -0
  39. package/dist/alerts/templates.js.map +1 -0
  40. package/dist/alerts/types.d.ts +113 -0
  41. package/dist/alerts/types.d.ts.map +1 -0
  42. package/dist/alerts/types.js +87 -0
  43. package/dist/alerts/types.js.map +1 -0
  44. package/dist/alerts/webhook.d.ts +13 -0
  45. package/dist/alerts/webhook.d.ts.map +1 -0
  46. package/dist/alerts/webhook.js +56 -0
  47. package/dist/alerts/webhook.js.map +1 -0
  48. package/dist/cli/schemaFormatter.d.ts +11 -0
  49. package/dist/cli/schemaFormatter.d.ts.map +1 -0
  50. package/dist/cli/schemaFormatter.js +74 -0
  51. package/dist/cli/schemaFormatter.js.map +1 -0
  52. package/dist/commands/alerts.d.ts +3 -0
  53. package/dist/commands/alerts.d.ts.map +1 -0
  54. package/dist/commands/alerts.js +256 -0
  55. package/dist/commands/alerts.js.map +1 -0
  56. package/dist/commands/budget.d.ts +3 -0
  57. package/dist/commands/budget.d.ts.map +1 -0
  58. package/dist/commands/budget.js +47 -0
  59. package/dist/commands/budget.js.map +1 -0
  60. package/dist/commands/channels.d.ts +3 -0
  61. package/dist/commands/channels.d.ts.map +1 -0
  62. package/dist/commands/channels.js +84 -0
  63. package/dist/commands/channels.js.map +1 -0
  64. package/dist/commands/check.d.ts +3 -0
  65. package/dist/commands/check.d.ts.map +1 -0
  66. package/dist/commands/check.js +58 -0
  67. package/dist/commands/check.js.map +1 -0
  68. package/dist/commands/completion.d.ts +3 -0
  69. package/dist/commands/completion.d.ts.map +1 -0
  70. package/dist/commands/completion.js +36 -0
  71. package/dist/commands/completion.js.map +1 -0
  72. package/dist/commands/costs.d.ts +15 -0
  73. package/dist/commands/costs.d.ts.map +1 -0
  74. package/dist/commands/costs.js +280 -0
  75. package/dist/commands/costs.js.map +1 -0
  76. package/dist/commands/daemon.d.ts +3 -0
  77. package/dist/commands/daemon.d.ts.map +1 -0
  78. package/dist/commands/daemon.js +93 -0
  79. package/dist/commands/daemon.js.map +1 -0
  80. package/dist/commands/db.d.ts +3 -0
  81. package/dist/commands/db.d.ts.map +1 -0
  82. package/dist/commands/db.js +140 -0
  83. package/dist/commands/db.js.map +1 -0
  84. package/dist/commands/guard.d.ts +3 -0
  85. package/dist/commands/guard.d.ts.map +1 -0
  86. package/dist/commands/guard.js +181 -0
  87. package/dist/commands/guard.js.map +1 -0
  88. package/dist/commands/history.d.ts +3 -0
  89. package/dist/commands/history.d.ts.map +1 -0
  90. package/dist/commands/history.js +57 -0
  91. package/dist/commands/history.js.map +1 -0
  92. package/dist/commands/inspect.d.ts +3 -0
  93. package/dist/commands/inspect.d.ts.map +1 -0
  94. package/dist/commands/inspect.js +82 -0
  95. package/dist/commands/inspect.js.map +1 -0
  96. package/dist/commands/mcp.d.ts +3 -0
  97. package/dist/commands/mcp.d.ts.map +1 -0
  98. package/dist/commands/mcp.js +20 -0
  99. package/dist/commands/mcp.js.map +1 -0
  100. package/dist/commands/pause.d.ts +3 -0
  101. package/dist/commands/pause.d.ts.map +1 -0
  102. package/dist/commands/pause.js +32 -0
  103. package/dist/commands/pause.js.map +1 -0
  104. package/dist/commands/resources.d.ts +3 -0
  105. package/dist/commands/resources.d.ts.map +1 -0
  106. package/dist/commands/resources.js +95 -0
  107. package/dist/commands/resources.js.map +1 -0
  108. package/dist/commands/restore.d.ts +3 -0
  109. package/dist/commands/restore.d.ts.map +1 -0
  110. package/dist/commands/restore.js +92 -0
  111. package/dist/commands/restore.js.map +1 -0
  112. package/dist/commands/resume.d.ts +3 -0
  113. package/dist/commands/resume.d.ts.map +1 -0
  114. package/dist/commands/resume.js +31 -0
  115. package/dist/commands/resume.js.map +1 -0
  116. package/dist/commands/status.d.ts +3 -0
  117. package/dist/commands/status.d.ts.map +1 -0
  118. package/dist/commands/status.js +63 -0
  119. package/dist/commands/status.js.map +1 -0
  120. package/dist/commands/watch.d.ts +3 -0
  121. package/dist/commands/watch.d.ts.map +1 -0
  122. package/dist/commands/watch.js +163 -0
  123. package/dist/commands/watch.js.map +1 -0
  124. package/dist/core/aws_secrets.d.ts +13 -0
  125. package/dist/core/aws_secrets.d.ts.map +1 -0
  126. package/dist/core/aws_secrets.js +34 -0
  127. package/dist/core/aws_secrets.js.map +1 -0
  128. package/dist/core/budget.d.ts +10 -0
  129. package/dist/core/budget.d.ts.map +1 -0
  130. package/dist/core/budget.js +31 -0
  131. package/dist/core/budget.js.map +1 -0
  132. package/dist/core/channels.d.ts +67 -0
  133. package/dist/core/channels.d.ts.map +1 -0
  134. package/dist/core/channels.js +136 -0
  135. package/dist/core/channels.js.map +1 -0
  136. package/dist/core/check.d.ts +25 -0
  137. package/dist/core/check.d.ts.map +1 -0
  138. package/dist/core/check.js +85 -0
  139. package/dist/core/check.js.map +1 -0
  140. package/dist/core/completion.d.ts +9 -0
  141. package/dist/core/completion.d.ts.map +1 -0
  142. package/dist/core/completion.js +68 -0
  143. package/dist/core/completion.js.map +1 -0
  144. package/dist/core/costs.d.ts +90 -0
  145. package/dist/core/costs.d.ts.map +1 -0
  146. package/dist/core/costs.js +147 -0
  147. package/dist/core/costs.js.map +1 -0
  148. package/dist/core/decoder.d.ts +7 -0
  149. package/dist/core/decoder.d.ts.map +1 -0
  150. package/dist/core/decoder.js +38 -0
  151. package/dist/core/decoder.js.map +1 -0
  152. package/dist/core/discovery.d.ts +38 -0
  153. package/dist/core/discovery.d.ts.map +1 -0
  154. package/dist/core/discovery.js +208 -0
  155. package/dist/core/discovery.js.map +1 -0
  156. package/dist/core/extension.d.ts +83 -0
  157. package/dist/core/extension.d.ts.map +1 -0
  158. package/dist/core/extension.js +429 -0
  159. package/dist/core/extension.js.map +1 -0
  160. package/dist/core/inspect.d.ts +62 -0
  161. package/dist/core/inspect.d.ts.map +1 -0
  162. package/dist/core/inspect.js +303 -0
  163. package/dist/core/inspect.js.map +1 -0
  164. package/dist/core/introspection.d.ts +8 -0
  165. package/dist/core/introspection.d.ts.map +1 -0
  166. package/dist/core/introspection.js +8 -0
  167. package/dist/core/introspection.js.map +1 -0
  168. package/dist/core/monitor.d.ts +42 -0
  169. package/dist/core/monitor.d.ts.map +1 -0
  170. package/dist/core/monitor.js +188 -0
  171. package/dist/core/monitor.js.map +1 -0
  172. package/dist/core/rent_projection.d.ts +153 -0
  173. package/dist/core/rent_projection.d.ts.map +1 -0
  174. package/dist/core/rent_projection.js +146 -0
  175. package/dist/core/rent_projection.js.map +1 -0
  176. package/dist/core/scvalTranslator.d.ts +11 -0
  177. package/dist/core/scvalTranslator.d.ts.map +1 -0
  178. package/dist/core/scvalTranslator.js +36 -0
  179. package/dist/core/scvalTranslator.js.map +1 -0
  180. package/dist/core/state_diff.d.ts +45 -0
  181. package/dist/core/state_diff.d.ts.map +1 -0
  182. package/dist/core/state_diff.js +112 -0
  183. package/dist/core/state_diff.js.map +1 -0
  184. package/dist/core/status.d.ts +24 -0
  185. package/dist/core/status.d.ts.map +1 -0
  186. package/dist/core/status.js +57 -0
  187. package/dist/core/status.js.map +1 -0
  188. package/dist/core/vault.d.ts +37 -0
  189. package/dist/core/vault.d.ts.map +1 -0
  190. package/dist/core/vault.js +179 -0
  191. package/dist/core/vault.js.map +1 -0
  192. package/dist/core/watch.d.ts +36 -0
  193. package/dist/core/watch.d.ts.map +1 -0
  194. package/dist/core/watch.js +176 -0
  195. package/dist/core/watch.js.map +1 -0
  196. package/dist/daemon/loop.d.ts +38 -0
  197. package/dist/daemon/loop.d.ts.map +1 -0
  198. package/dist/daemon/loop.js +169 -0
  199. package/dist/daemon/loop.js.map +1 -0
  200. package/dist/db/backup.d.ts +12 -0
  201. package/dist/db/backup.d.ts.map +1 -0
  202. package/dist/db/backup.js +132 -0
  203. package/dist/db/backup.js.map +1 -0
  204. package/dist/db/budget.d.ts +17 -0
  205. package/dist/db/budget.d.ts.map +1 -0
  206. package/dist/db/budget.js +31 -0
  207. package/dist/db/budget.js.map +1 -0
  208. package/dist/db/database.d.ts +6 -0
  209. package/dist/db/database.d.ts.map +1 -0
  210. package/dist/db/database.js +134 -0
  211. package/dist/db/database.js.map +1 -0
  212. package/dist/db/migrations/.gitkeep +1 -0
  213. package/dist/db/migrations/001_resource_usage_logs.sql +28 -0
  214. package/dist/db/migrator.d.ts +28 -0
  215. package/dist/db/migrator.d.ts.map +1 -0
  216. package/dist/db/migrator.js +75 -0
  217. package/dist/db/migrator.js.map +1 -0
  218. package/dist/db/repositories.d.ts +474 -0
  219. package/dist/db/repositories.d.ts.map +1 -0
  220. package/dist/db/repositories.js +809 -0
  221. package/dist/db/repositories.js.map +1 -0
  222. package/dist/db/schema.sql +207 -0
  223. package/dist/index.d.ts +3 -0
  224. package/dist/index.d.ts.map +1 -0
  225. package/dist/index.js +47 -0
  226. package/dist/index.js.map +1 -0
  227. package/dist/lib.d.ts +20 -0
  228. package/dist/lib.d.ts.map +1 -0
  229. package/dist/lib.js +16 -0
  230. package/dist/lib.js.map +1 -0
  231. package/dist/logging/index.d.ts +22 -0
  232. package/dist/logging/index.d.ts.map +1 -0
  233. package/dist/logging/index.js +34 -0
  234. package/dist/logging/index.js.map +1 -0
  235. package/dist/logging/logger.d.ts +14 -0
  236. package/dist/logging/logger.d.ts.map +1 -0
  237. package/dist/logging/logger.js +135 -0
  238. package/dist/logging/logger.js.map +1 -0
  239. package/dist/logging/types.d.ts +10 -0
  240. package/dist/logging/types.d.ts.map +1 -0
  241. package/dist/logging/types.js +2 -0
  242. package/dist/logging/types.js.map +1 -0
  243. package/dist/mcp/index.d.ts +3 -0
  244. package/dist/mcp/index.d.ts.map +1 -0
  245. package/dist/mcp/index.js +14 -0
  246. package/dist/mcp/index.js.map +1 -0
  247. package/dist/mcp/server.d.ts +10 -0
  248. package/dist/mcp/server.d.ts.map +1 -0
  249. package/dist/mcp/server.js +51 -0
  250. package/dist/mcp/server.js.map +1 -0
  251. package/dist/mcp/tools/get-extension-costs.d.ts +4 -0
  252. package/dist/mcp/tools/get-extension-costs.d.ts.map +1 -0
  253. package/dist/mcp/tools/get-extension-costs.js +33 -0
  254. package/dist/mcp/tools/get-extension-costs.js.map +1 -0
  255. package/dist/mcp/tools/get_contract_status.d.ts +8 -0
  256. package/dist/mcp/tools/get_contract_status.d.ts.map +1 -0
  257. package/dist/mcp/tools/get_contract_status.js +33 -0
  258. package/dist/mcp/tools/get_contract_status.js.map +1 -0
  259. package/dist/rpc/client.d.ts +154 -0
  260. package/dist/rpc/client.d.ts.map +1 -0
  261. package/dist/rpc/client.js +766 -0
  262. package/dist/rpc/client.js.map +1 -0
  263. package/dist/utils/config.d.ts +51 -0
  264. package/dist/utils/config.d.ts.map +1 -0
  265. package/dist/utils/config.js +86 -0
  266. package/dist/utils/config.js.map +1 -0
  267. package/dist/utils/formatting.d.ts +10 -0
  268. package/dist/utils/formatting.d.ts.map +1 -0
  269. package/dist/utils/formatting.js +66 -0
  270. package/dist/utils/formatting.js.map +1 -0
  271. package/dist/utils/watch-config.d.ts +13 -0
  272. package/dist/utils/watch-config.d.ts.map +1 -0
  273. package/dist/utils/watch-config.js +32 -0
  274. package/dist/utils/watch-config.js.map +1 -0
  275. package/package.json +73 -0
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Soroban rent cost projection model — Issue #168.
3
+ *
4
+ * Implements the canonical Stellar Soroban rent formula from
5
+ * stellar/rs-soroban-env (soroban-env-host/src/fees.rs):
6
+ *
7
+ * rent_stroops = ceil(
8
+ * entry_size_bytes × fee_per_rent_1kb × rent_ledgers
9
+ * / (1024 × rent_rate_denominator)
10
+ * )
11
+ *
12
+ * Where:
13
+ * - entry_size_bytes: in-memory size of the ledger entry
14
+ * - fee_per_rent_1kb: effective rent write fee per 1 KB (in stroops),
15
+ * computed from the current Soroban state size via
16
+ * `compute_rent_write_fee_per_1kb`. For projections, treated as an
17
+ * input (use network defaults or a fetched value).
18
+ * - rent_ledgers: number of ledger increments being projected
19
+ * - rent_rate_denominator: persistent_rent_rate_denominator or
20
+ * temporary_rent_rate_denominator (network configuration)
21
+ *
22
+ * Code entries (WASM) receive a 1/3 discount
23
+ * (CODE_ENTRY_RENT_DISCOUNT_FACTOR = 3).
24
+ *
25
+ * Network default values from stellar/rs-soroban-env and Stellar Lab
26
+ * resource configuration as of mid-2026.
27
+ */
28
+ /** Average Stellar ledger close time in seconds. */
29
+ export declare const AVG_LEDGER_CLOSE_SECONDS = 5.5;
30
+ /** Approximate number of ledgers per day (86400s ÷ 5.5s/ledger). */
31
+ export declare const LEDGERS_PER_DAY: number;
32
+ /** One XLM = 10,000,000 stroops. */
33
+ export declare const STROOPS_PER_XLM = 10000000;
34
+ /**
35
+ * DATA_SIZE_1KB_INCREMENT from fees.rs — the byte increment used in the
36
+ * fee calculation denominator.
37
+ */
38
+ export declare const DATA_SIZE_1KB_INCREMENT = 1024;
39
+ /**
40
+ * CODE_ENTRY_RENT_DISCOUNT_FACTOR from fees.rs.
41
+ * WASM code entry rent fees are divided (ceiling) by this value.
42
+ */
43
+ export declare const CODE_ENTRY_RENT_DISCOUNT_FACTOR = 3;
44
+ /**
45
+ * Default effective rent write fee per 1 KB in stroops.
46
+ *
47
+ * This is the `fee_per_rent_1kb` value, which in production is computed
48
+ * by `compute_rent_write_fee_per_1kb` based on the current Soroban state
49
+ * size. The default here (1000 stroops/KB) corresponds to the minimum
50
+ * floor `MINIMUM_RENT_WRITE_FEE_PER_1KB` defined in fees.rs and is a
51
+ * reasonable baseline for projections when live network data is unavailable.
52
+ *
53
+ * Callers should override this with a freshly-fetched value for production
54
+ * accuracy.
55
+ */
56
+ export declare const DEFAULT_FEE_PER_RENT_1KB = 1000;
57
+ /**
58
+ * Default denominator for persistent / instance entry rent rate.
59
+ *
60
+ * Source: Stellar network configuration (ConfigSettingsEntry).
61
+ * Current mainnet value is 2103 ledgers, meaning 1 KB of persistent state
62
+ * is charged `fee_per_rent_1kb / 2103` stroops per ledger.
63
+ */
64
+ export declare const DEFAULT_PERSISTENT_RENT_RATE_DENOMINATOR = 2103;
65
+ /**
66
+ * Default denominator for temporary entry rent rate.
67
+ *
68
+ * Temporary entries are cheaper — the network uses a larger denominator.
69
+ * Current mainnet value is 1000 ledgers.
70
+ */
71
+ export declare const DEFAULT_TEMPORARY_RENT_RATE_DENOMINATOR = 1000;
72
+ /** Input parameters for a single rent cost projection. */
73
+ export interface RentProjectionInput {
74
+ /** Size of the ledger entry in bytes. */
75
+ entrySizeBytes: number;
76
+ /** Number of days to project over. */
77
+ days: number;
78
+ /**
79
+ * Effective rent write fee per 1 KB in stroops (fee_per_rent_1kb).
80
+ * Defaults to DEFAULT_FEE_PER_RENT_1KB if not provided.
81
+ */
82
+ feePerRent1kb?: number;
83
+ /**
84
+ * Rent rate denominator for this entry type.
85
+ * Use DEFAULT_PERSISTENT_RENT_RATE_DENOMINATOR for persistent/instance.
86
+ * Use DEFAULT_TEMPORARY_RENT_RATE_DENOMINATOR for temporary.
87
+ */
88
+ rentRateDenominator?: number;
89
+ /**
90
+ * Whether this is a persistent (or instance) entry.
91
+ * Determines which denominator default to apply if rentRateDenominator
92
+ * is not explicitly provided.
93
+ */
94
+ isPersistent: boolean;
95
+ /**
96
+ * Whether this is a WASM code entry.
97
+ * Code entries receive a 1/3 rent discount per CODE_ENTRY_RENT_DISCOUNT_FACTOR.
98
+ */
99
+ isCodeEntry?: boolean;
100
+ }
101
+ /** Result of a single rent projection. */
102
+ export interface RentProjectionResult {
103
+ /** Number of days projected. */
104
+ days: number;
105
+ /** Approximate number of ledgers for this duration. */
106
+ ledgerCount: number;
107
+ /** Estimated rent cost in stroops. */
108
+ estimatedFeeStroops: number;
109
+ /** Estimated rent cost in XLM (stroops / 10_000_000). */
110
+ estimatedFeeXlm: number;
111
+ }
112
+ /** A single window result within a multi-window projection. */
113
+ export type RentWindowProjection = RentProjectionResult;
114
+ /** Result of a 30/60/90-day multi-window projection. */
115
+ export interface RentWindowsResult {
116
+ /** Entry size used for this projection. */
117
+ entrySizeBytes: number;
118
+ /** Fee per rent 1 KB used for this projection. */
119
+ feePerRent1kb: number;
120
+ /** Whether this is a persistent entry. */
121
+ isPersistent: boolean;
122
+ /** Whether this is a code entry. */
123
+ isCodeEntry: boolean;
124
+ /** The three projection windows: 30, 60, and 90 days. */
125
+ windows: [RentWindowProjection, RentWindowProjection, RentWindowProjection];
126
+ }
127
+ /**
128
+ * Project the rent cost for a single ledger entry over a given number of days.
129
+ *
130
+ * Uses the canonical Soroban rent formula from fees.rs:
131
+ *
132
+ * rent_stroops = ceil(
133
+ * entry_size_bytes × fee_per_rent_1kb × rent_ledgers
134
+ * / (1024 × rent_rate_denominator)
135
+ * )
136
+ *
137
+ * Code entries receive a discount: `ceil(full_fee / CODE_ENTRY_RENT_DISCOUNT_FACTOR)`.
138
+ *
139
+ * @param input - Projection parameters.
140
+ * @returns Cost estimate for the given duration.
141
+ */
142
+ export declare function projectRentCost(input: RentProjectionInput): RentProjectionResult;
143
+ /**
144
+ * Project rent costs for the standard 30, 60, and 90-day windows.
145
+ *
146
+ * Convenience wrapper around `projectRentCost` that returns all three
147
+ * windows in a single call.
148
+ *
149
+ * @param input - Base projection parameters (without `days`).
150
+ * @returns Multi-window projection result with 30, 60, and 90-day estimates.
151
+ */
152
+ export declare function projectRentWindows(input: Omit<RentProjectionInput, "days">): RentWindowsResult;
153
+ //# sourceMappingURL=rent_projection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rent_projection.d.ts","sourceRoot":"","sources":["../../src/core/rent_projection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,oDAAoD;AACpD,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,oEAAoE;AACpE,eAAO,MAAM,eAAe,QAAmC,CAAC;AAEhE,oCAAoC;AACpC,eAAO,MAAM,eAAe,WAAa,CAAC;AAE1C;;;GAGG;AACH,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,+BAA+B,IAAI,CAAC;AAEjD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C;;;;;;GAMG;AACH,eAAO,MAAM,wCAAwC,OAAO,CAAC;AAE7D;;;;;GAKG;AACH,eAAO,MAAM,uCAAuC,OAAO,CAAC;AAI5D,0DAA0D;AAC1D,MAAM,WAAW,mBAAmB;IAChC,yCAAyC;IACzC,cAAc,EAAE,MAAM,CAAC;IACvB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,YAAY,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,0CAA0C;AAC1C,MAAM,WAAW,oBAAoB;IACjC,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,yDAAyD;IACzD,eAAe,EAAE,MAAM,CAAC;CAC3B;AAED,+DAA+D;AAC/D,MAAM,MAAM,oBAAoB,GAAG,oBAAoB,CAAC;AAExD,wDAAwD;AACxD,MAAM,WAAW,iBAAiB;IAC9B,2CAA2C;IAC3C,cAAc,EAAE,MAAM,CAAC;IACvB,kDAAkD;IAClD,aAAa,EAAE,MAAM,CAAC;IACtB,0CAA0C;IAC1C,YAAY,EAAE,OAAO,CAAC;IACtB,oCAAoC;IACpC,WAAW,EAAE,OAAO,CAAC;IACrB,yDAAyD;IACzD,OAAO,EAAE,CAAC,oBAAoB,EAAE,oBAAoB,EAAE,oBAAoB,CAAC,CAAC;CAC/E;AAID;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,mBAAmB,GAAG,oBAAoB,CAoDhF;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAC9B,KAAK,EAAE,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,GACzC,iBAAiB,CAenB"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Soroban rent cost projection model — Issue #168.
3
+ *
4
+ * Implements the canonical Stellar Soroban rent formula from
5
+ * stellar/rs-soroban-env (soroban-env-host/src/fees.rs):
6
+ *
7
+ * rent_stroops = ceil(
8
+ * entry_size_bytes × fee_per_rent_1kb × rent_ledgers
9
+ * / (1024 × rent_rate_denominator)
10
+ * )
11
+ *
12
+ * Where:
13
+ * - entry_size_bytes: in-memory size of the ledger entry
14
+ * - fee_per_rent_1kb: effective rent write fee per 1 KB (in stroops),
15
+ * computed from the current Soroban state size via
16
+ * `compute_rent_write_fee_per_1kb`. For projections, treated as an
17
+ * input (use network defaults or a fetched value).
18
+ * - rent_ledgers: number of ledger increments being projected
19
+ * - rent_rate_denominator: persistent_rent_rate_denominator or
20
+ * temporary_rent_rate_denominator (network configuration)
21
+ *
22
+ * Code entries (WASM) receive a 1/3 discount
23
+ * (CODE_ENTRY_RENT_DISCOUNT_FACTOR = 3).
24
+ *
25
+ * Network default values from stellar/rs-soroban-env and Stellar Lab
26
+ * resource configuration as of mid-2026.
27
+ */
28
+ // ─── Constants ────────────────────────────────────────────────────────────────
29
+ /** Average Stellar ledger close time in seconds. */
30
+ export const AVG_LEDGER_CLOSE_SECONDS = 5.5;
31
+ /** Approximate number of ledgers per day (86400s ÷ 5.5s/ledger). */
32
+ export const LEDGERS_PER_DAY = 86400 / AVG_LEDGER_CLOSE_SECONDS;
33
+ /** One XLM = 10,000,000 stroops. */
34
+ export const STROOPS_PER_XLM = 10_000_000;
35
+ /**
36
+ * DATA_SIZE_1KB_INCREMENT from fees.rs — the byte increment used in the
37
+ * fee calculation denominator.
38
+ */
39
+ export const DATA_SIZE_1KB_INCREMENT = 1024;
40
+ /**
41
+ * CODE_ENTRY_RENT_DISCOUNT_FACTOR from fees.rs.
42
+ * WASM code entry rent fees are divided (ceiling) by this value.
43
+ */
44
+ export const CODE_ENTRY_RENT_DISCOUNT_FACTOR = 3;
45
+ /**
46
+ * Default effective rent write fee per 1 KB in stroops.
47
+ *
48
+ * This is the `fee_per_rent_1kb` value, which in production is computed
49
+ * by `compute_rent_write_fee_per_1kb` based on the current Soroban state
50
+ * size. The default here (1000 stroops/KB) corresponds to the minimum
51
+ * floor `MINIMUM_RENT_WRITE_FEE_PER_1KB` defined in fees.rs and is a
52
+ * reasonable baseline for projections when live network data is unavailable.
53
+ *
54
+ * Callers should override this with a freshly-fetched value for production
55
+ * accuracy.
56
+ */
57
+ export const DEFAULT_FEE_PER_RENT_1KB = 1000;
58
+ /**
59
+ * Default denominator for persistent / instance entry rent rate.
60
+ *
61
+ * Source: Stellar network configuration (ConfigSettingsEntry).
62
+ * Current mainnet value is 2103 ledgers, meaning 1 KB of persistent state
63
+ * is charged `fee_per_rent_1kb / 2103` stroops per ledger.
64
+ */
65
+ export const DEFAULT_PERSISTENT_RENT_RATE_DENOMINATOR = 2103;
66
+ /**
67
+ * Default denominator for temporary entry rent rate.
68
+ *
69
+ * Temporary entries are cheaper — the network uses a larger denominator.
70
+ * Current mainnet value is 1000 ledgers.
71
+ */
72
+ export const DEFAULT_TEMPORARY_RENT_RATE_DENOMINATOR = 1000;
73
+ // ─── Core projection logic ────────────────────────────────────────────────────
74
+ /**
75
+ * Project the rent cost for a single ledger entry over a given number of days.
76
+ *
77
+ * Uses the canonical Soroban rent formula from fees.rs:
78
+ *
79
+ * rent_stroops = ceil(
80
+ * entry_size_bytes × fee_per_rent_1kb × rent_ledgers
81
+ * / (1024 × rent_rate_denominator)
82
+ * )
83
+ *
84
+ * Code entries receive a discount: `ceil(full_fee / CODE_ENTRY_RENT_DISCOUNT_FACTOR)`.
85
+ *
86
+ * @param input - Projection parameters.
87
+ * @returns Cost estimate for the given duration.
88
+ */
89
+ export function projectRentCost(input) {
90
+ const { entrySizeBytes, days, isPersistent, isCodeEntry = false, } = input;
91
+ const feePerRent1kb = input.feePerRent1kb ?? DEFAULT_FEE_PER_RENT_1KB;
92
+ const rentRateDenominator = input.rentRateDenominator ??
93
+ (isPersistent
94
+ ? DEFAULT_PERSISTENT_RENT_RATE_DENOMINATOR
95
+ : DEFAULT_TEMPORARY_RENT_RATE_DENOMINATOR);
96
+ // Zero-input fast paths
97
+ if (entrySizeBytes <= 0 || days <= 0 || feePerRent1kb <= 0) {
98
+ return {
99
+ days,
100
+ ledgerCount: days <= 0 ? 0 : Math.ceil(days * LEDGERS_PER_DAY),
101
+ estimatedFeeStroops: 0,
102
+ estimatedFeeXlm: 0,
103
+ };
104
+ }
105
+ // Convert days to ledger count (ceiling to avoid underestimating)
106
+ const ledgerCount = Math.ceil(days * LEDGERS_PER_DAY);
107
+ // Canonical rent formula from fees.rs: rent_fee_for_size_and_ledgers
108
+ // num = entry_size_bytes × fee_per_rent_1kb × rent_ledgers
109
+ // denom = DATA_SIZE_1KB_INCREMENT × rent_rate_denominator
110
+ // fee = ceil(num / denom)
111
+ const denom = DATA_SIZE_1KB_INCREMENT * rentRateDenominator;
112
+ let estimatedFeeStroops = Math.ceil((entrySizeBytes * feePerRent1kb * ledgerCount) / denom);
113
+ // Apply code entry discount: ceil(fee / CODE_ENTRY_RENT_DISCOUNT_FACTOR)
114
+ if (isCodeEntry) {
115
+ estimatedFeeStroops = Math.ceil(estimatedFeeStroops / CODE_ENTRY_RENT_DISCOUNT_FACTOR);
116
+ }
117
+ const estimatedFeeXlm = estimatedFeeStroops / STROOPS_PER_XLM;
118
+ return {
119
+ days,
120
+ ledgerCount,
121
+ estimatedFeeStroops,
122
+ estimatedFeeXlm,
123
+ };
124
+ }
125
+ /**
126
+ * Project rent costs for the standard 30, 60, and 90-day windows.
127
+ *
128
+ * Convenience wrapper around `projectRentCost` that returns all three
129
+ * windows in a single call.
130
+ *
131
+ * @param input - Base projection parameters (without `days`).
132
+ * @returns Multi-window projection result with 30, 60, and 90-day estimates.
133
+ */
134
+ export function projectRentWindows(input) {
135
+ const resolvedFeePerRent1kb = input.feePerRent1kb ?? DEFAULT_FEE_PER_RENT_1KB;
136
+ const isCodeEntry = input.isCodeEntry ?? false;
137
+ const windows = [30, 60, 90].map(days => projectRentCost({ ...input, days }));
138
+ return {
139
+ entrySizeBytes: input.entrySizeBytes,
140
+ feePerRent1kb: resolvedFeePerRent1kb,
141
+ isPersistent: input.isPersistent,
142
+ isCodeEntry,
143
+ windows,
144
+ };
145
+ }
146
+ //# sourceMappingURL=rent_projection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rent_projection.js","sourceRoot":"","sources":["../../src/core/rent_projection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { xdr } from "@stellar/stellar-sdk";
2
+ /**
3
+ * Translate a Soroban `SCVal` into a JSON-safe representation.
4
+ *
5
+ * Design goals:
6
+ * - Recursively translate nested vectors/maps.
7
+ * - Avoid precision loss: large ints are always returned as strings.
8
+ * - Produce human-readable tokens for symbol/address.
9
+ */
10
+ export declare function scvalToJSON(val: xdr.ScVal): any;
11
+ //# sourceMappingURL=scvalTranslator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scvalTranslator.d.ts","sourceRoot":"","sources":["../../src/core/scvalTranslator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAiB,MAAM,sBAAsB,CAAC;AAE1D;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,GAAG,GAAG,CAQ/C"}
@@ -0,0 +1,36 @@
1
+ import { scValToNative } from "@stellar/stellar-sdk";
2
+ /**
3
+ * Translate a Soroban `SCVal` into a JSON-safe representation.
4
+ *
5
+ * Design goals:
6
+ * - Recursively translate nested vectors/maps.
7
+ * - Avoid precision loss: large ints are always returned as strings.
8
+ * - Produce human-readable tokens for symbol/address.
9
+ */
10
+ export function scvalToJSON(val) {
11
+ let native;
12
+ try {
13
+ native = scValToNative(val);
14
+ }
15
+ catch (e) {
16
+ return { unsupported: val.switch().name };
17
+ }
18
+ return serializeBigInts(native);
19
+ }
20
+ function serializeBigInts(obj) {
21
+ if (typeof obj === "bigint") {
22
+ return obj.toString(10);
23
+ }
24
+ if (Array.isArray(obj)) {
25
+ return obj.map(serializeBigInts);
26
+ }
27
+ if (obj !== null && typeof obj === "object") {
28
+ const res = {};
29
+ for (const [k, v] of Object.entries(obj)) {
30
+ res[k] = serializeBigInts(v);
31
+ }
32
+ return res;
33
+ }
34
+ return obj;
35
+ }
36
+ //# sourceMappingURL=scvalTranslator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scvalTranslator.js","sourceRoot":"","sources":["../../src/core/scvalTranslator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,GAAc;IACxC,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC;IAC5C,CAAC;IACD,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAClC,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAQ;IAChC,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC1B,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACnC,CAAC;IACD,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5C,MAAM,GAAG,GAAQ,EAAE,CAAC;QACpB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACzC,GAAG,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC;QAC/B,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,45 @@
1
+ import type Database from "better-sqlite3";
2
+ export interface StateDiffResult {
3
+ /** The kind of change: first appearance, value mutation, or removal. */
4
+ diffType: "created" | "updated" | "deleted";
5
+ /** The previous XDR value (null for `created`). */
6
+ oldValueXdr: string | null;
7
+ /** The current XDR value (null for `deleted`). */
8
+ newValueXdr: string | null;
9
+ /** SHA-256 hex hash of the old value (null for `created`). */
10
+ oldHash: string | null;
11
+ /** SHA-256 hex hash of the new value (null for `deleted`). */
12
+ newHash: string | null;
13
+ }
14
+ /**
15
+ * Compute a SHA-256 hex digest of a raw XDR value string.
16
+ * Used to quickly compare storage values without storing/diffing full payloads
17
+ * every cycle.
18
+ */
19
+ export declare function computeValueHash(valueXdr: string): string;
20
+ /**
21
+ * Compare two XDR value strings and produce a structured diff result.
22
+ *
23
+ * Returns `null` when the values are identical (including both-null),
24
+ * meaning no state change occurred.
25
+ *
26
+ * @param oldXdr - The previous XDR value, or null if the entry didn't exist.
27
+ * @param newXdr - The current XDR value, or null if the entry was removed.
28
+ */
29
+ export declare function diffStateValues(oldXdr: string | null, newXdr: string | null): StateDiffResult | null;
30
+ /**
31
+ * Process a state diff for a single contract entry within a monitoring cycle.
32
+ *
33
+ * 1. Fetch the latest snapshot for this entry from the DB.
34
+ * 2. Compare the current value hash against the stored hash.
35
+ * 3. If changed → persist a new snapshot and a state_change record.
36
+ * 4. If unchanged → return null (storage-optimised: no writes).
37
+ *
38
+ * @param db - An open better-sqlite3 Database handle.
39
+ * @param contractEntryId - The `contract_entries.id` of the entry being checked.
40
+ * @param currentValueXdr - The XDR-encoded value returned by the RPC for this entry.
41
+ * @param currentLedger - The ledger sequence at which this value was observed.
42
+ * @returns A `StateDiffResult` describing the change, or `null` if unchanged.
43
+ */
44
+ export declare function processStateDiff(db: Database.Database, contractEntryId: number, currentValueXdr: string, currentLedger: number): StateDiffResult | null;
45
+ //# sourceMappingURL=state_diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state_diff.d.ts","sourceRoot":"","sources":["../../src/core/state_diff.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAY3C,MAAM,WAAW,eAAe;IAC5B,wEAAwE;IACxE,QAAQ,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IAC5C,mDAAmD;IACnD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,kDAAkD;IAClD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,8DAA8D;IAC9D,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAID;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEzD;AAID;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,MAAM,EAAE,MAAM,GAAG,IAAI,GACtB,eAAe,GAAG,IAAI,CAqCxB;AAID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC5B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,eAAe,EAAE,MAAM,EACvB,eAAe,EAAE,MAAM,EACvB,aAAa,EAAE,MAAM,GACtB,eAAe,GAAG,IAAI,CA+CxB"}
@@ -0,0 +1,112 @@
1
+ import { createHash } from "node:crypto";
2
+ import { insertStateSnapshot, getLatestSnapshot, insertStateChange, } from "../db/repositories.js";
3
+ import { getLogger } from "../logging/index.js";
4
+ const logger = getLogger().child({ component: "StateDiff" });
5
+ // ─── Hash computation ─────────────────────────────────────────────────────────
6
+ /**
7
+ * Compute a SHA-256 hex digest of a raw XDR value string.
8
+ * Used to quickly compare storage values without storing/diffing full payloads
9
+ * every cycle.
10
+ */
11
+ export function computeValueHash(valueXdr) {
12
+ return createHash("sha256").update(valueXdr).digest("hex");
13
+ }
14
+ // ─── Diff logic ───────────────────────────────────────────────────────────────
15
+ /**
16
+ * Compare two XDR value strings and produce a structured diff result.
17
+ *
18
+ * Returns `null` when the values are identical (including both-null),
19
+ * meaning no state change occurred.
20
+ *
21
+ * @param oldXdr - The previous XDR value, or null if the entry didn't exist.
22
+ * @param newXdr - The current XDR value, or null if the entry was removed.
23
+ */
24
+ export function diffStateValues(oldXdr, newXdr) {
25
+ // Both null → no change
26
+ if (oldXdr === null && newXdr === null)
27
+ return null;
28
+ // Both present and identical → no change
29
+ if (oldXdr !== null && newXdr !== null && oldXdr === newXdr)
30
+ return null;
31
+ // Created: entry didn't exist before
32
+ if (oldXdr === null && newXdr !== null) {
33
+ return {
34
+ diffType: "created",
35
+ oldValueXdr: null,
36
+ newValueXdr: newXdr,
37
+ oldHash: null,
38
+ newHash: computeValueHash(newXdr),
39
+ };
40
+ }
41
+ // Deleted: entry existed before but is now gone
42
+ if (oldXdr !== null && newXdr === null) {
43
+ return {
44
+ diffType: "deleted",
45
+ oldValueXdr: oldXdr,
46
+ newValueXdr: null,
47
+ oldHash: computeValueHash(oldXdr),
48
+ newHash: null,
49
+ };
50
+ }
51
+ // Updated: both present and different
52
+ return {
53
+ diffType: "updated",
54
+ oldValueXdr: oldXdr,
55
+ newValueXdr: newXdr,
56
+ oldHash: computeValueHash(oldXdr),
57
+ newHash: computeValueHash(newXdr),
58
+ };
59
+ }
60
+ // ─── Orchestrator ─────────────────────────────────────────────────────────────
61
+ /**
62
+ * Process a state diff for a single contract entry within a monitoring cycle.
63
+ *
64
+ * 1. Fetch the latest snapshot for this entry from the DB.
65
+ * 2. Compare the current value hash against the stored hash.
66
+ * 3. If changed → persist a new snapshot and a state_change record.
67
+ * 4. If unchanged → return null (storage-optimised: no writes).
68
+ *
69
+ * @param db - An open better-sqlite3 Database handle.
70
+ * @param contractEntryId - The `contract_entries.id` of the entry being checked.
71
+ * @param currentValueXdr - The XDR-encoded value returned by the RPC for this entry.
72
+ * @param currentLedger - The ledger sequence at which this value was observed.
73
+ * @returns A `StateDiffResult` describing the change, or `null` if unchanged.
74
+ */
75
+ export function processStateDiff(db, contractEntryId, currentValueXdr, currentLedger) {
76
+ const currentHash = computeValueHash(currentValueXdr);
77
+ const lastSnapshot = getLatestSnapshot(db, contractEntryId);
78
+ // ── Determine diff ────────────────────────────────────────────────────────
79
+ const oldXdr = lastSnapshot?.value_xdr ?? null;
80
+ const diff = diffStateValues(oldXdr, currentValueXdr);
81
+ if (diff === null) {
82
+ // Value unchanged — skip writes to optimise storage
83
+ logger.debug(`State unchanged for entry ${contractEntryId} at ledger ${currentLedger}`);
84
+ return null;
85
+ }
86
+ // ── Persist new snapshot ──────────────────────────────────────────────────
87
+ const newSnapshotId = insertStateSnapshot(db, {
88
+ contract_entry_id: contractEntryId,
89
+ snapshot_ledger: currentLedger,
90
+ value_hash: currentHash,
91
+ value_xdr: currentValueXdr,
92
+ });
93
+ // ── Persist state change ─────────────────────────────────────────────────
94
+ const diffJson = JSON.stringify({
95
+ diffType: diff.diffType,
96
+ oldValueXdr: diff.oldValueXdr,
97
+ newValueXdr: diff.newValueXdr,
98
+ oldHash: diff.oldHash,
99
+ newHash: diff.newHash,
100
+ });
101
+ insertStateChange(db, {
102
+ contract_entry_id: contractEntryId,
103
+ old_snapshot_id: lastSnapshot?.id,
104
+ new_snapshot_id: newSnapshotId,
105
+ diff_type: diff.diffType,
106
+ diff_json: diffJson,
107
+ detected_at_ledger: currentLedger,
108
+ });
109
+ logger.info(`State ${diff.diffType} detected for entry ${contractEntryId} at ledger ${currentLedger}`);
110
+ return diff;
111
+ }
112
+ //# sourceMappingURL=state_diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state_diff.js","sourceRoot":"","sources":["../../src/core/state_diff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type Database from "better-sqlite3";
2
+ import { type TTLStatus } from "../utils/formatting.js";
3
+ export type EntryTTLStatus = TTLStatus | "unknown";
4
+ export type ContractStatusEntry = {
5
+ label: string;
6
+ entryType: string;
7
+ entryKeyXdr: string;
8
+ liveUntilLedger: number | null;
9
+ remainingTTL: number | null;
10
+ approximateTimeRemaining: string | null;
11
+ status: EntryTTLStatus;
12
+ };
13
+ export type ContractStatus = {
14
+ contractId: string;
15
+ name: string | null;
16
+ network: string;
17
+ lastCheckedLedger: number | null;
18
+ entries: ContractStatusEntry[];
19
+ };
20
+ export declare class ContractNotFoundError extends Error {
21
+ constructor(contractId: string);
22
+ }
23
+ export declare function getContractStatus(db: Database.Database, contractId: string): ContractStatus;
24
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/core/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAG3C,OAAO,EAGH,KAAK,SAAS,EACjB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,SAAS,CAAC;AAEnD,MAAM,MAAM,mBAAmB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,wBAAwB,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,MAAM,EAAE,cAAc,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,OAAO,EAAE,mBAAmB,EAAE,CAAC;CAClC,CAAC;AAEF,qBAAa,qBAAsB,SAAQ,KAAK;gBAChC,UAAU,EAAE,MAAM;CAIjC;AAyCD,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,GAAG,cAAc,CAmB3F"}
@@ -0,0 +1,57 @@
1
+ import { getContract, getEntriesForContract } from "../db/repositories.js";
2
+ import { classifyTTL, formatTimeToCloseLedger, } from "../utils/formatting.js";
3
+ export class ContractNotFoundError extends Error {
4
+ constructor(contractId) {
5
+ super(`Contract ${contractId} is not registered.`);
6
+ this.name = "ContractNotFoundError";
7
+ }
8
+ }
9
+ function getEntryLabel(entry) {
10
+ if (entry.entry_type === "instance")
11
+ return "Instance";
12
+ if (entry.entry_type === "wasm")
13
+ return "WASM Code";
14
+ return entry.label ?? entry.entry_type;
15
+ }
16
+ function mapEntryStatus(entry, lastCheckedLedger) {
17
+ const label = getEntryLabel(entry);
18
+ const liveUntilLedger = entry.live_until_ledger ?? null;
19
+ if (liveUntilLedger == null || lastCheckedLedger == null) {
20
+ return {
21
+ label,
22
+ entryType: entry.entry_type,
23
+ entryKeyXdr: entry.entry_key_xdr,
24
+ liveUntilLedger,
25
+ remainingTTL: null,
26
+ approximateTimeRemaining: null,
27
+ status: "unknown",
28
+ };
29
+ }
30
+ const remainingTTL = liveUntilLedger - lastCheckedLedger;
31
+ const status = classifyTTL(remainingTTL);
32
+ return {
33
+ label,
34
+ entryType: entry.entry_type,
35
+ entryKeyXdr: entry.entry_key_xdr,
36
+ liveUntilLedger,
37
+ remainingTTL,
38
+ approximateTimeRemaining: formatTimeToCloseLedger(remainingTTL),
39
+ status,
40
+ };
41
+ }
42
+ export function getContractStatus(db, contractId) {
43
+ const contract = getContract(db, contractId);
44
+ if (!contract) {
45
+ throw new ContractNotFoundError(contractId);
46
+ }
47
+ const lastCheckedLedger = contract.last_checked_ledger ?? null;
48
+ const entries = getEntriesForContract(db, contractId).map((entry) => mapEntryStatus(entry, lastCheckedLedger));
49
+ return {
50
+ contractId: contract.id,
51
+ name: contract.name,
52
+ network: contract.network,
53
+ lastCheckedLedger,
54
+ entries,
55
+ };
56
+ }
57
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/core/status.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAE3E,OAAO,EACH,WAAW,EACX,uBAAuB,GAE1B,MAAM,wBAAwB,CAAC;AAsBhC,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC5C,YAAY,UAAkB;QAC1B,KAAK,CAAC,YAAY,UAAU,qBAAqB,CAAC,CAAC;QACnD,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACxC,CAAC;CACJ;AAED,SAAS,aAAa,CAAC,KAAoB;IACvC,IAAI,KAAK,CAAC,UAAU,KAAK,UAAU;QAAE,OAAO,UAAU,CAAC;IACvD,IAAI,KAAK,CAAC,UAAU,KAAK,MAAM;QAAE,OAAO,WAAW,CAAC;IACpD,OAAO,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC;AAC3C,CAAC;AAED,SAAS,cAAc,CACnB,KAAoB,EACpB,iBAAgC;IAEhC,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,eAAe,GAAG,KAAK,CAAC,iBAAiB,IAAI,IAAI,CAAC;IAExD,IAAI,eAAe,IAAI,IAAI,IAAI,iBAAiB,IAAI,IAAI,EAAE,CAAC;QACvD,OAAO;YACH,KAAK;YACL,SAAS,EAAE,KAAK,CAAC,UAAU;YAC3B,WAAW,EAAE,KAAK,CAAC,aAAa;YAChC,eAAe;YACf,YAAY,EAAE,IAAI;YAClB,wBAAwB,EAAE,IAAI;YAC9B,MAAM,EAAE,SAAS;SACpB,CAAC;IACN,CAAC;IAED,MAAM,YAAY,GAAG,eAAe,GAAG,iBAAiB,CAAC;IACzD,MAAM,MAAM,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC;IAEzC,OAAO;QACH,KAAK;QACL,SAAS,EAAE,KAAK,CAAC,UAAU;QAC3B,WAAW,EAAE,KAAK,CAAC,aAAa;QAChC,eAAe;QACf,YAAY;QACZ,wBAAwB,EAAE,uBAAuB,CAAC,YAAY,CAAC;QAC/D,MAAM;KACT,CAAC;AACN,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAqB,EAAE,UAAkB;IACvE,MAAM,QAAQ,GAAG,WAAW,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAE7C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACZ,MAAM,IAAI,qBAAqB,CAAC,UAAU,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,iBAAiB,GAAG,QAAQ,CAAC,mBAAmB,IAAI,IAAI,CAAC;IAC/D,MAAM,OAAO,GAAG,qBAAqB,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAChE,cAAc,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAC3C,CAAC;IAEF,OAAO;QACH,UAAU,EAAE,QAAQ,CAAC,EAAE;QACvB,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,iBAAiB;QACjB,OAAO;KACV,CAAC;AACN,CAAC"}