@shieldedtech/moth-wallet 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 (230) hide show
  1. package/README.md +62 -0
  2. package/dist/contract/args-parser.d.ts +29 -0
  3. package/dist/contract/args-parser.d.ts.map +1 -0
  4. package/dist/contract/args-parser.js +123 -0
  5. package/dist/contract/args-parser.js.map +1 -0
  6. package/dist/contract/artifact-loader.d.ts +26 -0
  7. package/dist/contract/artifact-loader.d.ts.map +1 -0
  8. package/dist/contract/artifact-loader.js +129 -0
  9. package/dist/contract/artifact-loader.js.map +1 -0
  10. package/dist/contract/call.d.ts +32 -0
  11. package/dist/contract/call.d.ts.map +1 -0
  12. package/dist/contract/call.js +245 -0
  13. package/dist/contract/call.js.map +1 -0
  14. package/dist/contract/deploy.d.ts +36 -0
  15. package/dist/contract/deploy.d.ts.map +1 -0
  16. package/dist/contract/deploy.js +388 -0
  17. package/dist/contract/deploy.js.map +1 -0
  18. package/dist/contract/fungible-token.d.ts +40 -0
  19. package/dist/contract/fungible-token.d.ts.map +1 -0
  20. package/dist/contract/fungible-token.js +88 -0
  21. package/dist/contract/fungible-token.js.map +1 -0
  22. package/dist/contract/initial-private-state.d.ts +18 -0
  23. package/dist/contract/initial-private-state.d.ts.map +1 -0
  24. package/dist/contract/initial-private-state.js +44 -0
  25. package/dist/contract/initial-private-state.js.map +1 -0
  26. package/dist/contract/maintenance.d.ts +72 -0
  27. package/dist/contract/maintenance.d.ts.map +1 -0
  28. package/dist/contract/maintenance.js +430 -0
  29. package/dist/contract/maintenance.js.map +1 -0
  30. package/dist/contract/state.d.ts +16 -0
  31. package/dist/contract/state.d.ts.map +1 -0
  32. package/dist/contract/state.js +18 -0
  33. package/dist/contract/state.js.map +1 -0
  34. package/dist/contract/witness-loader.d.ts +3 -0
  35. package/dist/contract/witness-loader.d.ts.map +1 -0
  36. package/dist/contract/witness-loader.js +27 -0
  37. package/dist/contract/witness-loader.js.map +1 -0
  38. package/dist/daemon/api-keys.d.ts +66 -0
  39. package/dist/daemon/api-keys.d.ts.map +1 -0
  40. package/dist/daemon/api-keys.js +222 -0
  41. package/dist/daemon/api-keys.js.map +1 -0
  42. package/dist/daemon/audit-log.d.ts +65 -0
  43. package/dist/daemon/audit-log.d.ts.map +1 -0
  44. package/dist/daemon/audit-log.js +73 -0
  45. package/dist/daemon/audit-log.js.map +1 -0
  46. package/dist/daemon/client.d.ts +63 -0
  47. package/dist/daemon/client.d.ts.map +1 -0
  48. package/dist/daemon/client.js +214 -0
  49. package/dist/daemon/client.js.map +1 -0
  50. package/dist/daemon/confirmation-queue.d.ts +46 -0
  51. package/dist/daemon/confirmation-queue.d.ts.map +1 -0
  52. package/dist/daemon/confirmation-queue.js +92 -0
  53. package/dist/daemon/confirmation-queue.js.map +1 -0
  54. package/dist/daemon/index.d.ts +23 -0
  55. package/dist/daemon/index.d.ts.map +1 -0
  56. package/dist/daemon/index.js +26 -0
  57. package/dist/daemon/index.js.map +1 -0
  58. package/dist/daemon/protocol.d.ts +40 -0
  59. package/dist/daemon/protocol.d.ts.map +1 -0
  60. package/dist/daemon/protocol.js +104 -0
  61. package/dist/daemon/protocol.js.map +1 -0
  62. package/dist/daemon/server.d.ts +158 -0
  63. package/dist/daemon/server.d.ts.map +1 -0
  64. package/dist/daemon/server.js +297 -0
  65. package/dist/daemon/server.js.map +1 -0
  66. package/dist/daemon/wallet-handlers.d.ts +37 -0
  67. package/dist/daemon/wallet-handlers.d.ts.map +1 -0
  68. package/dist/daemon/wallet-handlers.js +533 -0
  69. package/dist/daemon/wallet-handlers.js.map +1 -0
  70. package/dist/daemon/wallet-rpc-parsers.d.ts +14 -0
  71. package/dist/daemon/wallet-rpc-parsers.d.ts.map +1 -0
  72. package/dist/daemon/wallet-rpc-parsers.js +259 -0
  73. package/dist/daemon/wallet-rpc-parsers.js.map +1 -0
  74. package/dist/daemon/wallet-rpc-types.d.ts +138 -0
  75. package/dist/daemon/wallet-rpc-types.d.ts.map +1 -0
  76. package/dist/daemon/wallet-rpc-types.js +11 -0
  77. package/dist/daemon/wallet-rpc-types.js.map +1 -0
  78. package/dist/diagnostics/fs-timing-store.d.ts +29 -0
  79. package/dist/diagnostics/fs-timing-store.d.ts.map +1 -0
  80. package/dist/diagnostics/fs-timing-store.js +81 -0
  81. package/dist/diagnostics/fs-timing-store.js.map +1 -0
  82. package/dist/diagnostics/timings.d.ts +64 -0
  83. package/dist/diagnostics/timings.d.ts.map +1 -0
  84. package/dist/diagnostics/timings.js +100 -0
  85. package/dist/diagnostics/timings.js.map +1 -0
  86. package/dist/index.d.ts +37 -0
  87. package/dist/index.d.ts.map +1 -0
  88. package/dist/index.js +47 -0
  89. package/dist/index.js.map +1 -0
  90. package/dist/network/indexer-client.d.ts +66 -0
  91. package/dist/network/indexer-client.d.ts.map +1 -0
  92. package/dist/network/indexer-client.js +112 -0
  93. package/dist/network/indexer-client.js.map +1 -0
  94. package/dist/network/node-client.d.ts +27 -0
  95. package/dist/network/node-client.d.ts.map +1 -0
  96. package/dist/network/node-client.js +94 -0
  97. package/dist/network/node-client.js.map +1 -0
  98. package/dist/proof/client.d.ts +15 -0
  99. package/dist/proof/client.d.ts.map +1 -0
  100. package/dist/proof/client.js +77 -0
  101. package/dist/proof/client.js.map +1 -0
  102. package/dist/proof/provider.d.ts +12 -0
  103. package/dist/proof/provider.d.ts.map +1 -0
  104. package/dist/proof/provider.js +96 -0
  105. package/dist/proof/provider.js.map +1 -0
  106. package/dist/providers/index.d.ts +2 -0
  107. package/dist/providers/index.d.ts.map +1 -0
  108. package/dist/providers/index.js +2 -0
  109. package/dist/providers/index.js.map +1 -0
  110. package/dist/providers/midnight-provider.d.ts +11 -0
  111. package/dist/providers/midnight-provider.d.ts.map +1 -0
  112. package/dist/providers/midnight-provider.js +30 -0
  113. package/dist/providers/midnight-provider.js.map +1 -0
  114. package/dist/storage/adapter.d.ts +8 -0
  115. package/dist/storage/adapter.d.ts.map +1 -0
  116. package/dist/storage/adapter.js +2 -0
  117. package/dist/storage/adapter.js.map +1 -0
  118. package/dist/storage/fs-adapter.d.ts +14 -0
  119. package/dist/storage/fs-adapter.d.ts.map +1 -0
  120. package/dist/storage/fs-adapter.js +126 -0
  121. package/dist/storage/fs-adapter.js.map +1 -0
  122. package/dist/storage/safe-path.d.ts +13 -0
  123. package/dist/storage/safe-path.d.ts.map +1 -0
  124. package/dist/storage/safe-path.js +35 -0
  125. package/dist/storage/safe-path.js.map +1 -0
  126. package/dist/sync/activity.d.ts +53 -0
  127. package/dist/sync/activity.d.ts.map +1 -0
  128. package/dist/sync/activity.js +92 -0
  129. package/dist/sync/activity.js.map +1 -0
  130. package/dist/sync/dust-registration-estimate.d.ts +54 -0
  131. package/dist/sync/dust-registration-estimate.d.ts.map +1 -0
  132. package/dist/sync/dust-registration-estimate.js +103 -0
  133. package/dist/sync/dust-registration-estimate.js.map +1 -0
  134. package/dist/sync/node-sync-store.d.ts +10 -0
  135. package/dist/sync/node-sync-store.d.ts.map +1 -0
  136. package/dist/sync/node-sync-store.js +42 -0
  137. package/dist/sync/node-sync-store.js.map +1 -0
  138. package/dist/sync/operations.d.ts +138 -0
  139. package/dist/sync/operations.d.ts.map +1 -0
  140. package/dist/sync/operations.js +395 -0
  141. package/dist/sync/operations.js.map +1 -0
  142. package/dist/sync/preseed-parts.d.ts +18 -0
  143. package/dist/sync/preseed-parts.d.ts.map +1 -0
  144. package/dist/sync/preseed-parts.js +33 -0
  145. package/dist/sync/preseed-parts.js.map +1 -0
  146. package/dist/sync/preseed.d.ts +95 -0
  147. package/dist/sync/preseed.d.ts.map +1 -0
  148. package/dist/sync/preseed.js +365 -0
  149. package/dist/sync/preseed.js.map +1 -0
  150. package/dist/sync/progress.d.ts +45 -0
  151. package/dist/sync/progress.d.ts.map +1 -0
  152. package/dist/sync/progress.js +51 -0
  153. package/dist/sync/progress.js.map +1 -0
  154. package/dist/sync/sdk-dedup.d.ts +53 -0
  155. package/dist/sync/sdk-dedup.d.ts.map +1 -0
  156. package/dist/sync/sdk-dedup.js +134 -0
  157. package/dist/sync/sdk-dedup.js.map +1 -0
  158. package/dist/sync/sync-store.d.ts +33 -0
  159. package/dist/sync/sync-store.d.ts.map +1 -0
  160. package/dist/sync/sync-store.js +45 -0
  161. package/dist/sync/sync-store.js.map +1 -0
  162. package/dist/sync/wallet-sync.d.ts +153 -0
  163. package/dist/sync/wallet-sync.d.ts.map +1 -0
  164. package/dist/sync/wallet-sync.js +943 -0
  165. package/dist/sync/wallet-sync.js.map +1 -0
  166. package/dist/types/errors.d.ts +18 -0
  167. package/dist/types/errors.d.ts.map +1 -0
  168. package/dist/types/errors.js +33 -0
  169. package/dist/types/errors.js.map +1 -0
  170. package/dist/types/exit-codes.d.ts +8 -0
  171. package/dist/types/exit-codes.d.ts.map +1 -0
  172. package/dist/types/exit-codes.js +7 -0
  173. package/dist/types/exit-codes.js.map +1 -0
  174. package/dist/types/index.d.ts +6 -0
  175. package/dist/types/index.d.ts.map +1 -0
  176. package/dist/types/index.js +3 -0
  177. package/dist/types/index.js.map +1 -0
  178. package/dist/types/network.d.ts +65 -0
  179. package/dist/types/network.d.ts.map +1 -0
  180. package/dist/types/network.js +108 -0
  181. package/dist/types/network.js.map +1 -0
  182. package/dist/types/tokens.d.ts +3 -0
  183. package/dist/types/tokens.d.ts.map +1 -0
  184. package/dist/types/tokens.js +11 -0
  185. package/dist/types/tokens.js.map +1 -0
  186. package/dist/types/transaction.d.ts +13 -0
  187. package/dist/types/transaction.d.ts.map +1 -0
  188. package/dist/types/transaction.js +2 -0
  189. package/dist/types/transaction.js.map +1 -0
  190. package/dist/types/wallet.d.ts +82 -0
  191. package/dist/types/wallet.d.ts.map +1 -0
  192. package/dist/types/wallet.js +2 -0
  193. package/dist/types/wallet.js.map +1 -0
  194. package/dist/wallet/address-validate.d.ts +7 -0
  195. package/dist/wallet/address-validate.d.ts.map +1 -0
  196. package/dist/wallet/address-validate.js +28 -0
  197. package/dist/wallet/address-validate.js.map +1 -0
  198. package/dist/wallet/address.d.ts +32 -0
  199. package/dist/wallet/address.d.ts.map +1 -0
  200. package/dist/wallet/address.js +107 -0
  201. package/dist/wallet/address.js.map +1 -0
  202. package/dist/wallet/app-secret.d.ts +15 -0
  203. package/dist/wallet/app-secret.d.ts.map +1 -0
  204. package/dist/wallet/app-secret.js +45 -0
  205. package/dist/wallet/app-secret.js.map +1 -0
  206. package/dist/wallet/balance-format.d.ts +12 -0
  207. package/dist/wallet/balance-format.d.ts.map +1 -0
  208. package/dist/wallet/balance-format.js +51 -0
  209. package/dist/wallet/balance-format.js.map +1 -0
  210. package/dist/wallet/batch-transfer.d.ts +36 -0
  211. package/dist/wallet/batch-transfer.d.ts.map +1 -0
  212. package/dist/wallet/batch-transfer.js +93 -0
  213. package/dist/wallet/batch-transfer.js.map +1 -0
  214. package/dist/wallet/keystore.d.ts +16 -0
  215. package/dist/wallet/keystore.d.ts.map +1 -0
  216. package/dist/wallet/keystore.js +76 -0
  217. package/dist/wallet/keystore.js.map +1 -0
  218. package/dist/wallet/manager.d.ts +77 -0
  219. package/dist/wallet/manager.d.ts.map +1 -0
  220. package/dist/wallet/manager.js +474 -0
  221. package/dist/wallet/manager.js.map +1 -0
  222. package/dist/wallet/mnemonic.d.ts +5 -0
  223. package/dist/wallet/mnemonic.d.ts.map +1 -0
  224. package/dist/wallet/mnemonic.js +28 -0
  225. package/dist/wallet/mnemonic.js.map +1 -0
  226. package/dist/wallet/sign-message.d.ts +20 -0
  227. package/dist/wallet/sign-message.d.ts.map +1 -0
  228. package/dist/wallet/sign-message.js +58 -0
  229. package/dist/wallet/sign-message.js.map +1 -0
  230. package/package.json +55 -0
@@ -0,0 +1,365 @@
1
+ // Pre-seed optimization for newly generated wallets.
2
+ // Syncs an empty reference wallet to chain tip once per network, then
3
+ // copies its state snapshot to new wallets with swapped keys.
4
+ // This avoids the full genesis scan for shielded + unshielded sub-wallets.
5
+ // Dust wallet always syncs from genesis (see comment below).
6
+ // Architecture follows mn-tui. See NOTICE for attribution.
7
+ // Key derivation now flows through deriveWalletKeys (Option A); the reference
8
+ // state persists through the async SyncStateStore (browser-safe), so v8's
9
+ // node:fs pre-seed bridge is not used here.
10
+ import { generateMnemonic24, mnemonicToSeed } from '../wallet/mnemonic.js';
11
+ import { createKeystore, PublicKey } from '@midnightntwrk/wallet-sdk/unshielded';
12
+ import { setNetworkId } from '@midnight-ntwrk/midnight-js/network-id';
13
+ import { startWalletSync, resolveSyncStore } from './wallet-sync.js';
14
+ import { emptyRefHeightKey, emptyRefMnemonicKey, emptyRefStateKey, EMPTY_REF_WALLET } from './sync-store.js';
15
+ import { IndexerClient } from '../network/indexer-client.js';
16
+ import { deriveWalletKeys } from './operations.js';
17
+ // In-memory cache: network → synced reference states
18
+ const refCache = new Map();
19
+ const refInFlight = new Map();
20
+ /**
21
+ * How long a deliberate reference build may take before giving up. Dust
22
+ * dominates: it streams the whole chain (~1.4M events on preprod at a few
23
+ * hundred per second), so this has to be generous. A build that times out
24
+ * leaves its partial state in the store, and the next attempt resumes from it.
25
+ */
26
+ const REF_BUILD_TIMEOUT_MS = 2 * 60 * 60_000;
27
+ function loadRefState(store, networkId, part) {
28
+ return store.get(emptyRefStateKey(networkId, part));
29
+ }
30
+ /**
31
+ * The applied index a serialized sub-wallet snapshot resumes from.
32
+ *
33
+ * `offset` is what the SDK writes from `progress.appliedIndex` and reads back as
34
+ * `appliedIndex` on restore (see makeDefaultV1SerializationCapability in the
35
+ * dust wallet's Serialization.ts). An offset of 0 is the "nothing applied yet"
36
+ * sentinel: the sync then opens its subscription with no cursor and streams from
37
+ * genesis. So a reference whose offset is 0 seeds nothing, however complete the
38
+ * rest of the snapshot looks.
39
+ */
40
+ function snapshotOffset(raw) {
41
+ try {
42
+ const parsed = JSON.parse(raw);
43
+ return parsed.offset === undefined ? 0n : BigInt(parsed.offset);
44
+ }
45
+ catch {
46
+ return 0n;
47
+ }
48
+ }
49
+ /**
50
+ * Read the cached reference states, but only accept them if they actually
51
+ * reached chain tip. Guards against the failure mode this cache had for months:
52
+ * the reference was serialized moments after starting, so every part sat at
53
+ * offset 0 and new wallets full-scanned anyway while being told they were
54
+ * pre-seeded "at chain tip".
55
+ */
56
+ async function loadUsableRefStates(store, networkId) {
57
+ const [shielded, unshielded, dust] = await Promise.all([
58
+ loadRefState(store, networkId, 'shielded'),
59
+ loadRefState(store, networkId, 'unshielded'),
60
+ loadRefState(store, networkId, 'dust'),
61
+ ]);
62
+ if (!shielded || !unshielded || !dust)
63
+ return null;
64
+ // Dust is the expensive one and the one whose cursor we are really after;
65
+ // shielded is checked too so a half-built reference is not treated as warm.
66
+ if (snapshotOffset(dust) <= 0n || snapshotOffset(shielded) <= 0n)
67
+ return null;
68
+ // No recorded height means no way to prove the reference is not newer than a
69
+ // given wallet's birthday, so it cannot be used safely at all.
70
+ const rawHeight = await store.get(emptyRefHeightKey(networkId));
71
+ const height = rawHeight ? Number(rawHeight) : 0;
72
+ if (!Number.isFinite(height) || height <= 0)
73
+ return null;
74
+ return { shielded, unshielded, dust, height };
75
+ }
76
+ /** Wait until the wallet reports fully synced, or give up. */
77
+ function waitForTip(synced, timeoutMs) {
78
+ return new Promise((resolve) => {
79
+ let settled = false;
80
+ let unsubscribe;
81
+ const finish = (reached) => {
82
+ if (settled)
83
+ return;
84
+ settled = true;
85
+ unsubscribe?.();
86
+ resolve(reached);
87
+ };
88
+ const timer = setTimeout(() => finish(false), timeoutMs);
89
+ // subscribe() replays the current balances synchronously, so an
90
+ // already-synced wallet settles here without waiting for a fresh emission.
91
+ const stop = synced.subscribe((b) => {
92
+ if (b.synced) {
93
+ clearTimeout(timer);
94
+ finish(true);
95
+ }
96
+ });
97
+ unsubscribe = stop;
98
+ if (settled)
99
+ stop();
100
+ });
101
+ }
102
+ /**
103
+ * Ensure an empty reference wallet is synced to chain tip for this network.
104
+ * The first call per network syncs from genesis (or restores from cache).
105
+ * Subsequent calls return the cached result immediately.
106
+ */
107
+ // NOTE: v8 had a loadFreshRefFromDisk() that reused a recently-warmed on-disk
108
+ // reference across daemon processes by stat-ing ~/.moth .dat mtimes. Under the
109
+ // async SyncStateStore model that cross-process reuse is subsumed by the store
110
+ // itself: buildEmptyRefCache reuses the reference mnemonic from the store and
111
+ // startWalletSync restores the cached ref state from it, so a second process
112
+ // resumes from cache rather than genesis without any node:fs freshness probe.
113
+ export async function ensureEmptyRefCache(network, onProgress, store,
114
+ /**
115
+ * Build the reference if none is warm yet. OFF by default, and deliberately
116
+ * so: building means syncing an empty wallet to chain tip, which dust makes a
117
+ * tens-of-minutes job. On the wallet-startup path that would block the user's
118
+ * own wallet from starting, which is far worse than letting it sync from
119
+ * genesis in the background. Callers that mean to pay the cost use
120
+ * warmEmptyRefCache().
121
+ */
122
+ opts) {
123
+ const cached = refCache.get(network.id);
124
+ if (cached)
125
+ return cached;
126
+ const inFlight = refInFlight.get(network.id);
127
+ if (inFlight)
128
+ return inFlight;
129
+ // A reference already synced to tip by an earlier run (or another process)
130
+ // is usable immediately, with no sync of any kind.
131
+ const resolved = await resolveSyncStore(store);
132
+ const warm = await loadUsableRefStates(resolved, network.id);
133
+ if (warm) {
134
+ refCache.set(network.id, warm);
135
+ return warm;
136
+ }
137
+ if (!opts?.build) {
138
+ onProgress?.('Pre-seed: no reference at chain tip for this network — syncing from genesis');
139
+ return null;
140
+ }
141
+ const promise = buildEmptyRefCache(network, onProgress, store, opts?.onWarmProgress);
142
+ refInFlight.set(network.id, promise);
143
+ try {
144
+ const result = await promise;
145
+ if (result)
146
+ refCache.set(network.id, result);
147
+ return result;
148
+ }
149
+ finally {
150
+ refInFlight.delete(network.id);
151
+ }
152
+ }
153
+ /**
154
+ * Build (or resume building) the empty reference wallet for a network, syncing
155
+ * it all the way to chain tip so it can actually pre-seed new wallets.
156
+ *
157
+ * This is the slow one: the first build walks the whole chain, which dust makes
158
+ * a tens-of-minutes job on preprod. It is worth paying once per network per
159
+ * machine, because afterwards every new wallet — and every dust rebuild — starts
160
+ * at tip instead of genesis. Intended for a background task or an explicit
161
+ * command, never the wallet-startup path.
162
+ */
163
+ /**
164
+ * Sync an EXISTING reference forward to chain tip, instead of rebuilding it.
165
+ *
166
+ * ensureEmptyRefCache short-circuits on a warm reference, so before this the only
167
+ * way to update one was to delete it and walk the chain again — 53.6 min on
168
+ * preprod. That was never a limitation of the mechanism, only of the entry
169
+ * points: buildEmptyRefCache already resumes from whatever reference state the
170
+ * store holds (it syncs under EMPTY_REF_WALLET, and startWalletSync restores that
171
+ * wallet's cache like any other), so catching up costs the drift and nothing more.
172
+ *
173
+ * A stale reference is safe to use — it only means more catch-up for the wallets
174
+ * it seeds, at roughly half a second per hour of age — so this is an optimisation,
175
+ * not a repair. Run it before cutting a release, or on a schedule.
176
+ */
177
+ export async function refreshEmptyRefCache(network, onProgress, store, onWarmProgress) {
178
+ // Drop the in-process memo so the refreshed state is what later callers see.
179
+ refCache.delete(network.id);
180
+ // Share an in-flight build rather than starting a second chain walk beside it.
181
+ const inFlight = refInFlight.get(network.id);
182
+ if (inFlight)
183
+ return inFlight;
184
+ const promise = buildEmptyRefCache(network, onProgress, store, onWarmProgress);
185
+ refInFlight.set(network.id, promise);
186
+ try {
187
+ const result = await promise;
188
+ if (result)
189
+ refCache.set(network.id, result);
190
+ return result;
191
+ }
192
+ finally {
193
+ refInFlight.delete(network.id);
194
+ }
195
+ }
196
+ export function warmEmptyRefCache(network, onProgress, store, onWarmProgress) {
197
+ return ensureEmptyRefCache(network, onProgress, store, { build: true, onWarmProgress });
198
+ }
199
+ /**
200
+ * Whether this network has a reference at chain tip, without building anything.
201
+ * Lets a UI distinguish "not started" from "in progress" from "done" — a build
202
+ * runs for an hour, so reporting it as perpetually in progress is misleading.
203
+ */
204
+ export async function preseedReferenceStatus(network, store) {
205
+ const resolved = await resolveSyncStore(store);
206
+ const states = await loadUsableRefStates(resolved, network.id);
207
+ return { ready: states !== null, height: states?.height ?? null };
208
+ }
209
+ async function buildEmptyRefCache(network, onProgress, store, onWarmProgress) {
210
+ const resolved = await resolveSyncStore(store);
211
+ // Generate or reuse a reference mnemonic
212
+ // Reference wallet mnemonic — this wallet is never funded and exists only
213
+ // as an empty-at-tip cache for pre-seeding new wallets.
214
+ // SECURITY: Stored with owner-only permissions (0600). Do NOT fund this wallet.
215
+ const mnemonicKey = emptyRefMnemonicKey(network.id);
216
+ let mnemonic = (await resolved.get(mnemonicKey))?.trim();
217
+ if (mnemonic) {
218
+ onProgress?.('Pre-seed: reusing reference wallet');
219
+ }
220
+ else {
221
+ mnemonic = generateMnemonic24();
222
+ await resolved.put(mnemonicKey, mnemonic);
223
+ onProgress?.('Pre-seed: generated new reference wallet (first sync may be slow)');
224
+ }
225
+ // Derive seed + typed key bundle
226
+ const seed = await mnemonicToSeed(mnemonic);
227
+ const seedHex = Array.from(seed).map((b) => b.toString(16).padStart(2, '0')).join('');
228
+ const referenceKeys = deriveWalletKeys(seedHex);
229
+ // Seed is no longer needed past derivation — zero the local buffer.
230
+ seed.fill(0);
231
+ onProgress?.('Pre-seed: syncing reference wallet to chain tip...');
232
+ try {
233
+ // Sync the reference wallet under EMPTY_REF_WALLET into the shared async
234
+ // store; startWalletSync persists each sub-wallet's state there, so
235
+ // loadRefState reads it straight back — no separate fs promotion needed.
236
+ const synced = await startWalletSync(referenceKeys, network, onProgress, EMPTY_REF_WALLET, undefined, undefined, {
237
+ syncStore: resolved,
238
+ });
239
+ // Wait for the reference to actually reach tip before serializing it.
240
+ // startWalletSync resolves on the first balance emission (or a 5s timeout),
241
+ // so stopping straight away — as this did — serialized an empty wallet:
242
+ // every part at offset 0, which the SDK reads as "stream from genesis". The
243
+ // pre-seed then reported success while seeding nothing, and dust paid a full
244
+ // chain walk on every new wallet.
245
+ const unsubscribeProgress = onWarmProgress
246
+ ? synced.subscribe((b) => onWarmProgress({
247
+ applied: b.subProgress.dust.applied,
248
+ total: b.subProgress.dust.total,
249
+ synced: b.synced,
250
+ }))
251
+ : undefined;
252
+ const reachedTip = await waitForTip(synced, REF_BUILD_TIMEOUT_MS);
253
+ unsubscribeProgress?.();
254
+ await synced.stop();
255
+ if (!reachedTip) {
256
+ // stop() persisted the partial state, so the next build resumes from it
257
+ // rather than restarting. Refuse to hand it out in the meantime.
258
+ onProgress?.('Pre-seed: reference did not reach chain tip in time — partial progress saved for the next attempt');
259
+ return null;
260
+ }
261
+ // Record the height this reference represents, so callers can refuse to
262
+ // seed wallets older than it. Read AFTER the sync finished: the tip may have
263
+ // advanced past what the reference actually applied, which overstates the
264
+ // height and therefore only ever makes the birthday check stricter.
265
+ try {
266
+ const tip = await new IndexerClient(network.indexerUrl).getBlock();
267
+ if (tip?.height)
268
+ await resolved.put(emptyRefHeightKey(network.id), String(tip.height));
269
+ }
270
+ catch {
271
+ // Without a height the reference is unusable — better than guessing.
272
+ onProgress?.('Pre-seed: could not record the reference height; reference will not be used');
273
+ }
274
+ // Re-read through the same usability check the warm path uses: reaching tip
275
+ // is necessary but the serialized cursors are what actually get copied, so
276
+ // verify them rather than trusting the sync's own verdict.
277
+ const states = await loadUsableRefStates(resolved, network.id);
278
+ if (!states) {
279
+ onProgress?.('Pre-seed: reference sync completed but its state carries no chain cursor');
280
+ return null;
281
+ }
282
+ onProgress?.('Pre-seed: reference wallet ready at chain tip');
283
+ return states;
284
+ }
285
+ catch (err) {
286
+ onProgress?.(`Pre-seed: reference sync failed — ${err}`);
287
+ return null;
288
+ }
289
+ }
290
+ /**
291
+ * Pre-seed a newly generated wallet's sync state from the empty reference.
292
+ *
293
+ * - Shielded: reference's Zswap tree + offset, new wallet's public keys, empty coins
294
+ * - Unshielded: new wallet's public key, reference's indexer cursor, empty UTXOs
295
+ * - Dust: reference state as-is with the new wallet's dust public key swapped in.
296
+ * Dust ledger events are global (the indexer streams `dustLedgerEvents` keyed
297
+ * by a global id), and a wallet holding no NIGHT has no designations of its
298
+ * own to preserve, so the reference's generation tree and cursor transfer
299
+ * directly. This is the expensive one to get right: without it dust walks the
300
+ * whole chain, which is minutes-to-an-hour where shielded takes seconds.
301
+ */
302
+ export function preSeedNewWallet(walletKeys, networkId, emptyRef) {
303
+ try {
304
+ setNetworkId(networkId);
305
+ // Keys arrive pre-derived (Option A) — no seed to re-derive from.
306
+ const shieldedSecretKeys = walletKeys.shieldedSecretKeys;
307
+ const ks = createKeystore(walletKeys.nightExternalKey, networkId);
308
+ const pk = PublicKey.fromKeyStore(ks);
309
+ // Parse reference states
310
+ const refSh = JSON.parse(emptyRef.shielded);
311
+ const refUn = JSON.parse(emptyRef.unshielded);
312
+ // Shielded: swap public keys, keep reference's tree + offset
313
+ const shieldedSnap = {
314
+ publicKeys: {
315
+ coinPublicKey: shieldedSecretKeys.coinPublicKey,
316
+ encryptionPublicKey: shieldedSecretKeys.encryptionPublicKey,
317
+ },
318
+ state: refSh.state,
319
+ protocolVersion: refSh.protocolVersion,
320
+ networkId,
321
+ coinHashes: {},
322
+ };
323
+ if (refSh.offset !== undefined)
324
+ shieldedSnap.offset = refSh.offset;
325
+ // Unshielded: swap public key, keep reference's indexer cursor
326
+ const unshieldedSnap = {
327
+ publicKey: { publicKey: pk.publicKey, addressHex: pk.addressHex, address: pk.address },
328
+ state: { availableUtxos: [], pendingUtxos: [] },
329
+ protocolVersion: refUn.protocolVersion,
330
+ networkId,
331
+ };
332
+ if (refUn.appliedId !== undefined)
333
+ unshieldedSnap.appliedId = refUn.appliedId;
334
+ // Dust: For a new wallet with no NIGHT UTXOs, the reference dust state is
335
+ // usable as-is — there are no wallet-specific designations to swap.
336
+ // The dust wallet just needs the global generation tree at chain tip to
337
+ // resume syncing from there instead of genesis.
338
+ let dustSnap;
339
+ try {
340
+ const refDust = JSON.parse(emptyRef.dust);
341
+ // Swap in the new wallet's dust key, keep everything else. The public
342
+ // key is a bigint; snapshots store it as a decimal string (JSON.stringify
343
+ // throws on a raw bigint) — hence the .toString().
344
+ dustSnap = JSON.stringify({
345
+ publicKey: { publicKey: walletKeys.dustSecretKey.publicKey.toString() },
346
+ state: refDust.state,
347
+ protocolVersion: refDust.protocolVersion,
348
+ networkId,
349
+ offset: refDust.offset,
350
+ });
351
+ }
352
+ catch {
353
+ // Dust preseed failure is non-fatal — wallet will sync dust from genesis
354
+ }
355
+ return {
356
+ shielded: JSON.stringify(shieldedSnap),
357
+ unshielded: JSON.stringify(unshieldedSnap),
358
+ dust: dustSnap,
359
+ };
360
+ }
361
+ catch {
362
+ return null;
363
+ }
364
+ }
365
+ //# sourceMappingURL=preseed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preseed.js","sourceRoot":"","sources":["../../src/sync/preseed.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ export interface SubProgressSnapshot {
2
+ /** Events/indices applied so far. */
3
+ applied: number;
4
+ /** Events/indices the chain currently has for this sub-wallet. */
5
+ total: number;
6
+ }
7
+ /** Which sub-wallet the reported percentage belongs to. */
8
+ export type SubWallet = 'shielded' | 'unshielded' | 'dust';
9
+ export interface OverallProgressInput {
10
+ shielded: SubProgressSnapshot;
11
+ unshielded: SubProgressSnapshot;
12
+ dust: SubProgressSnapshot;
13
+ shieldedSynced: boolean;
14
+ unshieldedSynced: boolean;
15
+ dustSynced: boolean;
16
+ /** The facade's own verdict: every sub-wallet strictly complete. */
17
+ synced: boolean;
18
+ /** Time since this sync started, for the ETA. 0 disables the estimate. */
19
+ elapsedMs: number;
20
+ }
21
+ /**
22
+ * Progress is the SLOWEST sub-wallet, never the shielded one alone.
23
+ *
24
+ * This used to read shielded indices only, on the stated assumption that shielded
25
+ * was the slowest. It is not — dust is, by two orders of magnitude: a full dust
26
+ * walk is ~1.4M events at a few hundred per second, where shielded covers the same
27
+ * range in under a minute. Observed consequence on preprod: a wallet reporting
28
+ * "100% (0s remaining)" with dust at 178,029/1,395,558 and roughly 69 minutes of
29
+ * work left — worse than reporting nothing, because it stops the user waiting.
30
+ */
31
+ export declare function overallSyncProgress(input: OverallProgressInput): {
32
+ percentage: number;
33
+ etaSeconds: number | null;
34
+ /**
35
+ * The sub-wallet the percentage came from — the slowest one, and null once
36
+ * everything is synced.
37
+ *
38
+ * Returned because reporting the minimum without saying whose it is produces
39
+ * a genuinely confusing log: a timeline reading "syncing 27%" beside a UI
40
+ * showing shielded and unshielded at 100% looks like a contradiction rather
41
+ * than like dust being the constraint.
42
+ */
43
+ slowest: SubWallet | null;
44
+ };
45
+ //# sourceMappingURL=progress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/sync/progress.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,mBAAmB;IAClC,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;CACf;AAED,2DAA2D;AAC3D,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,YAAY,GAAG,MAAM,CAAC;AAE3D,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,UAAU,EAAE,mBAAmB,CAAC;IAChC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;IACpB,oEAAoE;IACpE,MAAM,EAAE,OAAO,CAAC;IAChB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,oBAAoB,GAAG;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;;;;;;OAQG;IACH,OAAO,EAAE,SAAS,GAAG,IAAI,CAAC;CAC3B,CAoCA"}
@@ -0,0 +1,51 @@
1
+ // Overall sync progress, derived from the three sub-wallets' indices.
2
+ //
3
+ // WASM-free on purpose: wallet-sync.ts imports the ledger and the wallet SDK, so
4
+ // keeping this arithmetic in its own module lets it be unit-tested without
5
+ // loading WASM — the same split as types/tokens.ts and the extension's
6
+ // dust-heal.ts.
7
+ /**
8
+ * Progress is the SLOWEST sub-wallet, never the shielded one alone.
9
+ *
10
+ * This used to read shielded indices only, on the stated assumption that shielded
11
+ * was the slowest. It is not — dust is, by two orders of magnitude: a full dust
12
+ * walk is ~1.4M events at a few hundred per second, where shielded covers the same
13
+ * range in under a minute. Observed consequence on preprod: a wallet reporting
14
+ * "100% (0s remaining)" with dust at 178,029/1,395,558 and roughly 69 minutes of
15
+ * work left — worse than reporting nothing, because it stops the user waiting.
16
+ */
17
+ export function overallSyncProgress(input) {
18
+ if (input.synced)
19
+ return { percentage: 1, etaSeconds: 0, slowest: null };
20
+ // A sub-wallet with nothing relevant to apply (total 0) is complete, not
21
+ // stalled — count it as 1 so it cannot drag the minimum to zero. A fresh
22
+ // wallet's unshielded progress is legitimately 0/0.
23
+ const fraction = (sub, done) => {
24
+ if (done)
25
+ return 1;
26
+ return sub.total > 0 ? Math.min(1, sub.applied / sub.total) : 1;
27
+ };
28
+ const fractions = [
29
+ { sub: 'shielded', value: fraction(input.shielded, input.shieldedSynced) },
30
+ { sub: 'unshielded', value: fraction(input.unshielded, input.unshieldedSynced) },
31
+ { sub: 'dust', value: fraction(input.dust, input.dustSynced) },
32
+ ];
33
+ // Ties resolve to the earlier entry, so a fresh wallet where everything sits
34
+ // at 0 reports 'shielded' rather than an arbitrary one.
35
+ const binding = fractions.reduce((a, b) => (b.value < a.value ? b : a));
36
+ const slowest = binding.sub;
37
+ let percentage = binding.value;
38
+ // Never round up to 100% while not synced: rendering a near-complete fraction
39
+ // as "100% (0s remaining)" is the specific lie this function exists to remove.
40
+ if (percentage >= 0.995)
41
+ percentage = 0.99;
42
+ // ETA against the same fraction, so it reflects whichever sub-wallet is behind
43
+ // rather than one that finished a minute in.
44
+ let etaSeconds = null;
45
+ if (input.elapsedMs > 0 && percentage > 0.01) {
46
+ const totalEstMs = input.elapsedMs / percentage;
47
+ etaSeconds = Math.max(0, Math.round((totalEstMs - input.elapsedMs) / 1000));
48
+ }
49
+ return { percentage, etaSeconds, slowest };
50
+ }
51
+ //# sourceMappingURL=progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/sync/progress.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,iFAAiF;AACjF,2EAA2E;AAC3E,uEAAuE;AACvE,gBAAgB;AAyBhB;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAA2B;IAc7D,IAAI,KAAK,CAAC,MAAM;QAAE,OAAO,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAEzE,yEAAyE;IACzE,yEAAyE;IACzE,oDAAoD;IACpD,MAAM,QAAQ,GAAG,CAAC,GAAwB,EAAE,IAAa,EAAU,EAAE;QACnE,IAAI,IAAI;YAAE,OAAO,CAAC,CAAC;QACnB,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClE,CAAC,CAAC;IAEF,MAAM,SAAS,GAA6C;QAC1D,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,cAAc,CAAC,EAAE;QAC1E,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,gBAAgB,CAAC,EAAE;QAChF,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,CAAC,EAAE;KAC/D,CAAC;IACF,6EAA6E;IAC7E,wDAAwD;IACxD,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC;IAE5B,IAAI,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC;IAE/B,8EAA8E;IAC9E,+EAA+E;IAC/E,IAAI,UAAU,IAAI,KAAK;QAAE,UAAU,GAAG,IAAI,CAAC;IAE3C,+EAA+E;IAC/E,6CAA6C;IAC7C,IAAI,UAAU,GAAkB,IAAI,CAAC;IACrC,IAAI,KAAK,CAAC,SAAS,GAAG,CAAC,IAAI,UAAU,GAAG,IAAI,EAAE,CAAC;QAC7C,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,GAAG,UAAU,CAAC;QAChD,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IAC9E,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,53 @@
1
+ type Updateish<T = unknown> = {
2
+ readonly id: number | bigint | string;
3
+ readonly maxId: number | bigint | string;
4
+ readonly protocolVersion?: number | bigint;
5
+ } & T;
6
+ type WrappedUpdate<U> = {
7
+ readonly updates: ReadonlyArray<U>;
8
+ readonly [key: string]: unknown;
9
+ };
10
+ type ApplyUpdateFn<S, U> = (state: S, wrappedUpdate: WrappedUpdate<U>) => readonly [S, {
11
+ changes: unknown[];
12
+ protocolVersion: number;
13
+ }];
14
+ interface Capability<S, U> {
15
+ applyUpdate: ApplyUpdateFn<S, U>;
16
+ }
17
+ /**
18
+ * Walk the updates array and split into "already applied" vs "still to
19
+ * apply" against the wallet's current appliedIndex. The boundary event
20
+ * — the one whose id equals appliedIndex — counts as already applied.
21
+ */
22
+ declare function partitionByAppliedIndex<U extends Updateish>(updates: ReadonlyArray<U>, appliedIndex: bigint): {
23
+ fresh: ReadonlyArray<U>;
24
+ droppedCount: number;
25
+ };
26
+ declare function makeDedupingApplyUpdate<S extends {
27
+ progress: {
28
+ appliedIndex: bigint;
29
+ [k: string]: unknown;
30
+ };
31
+ protocolVersion: number | bigint;
32
+ }, U extends Updateish>(base: Capability<S, U>, updateProgress: (state: S, patch: {
33
+ highestRelevantWalletIndex: bigint;
34
+ isConnected: boolean;
35
+ }) => S): ApplyUpdateFn<S, U>;
36
+ /**
37
+ * Build a V1Builder for the shielded wallet whose syncCapability filters
38
+ * already-applied events before handing them to the SDK's
39
+ * makeEventsSyncCapability. Pass to CustomShieldedWallet(cfg, builder).
40
+ */
41
+ export declare function dedupingShieldedBuilder(): unknown;
42
+ /**
43
+ * Build a V1Builder for the dust wallet whose syncCapability filters
44
+ * already-applied events before handing them to the SDK's
45
+ * makeDefaultSyncCapability. Pass to CustomDustWallet(cfg, builder).
46
+ */
47
+ export declare function dedupingDustBuilder(): unknown;
48
+ export declare const __TEST_ONLY__: {
49
+ partitionByAppliedIndex: typeof partitionByAppliedIndex;
50
+ makeDedupingApplyUpdate: typeof makeDedupingApplyUpdate;
51
+ };
52
+ export {};
53
+ //# sourceMappingURL=sdk-dedup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk-dedup.d.ts","sourceRoot":"","sources":["../../src/sync/sdk-dedup.ts"],"names":[],"mappings":"AAuCA,KAAK,SAAS,CAAC,CAAC,GAAG,OAAO,IAAI;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACzC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5C,GAAG,CAAC,CAAC;AAEN,KAAK,aAAa,CAAC,CAAC,IAAI;IACtB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACnC,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC,CAAC;AAEF,KAAK,aAAa,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,aAAa,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,EAAE;IAAC,OAAO,EAAE,OAAO,EAAE,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAC,CAAC,CAAC;AAEtI,UAAU,UAAU,CAAC,CAAC,EAAE,CAAC;IACvB,WAAW,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CAClC;AAED;;;;GAIG;AACH,iBAAS,uBAAuB,CAAC,CAAC,SAAS,SAAS,EAClD,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,EACzB,YAAY,EAAE,MAAM,GACnB;IAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAC,CAMjD;AAED,iBAAS,uBAAuB,CAAC,CAAC,SAAS;IAAC,QAAQ,EAAE;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAC,CAAC;IAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAAA;CAAC,EAAE,CAAC,SAAS,SAAS,EACxJ,IAAI,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,EACtB,cAAc,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE;IAAC,0BAA0B,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAC,KAAK,CAAC,GACjG,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CA6BrB;AAQD;;;;GAIG;AAUH,wBAAgB,uBAAuB,IAAI,OAAO,CAkBjD;AAED;;;;GAIG;AAEH,wBAAgB,mBAAmB,IAAI,OAAO,CAoB7C;AAID,eAAO,MAAM,aAAa;;;CAGzB,CAAC"}