@argonprotocol/mainchain 1.0.18 → 1.1.0-rc.2

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 (156) hide show
  1. package/README.md +110 -3
  2. package/lib/cli.cjs +3078 -0
  3. package/lib/cli.cjs.map +1 -0
  4. package/lib/cli.d.cts +1 -0
  5. package/lib/cli.d.ts +1 -0
  6. package/lib/cli.js +3073 -0
  7. package/lib/cli.js.map +1 -0
  8. package/lib/clis/index.cjs +3097 -0
  9. package/lib/clis/index.cjs.map +1 -0
  10. package/lib/clis/index.d.cts +26 -0
  11. package/lib/clis/index.d.ts +26 -0
  12. package/lib/clis/index.js +3077 -0
  13. package/lib/clis/index.js.map +1 -0
  14. package/lib/index.cjs +2402 -0
  15. package/lib/index.cjs.map +1 -0
  16. package/lib/index.d.cts +11542 -0
  17. package/lib/index.d.ts +11542 -0
  18. package/lib/index.js +2333 -0
  19. package/lib/index.js.map +1 -0
  20. package/lib/test-utils/index.cjs +2933 -0
  21. package/lib/test-utils/index.cjs.map +1 -0
  22. package/lib/test-utils/index.d.cts +180 -0
  23. package/lib/test-utils/index.d.ts +180 -0
  24. package/lib/test-utils/index.js +2915 -0
  25. package/lib/test-utils/index.js.map +1 -0
  26. package/package.json +50 -45
  27. package/lib/cjs/WageProtector.d.ts +0 -37
  28. package/lib/cjs/WageProtector.js +0 -64
  29. package/lib/cjs/WageProtector.js.map +0 -1
  30. package/lib/cjs/__test__/WageProtector.test.d.ts +0 -1
  31. package/lib/cjs/__test__/WageProtector.test.js +0 -28
  32. package/lib/cjs/__test__/WageProtector.test.js.map +0 -1
  33. package/lib/cjs/examples/decodePkcs.d.ts +0 -1
  34. package/lib/cjs/examples/decodePkcs.js +0 -48
  35. package/lib/cjs/examples/decodePkcs.js.map +0 -1
  36. package/lib/cjs/examples/findSs58Formats.d.ts +0 -1
  37. package/lib/cjs/examples/findSs58Formats.js +0 -42
  38. package/lib/cjs/examples/findSs58Formats.js.map +0 -1
  39. package/lib/cjs/index.d.ts +0 -32
  40. package/lib/cjs/index.js +0 -60
  41. package/lib/cjs/index.js.map +0 -1
  42. package/lib/cjs/interfaces/augment-api-consts.d.ts +0 -442
  43. package/lib/cjs/interfaces/augment-api-consts.js +0 -8
  44. package/lib/cjs/interfaces/augment-api-consts.js.map +0 -1
  45. package/lib/cjs/interfaces/augment-api-errors.d.ts +0 -919
  46. package/lib/cjs/interfaces/augment-api-errors.js +0 -8
  47. package/lib/cjs/interfaces/augment-api-errors.js.map +0 -1
  48. package/lib/cjs/interfaces/augment-api-events.d.ts +0 -1675
  49. package/lib/cjs/interfaces/augment-api-events.js +0 -8
  50. package/lib/cjs/interfaces/augment-api-events.js.map +0 -1
  51. package/lib/cjs/interfaces/augment-api-query.d.ts +0 -988
  52. package/lib/cjs/interfaces/augment-api-query.js +0 -8
  53. package/lib/cjs/interfaces/augment-api-query.js.map +0 -1
  54. package/lib/cjs/interfaces/augment-api-rpc.d.ts +0 -683
  55. package/lib/cjs/interfaces/augment-api-rpc.js +0 -8
  56. package/lib/cjs/interfaces/augment-api-rpc.js.map +0 -1
  57. package/lib/cjs/interfaces/augment-api-runtime.d.ts +0 -204
  58. package/lib/cjs/interfaces/augment-api-runtime.js +0 -8
  59. package/lib/cjs/interfaces/augment-api-runtime.js.map +0 -1
  60. package/lib/cjs/interfaces/augment-api-tx.d.ts +0 -1605
  61. package/lib/cjs/interfaces/augment-api-tx.js +0 -8
  62. package/lib/cjs/interfaces/augment-api-tx.js.map +0 -1
  63. package/lib/cjs/interfaces/augment-api.d.ts +0 -7
  64. package/lib/cjs/interfaces/augment-api.js +0 -12
  65. package/lib/cjs/interfaces/augment-api.js.map +0 -1
  66. package/lib/cjs/interfaces/augment-types.d.ts +0 -1316
  67. package/lib/cjs/interfaces/augment-types.js +0 -8
  68. package/lib/cjs/interfaces/augment-types.js.map +0 -1
  69. package/lib/cjs/interfaces/definitions.d.ts +0 -0
  70. package/lib/cjs/interfaces/definitions.js +0 -2
  71. package/lib/cjs/interfaces/definitions.js.map +0 -1
  72. package/lib/cjs/interfaces/lookup.d.ts +0 -2999
  73. package/lib/cjs/interfaces/lookup.js +0 -3262
  74. package/lib/cjs/interfaces/lookup.js.map +0 -1
  75. package/lib/cjs/interfaces/registry.d.ts +0 -250
  76. package/lib/cjs/interfaces/registry.js +0 -8
  77. package/lib/cjs/interfaces/registry.js.map +0 -1
  78. package/lib/cjs/interfaces/types-lookup.d.ts +0 -2974
  79. package/lib/cjs/interfaces/types-lookup.js +0 -8
  80. package/lib/cjs/interfaces/types-lookup.js.map +0 -1
  81. package/lib/cjs/package.json +0 -3
  82. package/lib/esm/WageProtector.d.ts +0 -37
  83. package/lib/esm/WageProtector.js +0 -60
  84. package/lib/esm/WageProtector.js.map +0 -1
  85. package/lib/esm/__test__/WageProtector.test.d.ts +0 -1
  86. package/lib/esm/__test__/WageProtector.test.js +0 -26
  87. package/lib/esm/__test__/WageProtector.test.js.map +0 -1
  88. package/lib/esm/examples/decodePkcs.d.ts +0 -1
  89. package/lib/esm/examples/decodePkcs.js +0 -45
  90. package/lib/esm/examples/decodePkcs.js.map +0 -1
  91. package/lib/esm/examples/findSs58Formats.d.ts +0 -1
  92. package/lib/esm/examples/findSs58Formats.js +0 -39
  93. package/lib/esm/examples/findSs58Formats.js.map +0 -1
  94. package/lib/esm/index.d.ts +0 -32
  95. package/lib/esm/index.js +0 -50
  96. package/lib/esm/index.js.map +0 -1
  97. package/lib/esm/interfaces/augment-api-consts.d.ts +0 -442
  98. package/lib/esm/interfaces/augment-api-consts.js +0 -6
  99. package/lib/esm/interfaces/augment-api-consts.js.map +0 -1
  100. package/lib/esm/interfaces/augment-api-errors.d.ts +0 -919
  101. package/lib/esm/interfaces/augment-api-errors.js +0 -6
  102. package/lib/esm/interfaces/augment-api-errors.js.map +0 -1
  103. package/lib/esm/interfaces/augment-api-events.d.ts +0 -1675
  104. package/lib/esm/interfaces/augment-api-events.js +0 -6
  105. package/lib/esm/interfaces/augment-api-events.js.map +0 -1
  106. package/lib/esm/interfaces/augment-api-query.d.ts +0 -988
  107. package/lib/esm/interfaces/augment-api-query.js +0 -6
  108. package/lib/esm/interfaces/augment-api-query.js.map +0 -1
  109. package/lib/esm/interfaces/augment-api-rpc.d.ts +0 -683
  110. package/lib/esm/interfaces/augment-api-rpc.js +0 -6
  111. package/lib/esm/interfaces/augment-api-rpc.js.map +0 -1
  112. package/lib/esm/interfaces/augment-api-runtime.d.ts +0 -204
  113. package/lib/esm/interfaces/augment-api-runtime.js +0 -6
  114. package/lib/esm/interfaces/augment-api-runtime.js.map +0 -1
  115. package/lib/esm/interfaces/augment-api-tx.d.ts +0 -1605
  116. package/lib/esm/interfaces/augment-api-tx.js +0 -6
  117. package/lib/esm/interfaces/augment-api-tx.js.map +0 -1
  118. package/lib/esm/interfaces/augment-api.d.ts +0 -7
  119. package/lib/esm/interfaces/augment-api.js +0 -10
  120. package/lib/esm/interfaces/augment-api.js.map +0 -1
  121. package/lib/esm/interfaces/augment-types.d.ts +0 -1316
  122. package/lib/esm/interfaces/augment-types.js +0 -6
  123. package/lib/esm/interfaces/augment-types.js.map +0 -1
  124. package/lib/esm/interfaces/definitions.d.ts +0 -0
  125. package/lib/esm/interfaces/definitions.js +0 -2
  126. package/lib/esm/interfaces/definitions.js.map +0 -1
  127. package/lib/esm/interfaces/lookup.d.ts +0 -2999
  128. package/lib/esm/interfaces/lookup.js +0 -3260
  129. package/lib/esm/interfaces/lookup.js.map +0 -1
  130. package/lib/esm/interfaces/registry.d.ts +0 -250
  131. package/lib/esm/interfaces/registry.js +0 -6
  132. package/lib/esm/interfaces/registry.js.map +0 -1
  133. package/lib/esm/interfaces/types-lookup.d.ts +0 -2974
  134. package/lib/esm/interfaces/types-lookup.js +0 -6
  135. package/lib/esm/interfaces/types-lookup.js.map +0 -1
  136. package/lib/tsconfig-cjs.tsbuildinfo +0 -1
  137. package/lib/tsconfig-types.tsbuildinfo +0 -1
  138. package/lib/tsconfig.tsbuildinfo +0 -1
  139. package/lib/types/WageProtector.d.ts +0 -37
  140. package/lib/types/__test__/WageProtector.test.d.ts +0 -1
  141. package/lib/types/examples/decodePkcs.d.ts +0 -1
  142. package/lib/types/examples/findSs58Formats.d.ts +0 -1
  143. package/lib/types/index.d.ts +0 -32
  144. package/lib/types/interfaces/augment-api-consts.d.ts +0 -442
  145. package/lib/types/interfaces/augment-api-errors.d.ts +0 -919
  146. package/lib/types/interfaces/augment-api-events.d.ts +0 -1675
  147. package/lib/types/interfaces/augment-api-query.d.ts +0 -988
  148. package/lib/types/interfaces/augment-api-rpc.d.ts +0 -683
  149. package/lib/types/interfaces/augment-api-runtime.d.ts +0 -204
  150. package/lib/types/interfaces/augment-api-tx.d.ts +0 -1605
  151. package/lib/types/interfaces/augment-api.d.ts +0 -7
  152. package/lib/types/interfaces/augment-types.d.ts +0 -1316
  153. package/lib/types/interfaces/definitions.d.ts +0 -0
  154. package/lib/types/interfaces/lookup.d.ts +0 -2999
  155. package/lib/types/interfaces/registry.d.ts +0 -250
  156. package/lib/types/interfaces/types-lookup.d.ts +0 -2974
package/lib/index.js ADDED
@@ -0,0 +1,2333 @@
1
+ // src/interfaces/augment-api-consts.ts
2
+ import "@polkadot/api-base/types/consts";
3
+
4
+ // src/interfaces/augment-api-errors.ts
5
+ import "@polkadot/api-base/types/errors";
6
+
7
+ // src/interfaces/augment-api-events.ts
8
+ import "@polkadot/api-base/types/events";
9
+
10
+ // src/interfaces/augment-api-query.ts
11
+ import "@polkadot/api-base/types/storage";
12
+
13
+ // src/interfaces/augment-api-tx.ts
14
+ import "@polkadot/api-base/types/submittable";
15
+
16
+ // src/interfaces/augment-api-rpc.ts
17
+ import "@polkadot/rpc-core/types/jsonrpc";
18
+
19
+ // src/interfaces/augment-api-runtime.ts
20
+ import "@polkadot/api-base/types/calls";
21
+
22
+ // src/interfaces/augment-types.ts
23
+ import "@polkadot/types/types/registry";
24
+
25
+ // src/index.ts
26
+ import { ApiPromise, HttpProvider, Keyring, WsProvider } from "@polkadot/api";
27
+ import {
28
+ cryptoWaitReady as cryptoWaitReady2,
29
+ decodeAddress,
30
+ mnemonicGenerate
31
+ } from "@polkadot/util-crypto";
32
+
33
+ // src/WageProtector.ts
34
+ var WageProtector = class _WageProtector {
35
+ constructor(latestCpi) {
36
+ this.latestCpi = latestCpi;
37
+ }
38
+ /**
39
+ * Converts the base wage to the current wage using the latest CPI snapshot
40
+ *
41
+ * @param baseWage The base wage to convert
42
+ * @returns The protected wage
43
+ */
44
+ getProtectedWage(baseWage) {
45
+ return baseWage * this.latestCpi.argonUsdTargetPrice / this.latestCpi.argonUsdPrice;
46
+ }
47
+ /**
48
+ * Subscribes to the current CPI and calls the callback function whenever the CPI changes
49
+ * @param client The ArgonClient to use
50
+ * @param callback The callback function to call when the CPI changes
51
+ * @returns An object with an unsubscribe function that can be called to stop the subscription
52
+ */
53
+ static async subscribe(client, callback) {
54
+ const unsubscribe = await client.query.priceIndex.current(async (cpi) => {
55
+ if (cpi.isNone) {
56
+ return;
57
+ }
58
+ const finalizedBlock = await client.rpc.chain.getFinalizedHead();
59
+ callback(
60
+ new _WageProtector({
61
+ argonUsdTargetPrice: cpi.value.argonUsdTargetPrice.toBigInt(),
62
+ argonUsdPrice: cpi.value.argonUsdPrice.toBigInt(),
63
+ finalizedBlock: finalizedBlock.toU8a(),
64
+ tick: cpi.value.tick.toBigInt()
65
+ })
66
+ );
67
+ });
68
+ return { unsubscribe };
69
+ }
70
+ /**
71
+ * Creates a new WageProtector instance by subscribing to the current CPI and waiting for the first value
72
+ * @param client The ArgonClient to use
73
+ */
74
+ static async create(client) {
75
+ return new Promise(async (resolve, reject) => {
76
+ try {
77
+ const { unsubscribe } = await _WageProtector.subscribe(client, (x) => {
78
+ resolve(x);
79
+ unsubscribe();
80
+ });
81
+ } catch (e) {
82
+ reject(e);
83
+ }
84
+ });
85
+ }
86
+ };
87
+
88
+ // src/utils.ts
89
+ import BigNumber, * as BN from "bignumber.js";
90
+ var { ROUND_FLOOR } = BN;
91
+ function formatArgons(x) {
92
+ const isNegative = x < 0;
93
+ let format = BigNumber(x.toString()).abs().div(1e6).toFormat(2, ROUND_FLOOR);
94
+ if (format.endsWith(".00")) {
95
+ format = format.slice(0, -3);
96
+ }
97
+ return `${isNegative ? "-" : ""}\u20B3${format}`;
98
+ }
99
+ function formatPercent(x) {
100
+ if (!x) return "na";
101
+ return `${x.times(100).decimalPlaces(3)}%`;
102
+ }
103
+ function filterUndefined(obj) {
104
+ return Object.fromEntries(
105
+ Object.entries(obj).filter(
106
+ ([_, value]) => value !== void 0 && value !== null
107
+ )
108
+ );
109
+ }
110
+ async function gettersToObject(obj) {
111
+ if (obj === null || obj === void 0 || typeof obj !== "object") return obj;
112
+ const keys = [];
113
+ for (const key in obj) {
114
+ keys.push(key);
115
+ }
116
+ if (Symbol.iterator in obj) {
117
+ const iterableToArray = [];
118
+ for (const item of obj) {
119
+ iterableToArray.push(await gettersToObject(item));
120
+ }
121
+ return iterableToArray;
122
+ }
123
+ const result = {};
124
+ for (const key of keys) {
125
+ const descriptor = Object.getOwnPropertyDescriptor(obj, key);
126
+ if (descriptor && typeof descriptor.value === "function") {
127
+ continue;
128
+ }
129
+ const value = descriptor && descriptor.get ? descriptor.get.call(obj) : obj[key];
130
+ if (typeof value === "function") continue;
131
+ result[key] = await gettersToObject(value);
132
+ }
133
+ return result;
134
+ }
135
+ function convertFixedU128ToBigNumber(fixedU128) {
136
+ const decimalFactor = new BigNumber(10).pow(new BigNumber(18));
137
+ const rawValue = new BigNumber(fixedU128.toString());
138
+ return rawValue.div(decimalFactor);
139
+ }
140
+ function convertPermillToBigNumber(permill) {
141
+ const decimalFactor = new BigNumber(1e6);
142
+ const rawValue = new BigNumber(permill.toString());
143
+ return rawValue.div(decimalFactor);
144
+ }
145
+ function eventDataToJson(event) {
146
+ const obj = {};
147
+ event.data.forEach((data, index) => {
148
+ const name = event.data.names?.[index];
149
+ obj[name ?? `${index}`] = data.toJSON();
150
+ });
151
+ return obj;
152
+ }
153
+ function dispatchErrorToString(client, error) {
154
+ let message = error.toString();
155
+ if (error.isModule) {
156
+ const decoded = client.registry.findMetaError(error.asModule);
157
+ const { docs, name, section } = decoded;
158
+ message = `${section}.${name}: ${docs.join(" ")}`;
159
+ }
160
+ return message;
161
+ }
162
+ function checkForExtrinsicSuccess(events, client) {
163
+ return new Promise((resolve, reject) => {
164
+ for (const { event } of events) {
165
+ if (client.events.system.ExtrinsicSuccess.is(event)) {
166
+ resolve();
167
+ } else if (client.events.system.ExtrinsicFailed.is(event)) {
168
+ const [dispatchError] = event.data;
169
+ let errorInfo = dispatchError.toString();
170
+ if (dispatchError.isModule) {
171
+ const decoded = client.registry.findMetaError(dispatchError.asModule);
172
+ errorInfo = `${decoded.section}.${decoded.name}`;
173
+ }
174
+ reject(
175
+ new Error(
176
+ `${event.section}.${event.method}:: ExtrinsicFailed:: ${errorInfo}`
177
+ )
178
+ );
179
+ }
180
+ }
181
+ });
182
+ }
183
+ var JsonExt = class {
184
+ static stringify(obj, space) {
185
+ return JSON.stringify(
186
+ obj,
187
+ (_, v) => typeof v === "bigint" ? `${v}n` : v,
188
+ space
189
+ );
190
+ }
191
+ static parse(str) {
192
+ return JSON.parse(str, (_, v) => {
193
+ if (typeof v === "string" && v.endsWith("n")) {
194
+ return BigInt(v.slice(0, -1));
195
+ }
196
+ return v;
197
+ });
198
+ }
199
+ };
200
+
201
+ // src/TxSubmitter.ts
202
+ function logExtrinsicResult(result) {
203
+ if (process.env.DEBUG) {
204
+ const json = result.status.toJSON();
205
+ const status = Object.keys(json)[0];
206
+ console.debug('Transaction update: "%s"', status, json[status]);
207
+ }
208
+ }
209
+ var TxSubmitter = class {
210
+ constructor(client, tx, pair) {
211
+ this.client = client;
212
+ this.tx = tx;
213
+ this.pair = pair;
214
+ }
215
+ async feeEstimate(tip) {
216
+ const { partialFee } = await this.tx.paymentInfo(this.pair, { tip });
217
+ return partialFee.toBigInt();
218
+ }
219
+ async canAfford(options = {}) {
220
+ const { tip, unavailableBalance } = options;
221
+ const account = await this.client.query.system.account(this.pair.address);
222
+ let availableBalance = account.data.free.toBigInt();
223
+ if (unavailableBalance) {
224
+ availableBalance -= unavailableBalance;
225
+ }
226
+ const existentialDeposit = options.includeExistentialDeposit ? this.client.consts.balances.existentialDeposit.toBigInt() : 0n;
227
+ const fees = await this.feeEstimate(tip);
228
+ const totalCharge = fees + (tip ?? 0n);
229
+ const canAfford = availableBalance > totalCharge + existentialDeposit;
230
+ return { canAfford, availableBalance, txFee: fees };
231
+ }
232
+ async submit(options = {}) {
233
+ const { logResults } = options;
234
+ const result = new TxResult(this.client, logResults);
235
+ let toHuman = this.tx.toHuman().method;
236
+ let txString = [];
237
+ let api = formatCall(toHuman);
238
+ const args = [];
239
+ if (api === "proxy.proxy") {
240
+ toHuman = toHuman.args.call;
241
+ txString.push("Proxy");
242
+ api = formatCall(toHuman);
243
+ }
244
+ if (api.startsWith("utility.batch")) {
245
+ const calls = toHuman.args.calls.map(formatCall).join(", ");
246
+ txString.push(`Batch[${calls}]`);
247
+ } else {
248
+ txString.push(api);
249
+ args.push(toHuman.args);
250
+ }
251
+ args.unshift(txString.join("->"));
252
+ if (options.useLatestNonce && !options.nonce) {
253
+ options.nonce = await this.client.rpc.system.accountNextIndex(
254
+ this.pair.address
255
+ );
256
+ }
257
+ console.log("Submitting transaction:", ...args);
258
+ await this.tx.signAndSend(this.pair, options, result.onResult.bind(result));
259
+ if (options.waitForBlock) {
260
+ await result.inBlockPromise;
261
+ }
262
+ return result;
263
+ }
264
+ };
265
+ function formatCall(call) {
266
+ return `${call.section}.${call.method}`;
267
+ }
268
+ var TxResult = class {
269
+ constructor(client, shouldLog = false) {
270
+ this.client = client;
271
+ this.shouldLog = shouldLog;
272
+ this.inBlockPromise = new Promise((resolve, reject) => {
273
+ this.inBlockResolve = resolve;
274
+ this.inBlockReject = reject;
275
+ });
276
+ this.finalizedPromise = new Promise((resolve, reject) => {
277
+ this.finalizedResolve = resolve;
278
+ this.finalizedReject = reject;
279
+ });
280
+ this.inBlockPromise.catch(() => {
281
+ });
282
+ this.finalizedPromise.catch(() => {
283
+ });
284
+ }
285
+ inBlockPromise;
286
+ finalizedPromise;
287
+ status;
288
+ events = [];
289
+ /**
290
+ * The index of the batch that was interrupted, if any.
291
+ */
292
+ batchInterruptedIndex;
293
+ includedInBlock;
294
+ /**
295
+ * The final fee paid for the transaction, including the fee tip.
296
+ */
297
+ finalFee;
298
+ /**
299
+ * The fee tip paid for the transaction.
300
+ */
301
+ finalFeeTip;
302
+ inBlockResolve;
303
+ inBlockReject;
304
+ finalizedResolve;
305
+ finalizedReject;
306
+ onResult(result) {
307
+ this.status = result.status;
308
+ if (this.shouldLog) {
309
+ logExtrinsicResult(result);
310
+ }
311
+ const { events, status, dispatchError, isFinalized } = result;
312
+ if (status.isInBlock) {
313
+ this.includedInBlock = status.asInBlock.toU8a();
314
+ let encounteredError = dispatchError;
315
+ let batchErrorIndex;
316
+ for (const event of events) {
317
+ this.events.push(event.event);
318
+ if (this.client.events.utility.BatchInterrupted.is(event.event)) {
319
+ batchErrorIndex = event.event.data[0].toNumber();
320
+ this.batchInterruptedIndex = batchErrorIndex;
321
+ encounteredError = event.event.data[1];
322
+ }
323
+ if (this.client.events.transactionPayment.TransactionFeePaid.is(
324
+ event.event
325
+ )) {
326
+ const [_who, actualFee, tip] = event.event.data;
327
+ this.finalFee = actualFee.toBigInt();
328
+ this.finalFeeTip = tip.toBigInt();
329
+ }
330
+ }
331
+ if (encounteredError) {
332
+ const error = dispatchErrorToString(this.client, encounteredError);
333
+ if (batchErrorIndex) {
334
+ this.reject(
335
+ new Error(`Error in batch#${batchErrorIndex}: ${error.toString()}`)
336
+ );
337
+ }
338
+ this.reject(new Error(`Transaction failed: ${error}`));
339
+ } else {
340
+ this.inBlockResolve(status.asInBlock.toU8a());
341
+ }
342
+ }
343
+ if (isFinalized) {
344
+ this.finalizedResolve(status.asFinalized);
345
+ }
346
+ }
347
+ reject(error) {
348
+ this.inBlockReject(error);
349
+ this.finalizedReject(error);
350
+ }
351
+ };
352
+
353
+ // src/AccountRegistry.ts
354
+ var AccountRegistry = class _AccountRegistry {
355
+ namedAccounts = /* @__PURE__ */ new Map();
356
+ me = "me";
357
+ constructor(name) {
358
+ if (name) {
359
+ this.me = name;
360
+ }
361
+ }
362
+ getName(address) {
363
+ return this.namedAccounts.get(address);
364
+ }
365
+ register(address, name) {
366
+ this.namedAccounts.set(address, name);
367
+ }
368
+ static factory = (name) => new _AccountRegistry(name);
369
+ };
370
+
371
+ // src/Accountset.ts
372
+ import * as process2 from "node:process";
373
+
374
+ // src/AccountMiners.ts
375
+ import EventEmitter2 from "node:events";
376
+
377
+ // src/BlockWatch.ts
378
+ import EventEmitter from "node:events";
379
+ function getTickFromHeader(client, header) {
380
+ for (const x of header.digest.logs) {
381
+ if (x.isPreRuntime) {
382
+ const [engineId, data] = x.asPreRuntime;
383
+ if (engineId.toString() === "aura") {
384
+ return client.createType("u64", data).toNumber();
385
+ }
386
+ }
387
+ }
388
+ return void 0;
389
+ }
390
+ function getAuthorFromHeader(client, header) {
391
+ for (const x of header.digest.logs) {
392
+ if (x.isPreRuntime) {
393
+ const [engineId, data] = x.asPreRuntime;
394
+ if (engineId.toString() === "pow_") {
395
+ return client.createType("AccountId32", data).toHuman();
396
+ }
397
+ }
398
+ }
399
+ return void 0;
400
+ }
401
+ var BlockWatch = class {
402
+ constructor(mainchain, options = {}) {
403
+ this.mainchain = mainchain;
404
+ this.options = options;
405
+ this.options.shouldLog ??= true;
406
+ this.options.finalizedBlocks ??= false;
407
+ }
408
+ events = new EventEmitter();
409
+ obligationsById = {};
410
+ obligationIdByUtxoId = {};
411
+ unsubscribe;
412
+ stop() {
413
+ if (this.unsubscribe) {
414
+ this.unsubscribe();
415
+ this.unsubscribe = void 0;
416
+ }
417
+ }
418
+ async start() {
419
+ await this.watchBlocks();
420
+ }
421
+ async watchBlocks() {
422
+ const client = await this.mainchain;
423
+ const onBlock = async (header) => {
424
+ try {
425
+ await this.processBlock(header);
426
+ } catch (e) {
427
+ console.error("Error processing block", e);
428
+ }
429
+ };
430
+ if (this.options.finalizedBlocks) {
431
+ this.unsubscribe = await client.rpc.chain.subscribeFinalizedHeads(onBlock);
432
+ } else {
433
+ this.unsubscribe = await client.rpc.chain.subscribeNewHeads(onBlock);
434
+ }
435
+ }
436
+ async processBlock(header) {
437
+ const client = await this.mainchain;
438
+ if (this.options.shouldLog) {
439
+ console.log(`-------------------------------------
440
+ BLOCK #${header.number}, ${header.hash.toHuman()}`);
441
+ }
442
+ const blockHash = header.hash;
443
+ const api = await client.at(blockHash);
444
+ const isBlockVote = await api.query.blockSeal.isBlockFromVoteSeal();
445
+ if (!isBlockVote) {
446
+ console.warn("> Compute reactivated!");
447
+ }
448
+ const events = await api.query.system.events();
449
+ const reloadVaults = /* @__PURE__ */ new Set();
450
+ let block = void 0;
451
+ for (const { event, phase } of events) {
452
+ const data = eventDataToJson(event);
453
+ if (data.vaultId) {
454
+ const vaultId = data.vaultId;
455
+ reloadVaults.add(vaultId);
456
+ }
457
+ let logEvent = false;
458
+ if (event.section === "liquidityPools") {
459
+ if (client.events.liquidityPools.BidPoolDistributed.is(event)) {
460
+ const { bidPoolBurned, bidPoolDistributed } = event.data;
461
+ data.burned = formatArgons(bidPoolBurned.toBigInt());
462
+ data.distributed = formatArgons(bidPoolDistributed.toBigInt());
463
+ logEvent = true;
464
+ } else if (client.events.liquidityPools.NextBidPoolCapitalLocked.is(event)) {
465
+ const { totalActivatedCapital } = event.data;
466
+ data.totalActivatedCapital = formatArgons(
467
+ totalActivatedCapital.toBigInt()
468
+ );
469
+ logEvent = true;
470
+ }
471
+ } else if (event.section === "bitcoinLocks") {
472
+ if (client.events.bitcoinLocks.BitcoinLockCreated.is(event)) {
473
+ const { lockPrice, utxoId, accountId, vaultId } = event.data;
474
+ this.obligationsById[utxoId.toNumber()] = {
475
+ vaultId: vaultId.toNumber(),
476
+ amount: lockPrice.toBigInt()
477
+ };
478
+ data.lockPrice = formatArgons(lockPrice.toBigInt());
479
+ data.accountId = accountId.toHuman();
480
+ reloadVaults.add(vaultId.toNumber());
481
+ }
482
+ logEvent = true;
483
+ } else if (event.section === "mint") {
484
+ logEvent = true;
485
+ if (client.events.mint.MiningMint.is(event)) {
486
+ const { amount } = event.data;
487
+ data.amount = formatArgons(amount.toBigInt());
488
+ }
489
+ } else if (event.section === "miningSlot") {
490
+ logEvent = true;
491
+ if (client.events.miningSlot.SlotBidderAdded.is(event)) {
492
+ data.amount = formatArgons(event.data.bidAmount.toBigInt());
493
+ this.events.emit("mining-bid", header, {
494
+ amount: event.data.bidAmount.toBigInt(),
495
+ accountId: event.data.accountId.toString()
496
+ });
497
+ } else if (client.events.miningSlot.SlotBidderDropped.is(event)) {
498
+ this.events.emit("mining-bid-ousted", header, {
499
+ accountId: event.data.accountId.toString(),
500
+ preservedArgonotHold: event.data.preservedArgonotHold.toPrimitive()
501
+ });
502
+ }
503
+ } else if (event.section === "bitcoinUtxos") {
504
+ logEvent = true;
505
+ if (client.events.bitcoinUtxos.UtxoVerified.is(event)) {
506
+ const { utxoId } = event.data;
507
+ const details = await this.getBitcoinLockDetails(
508
+ utxoId.toNumber(),
509
+ blockHash
510
+ );
511
+ this.events.emit("bitcoin-verified", header, {
512
+ utxoId: utxoId.toNumber(),
513
+ vaultId: details.vaultId,
514
+ amount: details.amount
515
+ });
516
+ data.amount = formatArgons(details.amount);
517
+ reloadVaults.add(details.vaultId);
518
+ }
519
+ } else if (event.section === "system") {
520
+ if (client.events.system.ExtrinsicFailed.is(event)) {
521
+ const { dispatchError } = event.data;
522
+ if (dispatchError.isModule) {
523
+ const decoded = api.registry.findMetaError(dispatchError.asModule);
524
+ const { name, section } = decoded;
525
+ block ??= await client.rpc.chain.getBlock(header.hash);
526
+ const extrinsicIndex = phase.asApplyExtrinsic.toNumber();
527
+ const ext = block.block.extrinsics[extrinsicIndex];
528
+ if (this.options.shouldLog) {
529
+ console.log(
530
+ `> [Failed Tx] ${section}.${name} -> ${ext.method.section}.${ext.method.method} (nonce=${ext.nonce})`,
531
+ ext.toHuman()?.method?.args
532
+ );
533
+ }
534
+ } else {
535
+ if (this.options.shouldLog) {
536
+ console.log(`x [Failed Tx] ${dispatchError.toJSON()}`);
537
+ }
538
+ }
539
+ }
540
+ }
541
+ if (this.options.shouldLog && logEvent) {
542
+ console.log(`> ${event.section}.${event.method}`, data);
543
+ }
544
+ this.events.emit("event", header, event);
545
+ }
546
+ if (reloadVaults.size)
547
+ this.events.emit("vaults-updated", header, reloadVaults);
548
+ const tick = getTickFromHeader(client, header);
549
+ const author = getAuthorFromHeader(client, header);
550
+ this.events.emit(
551
+ "block",
552
+ header,
553
+ { tick, author },
554
+ events.map((x) => x.event)
555
+ );
556
+ }
557
+ async getBitcoinLockDetails(utxoId, blockHash) {
558
+ const client = await this.mainchain;
559
+ const api = await client.at(blockHash);
560
+ let obligationId = this.obligationIdByUtxoId[utxoId];
561
+ if (!obligationId) {
562
+ const lock = await api.query.bitcoinLocks.locksByUtxoId(utxoId);
563
+ obligationId = lock.value.obligationId.toNumber();
564
+ this.obligationIdByUtxoId[utxoId] = obligationId;
565
+ this.obligationsById[obligationId] = {
566
+ vaultId: lock.value.vaultId.toNumber(),
567
+ amount: lock.value.lockPrice.toBigInt()
568
+ };
569
+ }
570
+ return this.obligationsById[obligationId];
571
+ }
572
+ };
573
+
574
+ // src/MiningRotations.ts
575
+ var MiningRotations = class {
576
+ miningConfig;
577
+ genesisTick;
578
+ async getForTick(client, tick) {
579
+ this.miningConfig ??= await client.query.miningSlot.miningConfig().then((x) => ({
580
+ ticksBetweenSlots: x.ticksBetweenSlots.toNumber(),
581
+ slotBiddingStartAfterTicks: x.slotBiddingStartAfterTicks.toNumber()
582
+ }));
583
+ this.genesisTick ??= await client.query.ticks.genesisTick().then((x) => x.toNumber());
584
+ const ticksBetweenSlots = this.miningConfig.ticksBetweenSlots;
585
+ const slot1Tick = this.genesisTick + this.miningConfig.slotBiddingStartAfterTicks;
586
+ const ticksSinceMiningStart = tick - slot1Tick;
587
+ return Math.floor(ticksSinceMiningStart / ticksBetweenSlots);
588
+ }
589
+ async getForHeader(client, header) {
590
+ const tick = getTickFromHeader(client, header);
591
+ if (tick === void 0) return void 0;
592
+ return this.getForTick(client, tick);
593
+ }
594
+ };
595
+
596
+ // src/AccountMiners.ts
597
+ var AccountMiners = class {
598
+ constructor(accountset, options = { shouldLog: false }) {
599
+ this.accountset = accountset;
600
+ this.options = options;
601
+ this.miningRotations = new MiningRotations();
602
+ }
603
+ events = new EventEmitter2();
604
+ miningRotations;
605
+ trackedAccountsByAddress = {};
606
+ async watch() {
607
+ const blockWatch = new BlockWatch(this.accountset.client, {
608
+ shouldLog: this.options.shouldLog
609
+ });
610
+ blockWatch.events.on("block", this.onBlock.bind(this));
611
+ await blockWatch.start();
612
+ return blockWatch;
613
+ }
614
+ loadCurrentState(seats) {
615
+ for (const seat of seats) {
616
+ this.trackedAccountsByAddress[seat.address] = {
617
+ cohortId: seat.cohortId,
618
+ subaccountIndex: seat.subaccountIndex
619
+ };
620
+ }
621
+ }
622
+ async loadAt(blockHash) {
623
+ const seats = await this.accountset.miningSeats(blockHash);
624
+ this.loadCurrentState(seats.filter((x) => x.cohortId !== void 0));
625
+ }
626
+ async onBlock(header, digests, events) {
627
+ const { author, tick } = digests;
628
+ if (author) {
629
+ const voteAuthor = this.trackedAccountsByAddress[author];
630
+ if (voteAuthor && this.options.shouldLog) {
631
+ console.log(
632
+ "> Our vote author",
633
+ this.accountset.accountRegistry.getName(author)
634
+ );
635
+ }
636
+ } else {
637
+ console.warn("> No vote author found");
638
+ }
639
+ const client = await this.accountset.client;
640
+ const rotation = await this.miningRotations.getForTick(client, tick);
641
+ let newMiners;
642
+ const dataByCohort = { rotation };
643
+ for (const event of events) {
644
+ if (client.events.miningSlot.NewMiners.is(event)) {
645
+ newMiners = {
646
+ cohortId: event.data.cohortId.toNumber(),
647
+ addresses: event.data.newMiners.map((x) => x.accountId.toHuman())
648
+ };
649
+ }
650
+ if (client.events.blockRewards.RewardCreated.is(event)) {
651
+ const { rewards } = event.data;
652
+ for (const reward of rewards) {
653
+ const { argons, ownership } = reward;
654
+ const entry = this.trackedAccountsByAddress[author];
655
+ if (entry) {
656
+ dataByCohort[entry.cohortId] ??= {
657
+ argonsMinted: 0n,
658
+ argonsMined: 0n,
659
+ argonotsMined: 0n
660
+ };
661
+ dataByCohort[entry.cohortId].argonotsMined += ownership.toBigInt();
662
+ dataByCohort[entry.cohortId].argonsMined += argons.toBigInt();
663
+ this.events.emit("mined", header, {
664
+ author,
665
+ argons: argons.toBigInt(),
666
+ argonots: ownership.toBigInt(),
667
+ cohortId: entry.cohortId,
668
+ rotation
669
+ });
670
+ }
671
+ }
672
+ }
673
+ if (client.events.mint.MiningMint.is(event)) {
674
+ const { perMiner } = event.data;
675
+ const amountPerMiner = perMiner.toBigInt();
676
+ if (amountPerMiner > 0n) {
677
+ for (const [address, info] of Object.entries(
678
+ this.trackedAccountsByAddress
679
+ )) {
680
+ const { cohortId } = info;
681
+ dataByCohort[cohortId] ??= {
682
+ argonsMinted: 0n,
683
+ argonsMined: 0n,
684
+ argonotsMined: 0n
685
+ };
686
+ dataByCohort[cohortId].argonsMinted += amountPerMiner;
687
+ this.events.emit("minted", header, {
688
+ accountId: address,
689
+ argons: amountPerMiner,
690
+ cohortId,
691
+ rotation
692
+ });
693
+ }
694
+ }
695
+ }
696
+ }
697
+ if (newMiners) {
698
+ this.newCohortMiners(newMiners.cohortId, newMiners.addresses);
699
+ }
700
+ return dataByCohort;
701
+ }
702
+ newCohortMiners(cohortId, addresses) {
703
+ for (const [address, info] of Object.entries(
704
+ this.trackedAccountsByAddress
705
+ )) {
706
+ if (info.cohortId === cohortId - 10) {
707
+ delete this.trackedAccountsByAddress[address];
708
+ }
709
+ }
710
+ for (const address of addresses) {
711
+ const entry = this.accountset.subAccountsByAddress[address];
712
+ if (entry) {
713
+ console.log("new miners has subaccount", address);
714
+ this.trackedAccountsByAddress[address] = {
715
+ cohortId,
716
+ subaccountIndex: entry.index
717
+ };
718
+ }
719
+ }
720
+ }
721
+ };
722
+
723
+ // src/Accountset.ts
724
+ var Accountset = class _Accountset {
725
+ txSubmitterPair;
726
+ isProxy = false;
727
+ seedAddress;
728
+ subAccountsByAddress = {};
729
+ accountRegistry;
730
+ client;
731
+ get addresses() {
732
+ return [this.seedAddress, ...Object.keys(this.subAccountsByAddress)];
733
+ }
734
+ get namedAccounts() {
735
+ return this.accountRegistry.namedAccounts;
736
+ }
737
+ sessionKeyMnemonic;
738
+ constructor(options) {
739
+ if ("seedAccount" in options) {
740
+ this.txSubmitterPair = options.seedAccount;
741
+ this.seedAddress = options.seedAccount.address;
742
+ this.isProxy = false;
743
+ } else {
744
+ this.isProxy = options.isProxy;
745
+ this.txSubmitterPair = options.txSubmitter;
746
+ this.seedAddress = options.seedAddress;
747
+ }
748
+ this.sessionKeyMnemonic = options.sessionKeyMnemonic;
749
+ this.accountRegistry = options.accountRegistry ?? AccountRegistry.factory(options.name);
750
+ this.client = options.client;
751
+ const defaultRange = options.subaccountRange ?? getDefaultSubaccountRange();
752
+ this.accountRegistry.register(
753
+ this.seedAddress,
754
+ `${this.accountRegistry.me}//seed`
755
+ );
756
+ for (const i of defaultRange) {
757
+ const pair = this.txSubmitterPair.derive(`//${i}`);
758
+ this.subAccountsByAddress[pair.address] = { pair, index: i };
759
+ this.accountRegistry.register(
760
+ pair.address,
761
+ `${this.accountRegistry.me}//${i}`
762
+ );
763
+ }
764
+ }
765
+ async balance(blockHash) {
766
+ const client = await this.client;
767
+ const api = blockHash ? await client.at(blockHash) : client;
768
+ const accountData = await api.query.system.account(this.seedAddress);
769
+ return accountData.data.free.toBigInt();
770
+ }
771
+ async totalArgonsAt(blockHash) {
772
+ const client = await this.client;
773
+ const api = blockHash ? await client.at(blockHash) : client;
774
+ const addresses = this.addresses;
775
+ const results = await api.query.system.account.multi(addresses);
776
+ return results.map((account, i) => {
777
+ const address = addresses[i];
778
+ return {
779
+ address,
780
+ amount: account.data.free.toBigInt(),
781
+ index: this.subAccountsByAddress[address]?.index ?? Number.NaN
782
+ };
783
+ });
784
+ }
785
+ async totalArgonotsAt(blockHash) {
786
+ const client = await this.client;
787
+ const api = blockHash ? await client.at(blockHash) : client;
788
+ const addresses = this.addresses;
789
+ const results = await api.query.ownership.account.multi(addresses);
790
+ return results.map((account, i) => {
791
+ const address = addresses[i];
792
+ return {
793
+ address,
794
+ amount: account.free.toBigInt(),
795
+ index: this.subAccountsByAddress[address]?.index ?? Number.NaN
796
+ };
797
+ });
798
+ }
799
+ async getAvailableMinerAccounts(maxSeats) {
800
+ const miningSeats = await this.miningSeats();
801
+ const subaccountRange = [];
802
+ for (const seat of miningSeats) {
803
+ if (seat.hasWinningBid) {
804
+ continue;
805
+ }
806
+ if (seat.isLastDay || seat.seat === void 0) {
807
+ subaccountRange.push({
808
+ index: seat.subaccountIndex,
809
+ isRebid: seat.seat !== void 0,
810
+ address: seat.address
811
+ });
812
+ if (subaccountRange.length >= maxSeats) {
813
+ break;
814
+ }
815
+ }
816
+ }
817
+ return subaccountRange;
818
+ }
819
+ async miningSeats(blockHash) {
820
+ const client = await this.client;
821
+ const api = blockHash ? await client.at(blockHash) : client;
822
+ const addresses = Object.keys(this.subAccountsByAddress);
823
+ const rawIndices = await api.query.miningSlot.accountIndexLookup.multi(addresses);
824
+ const addressToIndex = {};
825
+ for (let i = 0; i < addresses.length; i++) {
826
+ const address = addresses[i];
827
+ if (rawIndices[i].isNone) continue;
828
+ addressToIndex[address] = rawIndices[i].value.toNumber();
829
+ }
830
+ const indices = Object.values(addressToIndex).filter(
831
+ (x) => x !== void 0
832
+ );
833
+ const indexRegistrations = indices.length ? await api.query.miningSlot.activeMinersByIndex.multi(indices) : [];
834
+ const registrationBySeatIndex = {};
835
+ for (let i = 0; i < indices.length; i++) {
836
+ const seatIndex = indices[i];
837
+ const registration = indexRegistrations[i];
838
+ if (registration?.isSome) {
839
+ registrationBySeatIndex[seatIndex] = {
840
+ cohortId: registration.value.cohortId.toNumber(),
841
+ bid: registration.value.bid.toBigInt()
842
+ };
843
+ }
844
+ }
845
+ const nextCohortId = await api.query.miningSlot.nextCohortId();
846
+ const nextCohort = await api.query.miningSlot.nextSlotCohort();
847
+ return addresses.map((address) => {
848
+ const seat = addressToIndex[address];
849
+ const registration = registrationBySeatIndex[seat];
850
+ let isLastDay = false;
851
+ if (registration) {
852
+ isLastDay = nextCohortId.toNumber() - registration.cohortId === 10;
853
+ }
854
+ return {
855
+ address,
856
+ seat,
857
+ subaccountIndex: this.subAccountsByAddress[address]?.index ?? Number.NaN,
858
+ hasWinningBid: nextCohort.some((x) => x.accountId.toHuman() === address),
859
+ cohortId: registration?.cohortId,
860
+ bidAmount: registration?.bid,
861
+ isLastDay
862
+ };
863
+ });
864
+ }
865
+ async bids(blockHash) {
866
+ const client = await this.client;
867
+ const api = blockHash ? await client.at(blockHash) : client;
868
+ const addresses = Object.keys(this.subAccountsByAddress);
869
+ const nextCohort = await api.query.miningSlot.nextSlotCohort();
870
+ const registrationsByAddress = Object.fromEntries(
871
+ nextCohort.map((x, i) => [x.accountId.toHuman(), { ...x, index: i }])
872
+ );
873
+ return addresses.map((address) => {
874
+ const entry = registrationsByAddress[address];
875
+ return {
876
+ address,
877
+ bidPlace: entry?.index,
878
+ bidAmount: entry?.bid?.toBigInt(),
879
+ index: this.subAccountsByAddress[address]?.index ?? Number.NaN
880
+ };
881
+ });
882
+ }
883
+ async consolidate(subaccounts) {
884
+ const client = await this.client;
885
+ const accounts = this.getAccountsInRange(subaccounts);
886
+ const results = [];
887
+ await Promise.allSettled(
888
+ accounts.map(({ pair, index }) => {
889
+ if (!pair) {
890
+ results.push({
891
+ index,
892
+ failedError: new Error(`No keypair for //${index}`)
893
+ });
894
+ return Promise.resolve();
895
+ }
896
+ return new Promise((resolve) => {
897
+ client.tx.utility.batchAll([
898
+ client.tx.balances.transferAll(this.seedAddress, true),
899
+ client.tx.ownership.transferAll(this.seedAddress, true)
900
+ ]).signAndSend(pair, (cb) => {
901
+ logExtrinsicResult(cb);
902
+ if (cb.dispatchError) {
903
+ const error = dispatchErrorToString(client, cb.dispatchError);
904
+ results.push({
905
+ index,
906
+ failedError: new Error(
907
+ `Error consolidating //${index}: ${error}`
908
+ )
909
+ });
910
+ resolve();
911
+ }
912
+ if (cb.isInBlock) {
913
+ results.push({ index, inBlock: cb.status.asInBlock.toHex() });
914
+ resolve();
915
+ }
916
+ }).catch((e) => {
917
+ results.push({ index, failedError: e });
918
+ resolve();
919
+ });
920
+ });
921
+ })
922
+ );
923
+ return results;
924
+ }
925
+ status(opts) {
926
+ const { argons, argonots, accountSubset, bids, seats } = opts;
927
+ const accounts = [
928
+ {
929
+ index: "main",
930
+ address: this.seedAddress,
931
+ argons: formatArgons(
932
+ argons.find((x) => x.address === this.seedAddress)?.amount ?? 0n
933
+ ),
934
+ argonots: formatArgons(
935
+ argonots.find((x) => x.address === this.seedAddress)?.amount ?? 0n
936
+ )
937
+ }
938
+ ];
939
+ for (const [address, { index }] of Object.entries(
940
+ this.subAccountsByAddress
941
+ )) {
942
+ const argonAmount = argons.find((x) => x.address === address)?.amount ?? 0n;
943
+ const argonotAmount = argonots.find((x) => x.address === address)?.amount ?? 0n;
944
+ const bid = bids.find((x) => x.address === address);
945
+ const seat = seats.find((x) => x.address === address)?.seat;
946
+ const entry = {
947
+ index: ` //${index}`,
948
+ address,
949
+ argons: formatArgons(argonAmount),
950
+ argonots: formatArgons(argonotAmount),
951
+ seat,
952
+ bidPlace: bid?.bidPlace,
953
+ bidAmount: bid?.bidAmount ?? 0n
954
+ };
955
+ if (accountSubset) {
956
+ entry.isWorkingOn = accountSubset.some((x) => x.address === address);
957
+ }
958
+ accounts.push(entry);
959
+ }
960
+ return accounts;
961
+ }
962
+ async registerKeys(url) {
963
+ const client = await getClient(url.replace("ws:", "http:"));
964
+ const keys = this.keys();
965
+ for (const [name, key] of Object.entries(keys)) {
966
+ console.log("Registering key", name, key.publicKey);
967
+ const result = await client.rpc.author.insertKey(
968
+ name,
969
+ key.privateKey,
970
+ key.publicKey
971
+ );
972
+ const saved = await client.rpc.author.hasKey(key.publicKey, name);
973
+ if (!saved) {
974
+ console.error("Failed to register key", name, key.publicKey);
975
+ throw new Error(`Failed to register ${name} key ${key.publicKey}`);
976
+ }
977
+ console.log(`Registered ${name} key`, result.toHuman());
978
+ }
979
+ await client.disconnect();
980
+ }
981
+ keys(keysVersion) {
982
+ let version = keysVersion ?? 0;
983
+ if (process2.env.KEYS_VERSION) {
984
+ version = parseInt(process2.env.KEYS_VERSION) ?? 0;
985
+ }
986
+ const seedMnemonic = this.sessionKeyMnemonic ?? process2.env.KEYS_MNEMONIC;
987
+ if (!seedMnemonic) {
988
+ throw new Error(
989
+ "KEYS_MNEMONIC environment variable not set. Cannot derive keys."
990
+ );
991
+ }
992
+ const blockSealKey = `${seedMnemonic}//block-seal//${version}`;
993
+ const granKey = `${seedMnemonic}//grandpa//${version}`;
994
+ const blockSealAccount = new Keyring().createFromUri(blockSealKey, {
995
+ type: "ed25519"
996
+ });
997
+ const grandpaAccount = new Keyring().createFromUri(granKey, {
998
+ type: "ed25519"
999
+ });
1000
+ return {
1001
+ seal: {
1002
+ privateKey: blockSealKey,
1003
+ publicKey: `0x${Buffer.from(blockSealAccount.publicKey).toString("hex")}`,
1004
+ rawPublicKey: blockSealAccount.publicKey
1005
+ },
1006
+ gran: {
1007
+ privateKey: granKey,
1008
+ publicKey: `0x${Buffer.from(grandpaAccount.publicKey).toString("hex")}`,
1009
+ rawPublicKey: grandpaAccount.publicKey
1010
+ }
1011
+ };
1012
+ }
1013
+ async tx(tx) {
1014
+ const client = await this.client;
1015
+ return new TxSubmitter(client, tx, this.txSubmitterPair);
1016
+ }
1017
+ /**
1018
+ * Create a mining bid. This will create a bid for each account in the given range from the seed account as funding.
1019
+ */
1020
+ async createMiningBids(options) {
1021
+ const accounts = this.getAccountsInRange(options.subaccountRange);
1022
+ const client = await this.client;
1023
+ let tip = options.tip ?? 0n;
1024
+ const batch = client.tx.utility.batch(
1025
+ accounts.map((x) => {
1026
+ const keys = this.keys();
1027
+ const rewards = options.sendRewardsToSeed ? { Account: this.seedAddress } : { Owner: null };
1028
+ return client.tx.miningSlot.bid(
1029
+ options.bidAmount,
1030
+ rewards,
1031
+ {
1032
+ grandpa: keys.gran.rawPublicKey,
1033
+ blockSealAuthority: keys.seal.rawPublicKey
1034
+ },
1035
+ x.address
1036
+ );
1037
+ })
1038
+ );
1039
+ let tx = batch;
1040
+ if (this.isProxy) {
1041
+ tx = client.tx.proxy.proxy(this.seedAddress, "MiningBid", batch);
1042
+ }
1043
+ const submitter = new TxSubmitter(client, tx, this.txSubmitterPair);
1044
+ const txFee = await submitter.feeEstimate(tip);
1045
+ let minBalance = options.bidAmount * BigInt(accounts.length);
1046
+ let totalFees = tip + 1n + txFee;
1047
+ const seedBalance = await client.query.system.account(this.seedAddress).then((x) => x.data.free.toBigInt());
1048
+ if (!this.isProxy) {
1049
+ minBalance += totalFees;
1050
+ }
1051
+ if (seedBalance < minBalance) {
1052
+ throw new Error(
1053
+ `Insufficient balance to create mining bids. Seed account has ${formatArgons(
1054
+ seedBalance
1055
+ )} but needs ${formatArgons(minBalance)}`
1056
+ );
1057
+ }
1058
+ if (this.isProxy) {
1059
+ const { canAfford, availableBalance } = await submitter.canAfford({
1060
+ tip
1061
+ });
1062
+ if (!canAfford) {
1063
+ throw new Error(
1064
+ `Insufficient balance to pay proxy fees. Proxy account has ${formatArgons(
1065
+ availableBalance
1066
+ )} but needs ${formatArgons(totalFees)}`
1067
+ );
1068
+ }
1069
+ }
1070
+ console.log("Creating bids", {
1071
+ perSeatBid: options.bidAmount,
1072
+ subaccounts: options.subaccountRange,
1073
+ txFee
1074
+ });
1075
+ const txResult = await submitter.submit({
1076
+ tip,
1077
+ useLatestNonce: true
1078
+ });
1079
+ const bidError = await txResult.inBlockPromise.then(() => void 0).catch((x) => x);
1080
+ return {
1081
+ finalFee: txResult.finalFee,
1082
+ bidError,
1083
+ blockHash: txResult.includedInBlock,
1084
+ successfulBids: txResult.batchInterruptedIndex !== void 0 ? txResult.batchInterruptedIndex : accounts.length
1085
+ };
1086
+ }
1087
+ getAccountsInRange(range) {
1088
+ const entries = new Set(range ?? getDefaultSubaccountRange());
1089
+ return Object.entries(this.subAccountsByAddress).filter(([_, account]) => {
1090
+ return entries.has(account.index);
1091
+ }).map(([address, { pair, index }]) => ({ pair, index, address }));
1092
+ }
1093
+ async watchBlocks(shouldLog = false) {
1094
+ const accountMiners = new AccountMiners(this, { shouldLog });
1095
+ await accountMiners.loadAt();
1096
+ await accountMiners.watch();
1097
+ return accountMiners;
1098
+ }
1099
+ static async fromCli(program, proxyForAddress) {
1100
+ const parentOptions = program.parent?.optsWithGlobals();
1101
+ const keypair = await keyringFromCli(parentOptions);
1102
+ const client = getClient(parentOptions.mainchainUrl);
1103
+ if (proxyForAddress) {
1104
+ return new _Accountset({
1105
+ client,
1106
+ isProxy: true,
1107
+ seedAddress: proxyForAddress,
1108
+ txSubmitter: keypair
1109
+ });
1110
+ } else {
1111
+ return new _Accountset({
1112
+ seedAccount: keypair,
1113
+ client
1114
+ });
1115
+ }
1116
+ }
1117
+ };
1118
+ function getDefaultSubaccountRange() {
1119
+ return parseSubaccountRange(process2.env.SUBACCOUNT_RANGE ?? "0-9");
1120
+ }
1121
+ function parseSubaccountRange(range) {
1122
+ if (!range) {
1123
+ return void 0;
1124
+ }
1125
+ const indices = [];
1126
+ for (const entry of range.split(",")) {
1127
+ if (entry.includes("-")) {
1128
+ const [start, end] = entry.split("-").map((x) => parseInt(x, 10));
1129
+ for (let i = start; i <= end; i++) {
1130
+ indices.push(i);
1131
+ }
1132
+ continue;
1133
+ }
1134
+ const record = parseInt(entry.trim(), 10);
1135
+ if (Number.isNaN(record) || !Number.isInteger(record)) {
1136
+ throw new Error(`Invalid range entry: ${entry}`);
1137
+ }
1138
+ if (Number.isInteger(record)) {
1139
+ indices.push(record);
1140
+ }
1141
+ }
1142
+ return indices;
1143
+ }
1144
+
1145
+ // src/MiningBids.ts
1146
+ import { printTable } from "console-table-printer";
1147
+ var MiningBids = class {
1148
+ constructor(client, shouldLog = true) {
1149
+ this.client = client;
1150
+ this.shouldLog = shouldLog;
1151
+ }
1152
+ nextCohort = [];
1153
+ async maxCohortSize() {
1154
+ const client = await this.client;
1155
+ return client.consts.miningSlot.maxCohortSize.toNumber();
1156
+ }
1157
+ async onCohortChange(options) {
1158
+ const { onBiddingStart, onBiddingEnd } = options;
1159
+ const client = await this.client;
1160
+ let openCohortId = 0;
1161
+ const unsubscribe = await client.queryMulti(
1162
+ [
1163
+ client.query.miningSlot.isNextSlotBiddingOpen,
1164
+ client.query.miningSlot.nextCohortId
1165
+ ],
1166
+ async ([isBiddingOpen, rawNextCohortId]) => {
1167
+ const nextCohortId = rawNextCohortId.toNumber();
1168
+ if (isBiddingOpen.isTrue) {
1169
+ if (openCohortId !== 0) {
1170
+ await onBiddingEnd?.(openCohortId);
1171
+ }
1172
+ openCohortId = nextCohortId;
1173
+ await onBiddingStart?.(nextCohortId);
1174
+ } else {
1175
+ await onBiddingEnd?.(nextCohortId);
1176
+ openCohortId = 0;
1177
+ }
1178
+ }
1179
+ );
1180
+ return { unsubscribe };
1181
+ }
1182
+ async watch(accountNames, blockHash, printFn) {
1183
+ const client = await this.client;
1184
+ const api = blockHash ? await client.at(blockHash) : client;
1185
+ const unsubscribe = await api.query.miningSlot.nextSlotCohort(
1186
+ async (next) => {
1187
+ this.nextCohort = await Promise.all(
1188
+ next.map((x) => this.toBid(accountNames, x))
1189
+ );
1190
+ if (!this.shouldLog) return;
1191
+ console.clear();
1192
+ const block = await client.query.system.number();
1193
+ if (!printFn) {
1194
+ console.log("At block", block.toNumber());
1195
+ this.print();
1196
+ } else {
1197
+ printFn(block.toNumber());
1198
+ }
1199
+ }
1200
+ );
1201
+ return { unsubscribe };
1202
+ }
1203
+ async loadAt(accountNames, blockHash) {
1204
+ const client = await this.client;
1205
+ const api = blockHash ? await client.at(blockHash) : client;
1206
+ const nextCohort = await api.query.miningSlot.nextSlotCohort();
1207
+ this.nextCohort = await Promise.all(
1208
+ nextCohort.map((x) => this.toBid(accountNames, x))
1209
+ );
1210
+ }
1211
+ async toBid(accountNames, bid) {
1212
+ return {
1213
+ accountId: bid.accountId.toString(),
1214
+ isOurs: accountNames.get(bid.accountId.toString()) ?? "n",
1215
+ bidAmount: bid.bid.toBigInt()
1216
+ };
1217
+ }
1218
+ print() {
1219
+ const bids = this.nextCohort.map((bid) => {
1220
+ return {
1221
+ account: bid.accountId,
1222
+ isOurs: bid.isOurs,
1223
+ bidAmount: formatArgons(bid.bidAmount)
1224
+ };
1225
+ });
1226
+ if (bids.length) {
1227
+ console.log("\n\nMining Bids:");
1228
+ printTable(bids);
1229
+ }
1230
+ }
1231
+ };
1232
+
1233
+ // src/Vault.ts
1234
+ import BigNumber2, * as BN2 from "bignumber.js";
1235
+ var { ROUND_FLOOR: ROUND_FLOOR2 } = BN2;
1236
+ var Vault = class {
1237
+ securitization;
1238
+ securitizationRatio;
1239
+ bitcoinLocked;
1240
+ bitcoinPending;
1241
+ terms;
1242
+ operatorAccountId;
1243
+ isClosed;
1244
+ vaultId;
1245
+ pendingTerms;
1246
+ pendingTermsChangeTick;
1247
+ openedDate;
1248
+ constructor(id, vault, tickDuration) {
1249
+ this.securitization = vault.securitization.toBigInt();
1250
+ this.securitizationRatio = convertFixedU128ToBigNumber(
1251
+ vault.securitizationRatio.toBigInt()
1252
+ );
1253
+ this.bitcoinLocked = vault.bitcoinLocked.toBigInt();
1254
+ this.bitcoinPending = vault.bitcoinPending.toBigInt();
1255
+ this.terms = {
1256
+ bitcoinAnnualPercentRate: convertFixedU128ToBigNumber(
1257
+ vault.terms.bitcoinAnnualPercentRate.toBigInt()
1258
+ ),
1259
+ bitcoinBaseFee: vault.terms.bitcoinBaseFee.toBigInt(),
1260
+ liquidityPoolProfitSharing: convertPermillToBigNumber(
1261
+ vault.terms.liquidityPoolProfitSharing.toBigInt()
1262
+ )
1263
+ };
1264
+ this.operatorAccountId = vault.operatorAccountId.toString();
1265
+ this.isClosed = vault.isClosed.valueOf();
1266
+ this.vaultId = id;
1267
+ if (vault.pendingTerms.isSome) {
1268
+ const [tickApply, terms] = vault.pendingTerms.value;
1269
+ this.pendingTermsChangeTick = tickApply.toNumber();
1270
+ this.pendingTerms = {
1271
+ bitcoinAnnualPercentRate: convertFixedU128ToBigNumber(
1272
+ terms.bitcoinAnnualPercentRate.toBigInt()
1273
+ ),
1274
+ bitcoinBaseFee: terms.bitcoinBaseFee.toBigInt(),
1275
+ liquidityPoolProfitSharing: convertPermillToBigNumber(
1276
+ vault.terms.liquidityPoolProfitSharing.toBigInt()
1277
+ )
1278
+ };
1279
+ }
1280
+ this.openedDate = vault.openedTick ? new Date(vault.openedTick.toNumber() * tickDuration) : /* @__PURE__ */ new Date();
1281
+ }
1282
+ availableBitcoinSpace() {
1283
+ const recoverySecuritization = this.recoverySecuritization();
1284
+ return this.securitization - recoverySecuritization - this.bitcoinLocked;
1285
+ }
1286
+ recoverySecuritization() {
1287
+ const reserved = new BigNumber2(1).div(this.securitizationRatio);
1288
+ return this.securitization - BigInt(
1289
+ reserved.multipliedBy(this.securitization.toString()).toFixed(0, ROUND_FLOOR2)
1290
+ );
1291
+ }
1292
+ minimumSecuritization() {
1293
+ return BigInt(
1294
+ this.securitizationRatio.multipliedBy(this.bitcoinLocked.toString()).decimalPlaces(0, BigNumber2.ROUND_CEIL).toString()
1295
+ );
1296
+ }
1297
+ activatedSecuritization() {
1298
+ const activated = this.bitcoinLocked - this.bitcoinPending;
1299
+ let maxRatio = this.securitizationRatio;
1300
+ if (this.securitizationRatio.toNumber() > 2) {
1301
+ maxRatio = BigNumber2(2);
1302
+ }
1303
+ return BigInt(
1304
+ maxRatio.multipliedBy(activated.toString()).toFixed(0, ROUND_FLOOR2)
1305
+ );
1306
+ }
1307
+ /**
1308
+ * Returns the amount of Argons available to match per liquidity pool
1309
+ */
1310
+ activatedSecuritizationPerSlot() {
1311
+ const activated = this.activatedSecuritization();
1312
+ return activated / 10n;
1313
+ }
1314
+ calculateBitcoinFee(amount) {
1315
+ const fee = this.terms.bitcoinAnnualPercentRate.multipliedBy(Number(amount)).integerValue(BigNumber2.ROUND_CEIL);
1316
+ return BigInt(fee.toString()) + this.terms.bitcoinBaseFee;
1317
+ }
1318
+ };
1319
+
1320
+ // src/VaultMonitor.ts
1321
+ import EventEmitter3 from "node:events";
1322
+ import { printTable as printTable2 } from "console-table-printer";
1323
+ var VaultMonitor = class {
1324
+ constructor(accountset, alerts = {}, options = {}) {
1325
+ this.accountset = accountset;
1326
+ this.alerts = alerts;
1327
+ this.options = options;
1328
+ this.mainchain = accountset.client;
1329
+ if (options.vaultOnlyWatchMode !== void 0) {
1330
+ this.vaultOnlyWatchMode = options.vaultOnlyWatchMode;
1331
+ }
1332
+ if (options.shouldLog !== void 0) {
1333
+ this.shouldLog = options.shouldLog;
1334
+ }
1335
+ this.miningBids = new MiningBids(this.mainchain, this.shouldLog);
1336
+ this.blockWatch = new BlockWatch(this.mainchain, {
1337
+ shouldLog: this.shouldLog
1338
+ });
1339
+ this.blockWatch.events.on(
1340
+ "vaults-updated",
1341
+ (header, vaultIds) => this.onVaultsUpdated(header.hash, vaultIds)
1342
+ );
1343
+ this.blockWatch.events.on("mining-bid", async (header, _bid) => {
1344
+ await this.miningBids.loadAt(this.accountset.namedAccounts, header.hash);
1345
+ this.printBids(header.hash);
1346
+ });
1347
+ this.blockWatch.events.on("mining-bid-ousted", async (header) => {
1348
+ await this.miningBids.loadAt(this.accountset.namedAccounts, header.hash);
1349
+ this.printBids(header.hash);
1350
+ });
1351
+ }
1352
+ events = new EventEmitter3();
1353
+ vaultsById = {};
1354
+ blockWatch;
1355
+ mainchain;
1356
+ activatedCapitalByVault = {};
1357
+ lastPrintedBids;
1358
+ miningBids;
1359
+ tickDuration = 0;
1360
+ vaultOnlyWatchMode = false;
1361
+ shouldLog = true;
1362
+ stop() {
1363
+ this.blockWatch.stop();
1364
+ }
1365
+ async monitor(justPrint = false) {
1366
+ const client = await this.mainchain;
1367
+ this.tickDuration = (await client.query.ticks.genesisTicker()).tickDurationMillis.toNumber();
1368
+ const blockHeader = await client.rpc.chain.getHeader();
1369
+ const blockHash = blockHeader.hash.toU8a();
1370
+ console.log(
1371
+ `${justPrint ? "Run" : "Started"} at block ${blockHeader.number} - ${blockHeader.hash.toHuman()}`
1372
+ );
1373
+ await this.miningBids.loadAt(this.accountset.namedAccounts, blockHash);
1374
+ const vaults = await client.query.vaults.vaultsById.entries();
1375
+ for (const [storageKey, rawVault] of vaults) {
1376
+ const vaultId = storageKey.args[0].toNumber();
1377
+ this.updateVault(vaultId, rawVault);
1378
+ }
1379
+ await client.query.liquidityPools.nextLiquidityPoolCapital((x) => {
1380
+ this.activatedCapitalByVault = {};
1381
+ for (const entry of x) {
1382
+ const vaultId = entry.vaultId.toNumber();
1383
+ this.activatedCapitalByVault[vaultId] = entry.activatedCapital.toBigInt();
1384
+ }
1385
+ for (const [vaultId, vault] of Object.entries(this.vaultsById)) {
1386
+ const id = Number(vaultId);
1387
+ this.activatedCapitalByVault[id] ??= 0n;
1388
+ this.checkMiningBondAlerts(id, vault);
1389
+ }
1390
+ });
1391
+ this.printVaults();
1392
+ if (!this.vaultOnlyWatchMode && this.shouldLog) {
1393
+ this.miningBids.print();
1394
+ }
1395
+ if (!justPrint) await this.blockWatch.start();
1396
+ }
1397
+ printVaults() {
1398
+ if (!this.shouldLog) return;
1399
+ const vaults = [];
1400
+ for (const [vaultId, vault] of Object.entries(this.vaultsById)) {
1401
+ vaults.push({
1402
+ id: vaultId,
1403
+ btcSpace: `${formatArgons(vault.availableBitcoinSpace())} (${formatArgons(vault.bitcoinPending)} pending)`,
1404
+ btcDeal: `${formatArgons(vault.terms.bitcoinBaseFee)} + ${formatPercent(vault.terms.bitcoinAnnualPercentRate)}`,
1405
+ securitization: `${formatArgons(vault.securitization)} at ${vault.securitizationRatio.toFormat(1)}x`,
1406
+ securActivated: `${formatArgons(vault.activatedSecuritizationPerSlot())}/slot`,
1407
+ liquidPoolDeal: `${formatPercent(vault.terms.liquidityPoolProfitSharing)} sharing`,
1408
+ operator: `${this.accountset.namedAccounts.has(vault.operatorAccountId) ? ` (${this.accountset.namedAccounts.get(vault.operatorAccountId)})` : vault.operatorAccountId}`,
1409
+ state: vault.isClosed ? "closed" : vault.openedDate < /* @__PURE__ */ new Date() ? "open" : "pending"
1410
+ });
1411
+ }
1412
+ if (vaults.length) {
1413
+ if (this.vaultOnlyWatchMode) {
1414
+ console.clear();
1415
+ }
1416
+ console.log("\n\nVaults:");
1417
+ printTable2(vaults);
1418
+ }
1419
+ }
1420
+ async recheckAfterActive(vaultId) {
1421
+ const activationDate = this.vaultsById[vaultId].openedDate;
1422
+ if (this.shouldLog) {
1423
+ console.log(`Waiting for vault ${vaultId} to activate ${activationDate}`);
1424
+ }
1425
+ await new Promise(
1426
+ (resolve) => setTimeout(resolve, activationDate.getTime() - Date.now())
1427
+ );
1428
+ const client = await this.mainchain;
1429
+ let isReady = false;
1430
+ while (!isReady) {
1431
+ const rawVault = await client.query.vaults.vaultsById(vaultId);
1432
+ if (!rawVault.isSome) return;
1433
+ const vault = new Vault(vaultId, rawVault.value, this.tickDuration);
1434
+ this.vaultsById[vaultId] = vault;
1435
+ if (vault.isClosed) return;
1436
+ if (vault.openedDate < /* @__PURE__ */ new Date()) {
1437
+ isReady = true;
1438
+ break;
1439
+ }
1440
+ await new Promise((resolve) => setTimeout(resolve, 100));
1441
+ }
1442
+ this.checkAlerts(vaultId, this.vaultsById[vaultId]);
1443
+ }
1444
+ async onVaultsUpdated(blockHash, vaultIds) {
1445
+ await this.reloadVaultsAt([...vaultIds], blockHash).catch((err) => {
1446
+ console.error(
1447
+ `Failed to reload vault ${[...vaultIds]} at block ${blockHash}:`,
1448
+ err
1449
+ );
1450
+ });
1451
+ this.printVaults();
1452
+ }
1453
+ async reloadVaultsAt(vaultIds, blockHash) {
1454
+ const client = await this.mainchain;
1455
+ const api = await client.at(blockHash);
1456
+ const vaults = await api.query.vaults.vaultsById.multi(vaultIds);
1457
+ for (let i = 0; i < vaultIds.length; i += 1) {
1458
+ this.updateVault(vaultIds[i], vaults[i]);
1459
+ }
1460
+ }
1461
+ updateVault(vaultId, rawVault) {
1462
+ if (rawVault.isNone) return;
1463
+ const vault = new Vault(vaultId, rawVault.value, this.tickDuration);
1464
+ this.vaultsById[vaultId] = vault;
1465
+ if (vault.openedDate > /* @__PURE__ */ new Date()) {
1466
+ void this.recheckAfterActive(vaultId);
1467
+ } else {
1468
+ this.checkAlerts(vaultId, vault);
1469
+ }
1470
+ }
1471
+ checkAlerts(vaultId, vault) {
1472
+ if (this.alerts.bitcoinSpaceAvailable !== void 0) {
1473
+ const availableBitcoinSpace = vault.availableBitcoinSpace();
1474
+ if (availableBitcoinSpace >= this.alerts.bitcoinSpaceAvailable) {
1475
+ console.warn(
1476
+ `Vault ${vaultId} has available bitcoins above ${formatArgons(this.alerts.bitcoinSpaceAvailable)}`
1477
+ );
1478
+ this.events.emit("bitcoin-space-above", vaultId, availableBitcoinSpace);
1479
+ }
1480
+ }
1481
+ }
1482
+ checkMiningBondAlerts(vaultId, vault) {
1483
+ if (this.alerts.liquidityPoolSpaceAvailable === void 0) return;
1484
+ const activatedSecuritization = vault.activatedSecuritizationPerSlot();
1485
+ const capitalization = this.activatedCapitalByVault[vaultId] ?? 0n;
1486
+ const available = activatedSecuritization - capitalization;
1487
+ if (available >= this.alerts.liquidityPoolSpaceAvailable) {
1488
+ this.events.emit("liquidity-pool-space-above", vaultId, available);
1489
+ }
1490
+ }
1491
+ printBids(blockHash) {
1492
+ if (!this.shouldLog) return;
1493
+ if (this.lastPrintedBids === blockHash) return;
1494
+ this.miningBids.print();
1495
+ this.lastPrintedBids = blockHash;
1496
+ }
1497
+ };
1498
+
1499
+ // src/CohortBidder.ts
1500
+ var CohortBidder = class {
1501
+ constructor(accountset, cohortId, subaccounts, options) {
1502
+ this.accountset = accountset;
1503
+ this.cohortId = cohortId;
1504
+ this.subaccounts = subaccounts;
1505
+ this.options = options;
1506
+ this.subaccounts.forEach((x) => {
1507
+ this.allAddresses.add(x.address);
1508
+ });
1509
+ }
1510
+ get client() {
1511
+ return this.accountset.client;
1512
+ }
1513
+ stats = {
1514
+ // number of seats won
1515
+ seats: 0,
1516
+ // sum of argons bid in successful bids
1517
+ totalArgonsBid: 0n,
1518
+ // total number of bids placed (includes 1 per seat)
1519
+ bids: 0,
1520
+ // fees including the tip
1521
+ fees: 0n,
1522
+ // Max bid per seat
1523
+ maxBidPerSeat: 0n,
1524
+ // The cost in argonots of each seat
1525
+ argonotsPerSeat: 0n,
1526
+ // The argonot price in USD for cost basis
1527
+ argonotUsdPrice: 0,
1528
+ // The cohort expected argons per block
1529
+ cohortArgonsPerBlock: 0n
1530
+ };
1531
+ unsubscribe;
1532
+ pendingRequest;
1533
+ retryTimeout;
1534
+ isStopped = false;
1535
+ needsRebid = false;
1536
+ lastBidTime = 0;
1537
+ millisPerTick;
1538
+ allAddresses = /* @__PURE__ */ new Set();
1539
+ async stop() {
1540
+ if (this.isStopped) return this.stats;
1541
+ this.isStopped = true;
1542
+ console.log("Stopping bidder for cohort", this.cohortId);
1543
+ clearTimeout(this.retryTimeout);
1544
+ if (this.unsubscribe) {
1545
+ this.unsubscribe();
1546
+ }
1547
+ const client = await this.client;
1548
+ const [nextCohort, nextCohortId] = await new Promise(async (resolve) => {
1549
+ const unsub = await client.queryMulti(
1550
+ [
1551
+ client.query.miningSlot.nextSlotCohort,
1552
+ client.query.miningSlot.nextCohortId,
1553
+ client.query.miningSlot.isNextSlotBiddingOpen
1554
+ ],
1555
+ ([nextCohort2, nextCohortId2, isBiddingStillOpen]) => {
1556
+ if (nextCohortId2.toNumber() !== this.cohortId || isBiddingStillOpen.isFalse) {
1557
+ unsub();
1558
+ resolve([nextCohort2, nextCohortId2]);
1559
+ }
1560
+ }
1561
+ );
1562
+ });
1563
+ void await this.pendingRequest;
1564
+ if (nextCohortId.toNumber() === this.cohortId) {
1565
+ console.log("Bidder updating stats with bid queue");
1566
+ this.updateStats(nextCohort);
1567
+ } else {
1568
+ const bestBlock = await client.rpc.chain.getBlockHash();
1569
+ const api = await client.at(bestBlock);
1570
+ const wonIndices = await api.query.miningSlot.accountIndexLookup.multi([...this.allAddresses]).then((x) => x.filter((x2) => x2.isSome).map((x2) => x2.value));
1571
+ const wonSeats = await api.query.miningSlot.activeMinersByIndex.multi(wonIndices).then(
1572
+ (x) => x.filter(
1573
+ (x2) => x2.isSome && x2.value.cohortId.toNumber() === this.cohortId
1574
+ ).map((x2) => x2.value)
1575
+ );
1576
+ console.log("Bidder updating stats with finalized cohort");
1577
+ this.updateStats(wonSeats);
1578
+ }
1579
+ return this.stats;
1580
+ }
1581
+ async start() {
1582
+ console.log(`Starting cohort ${this.cohortId} bidder`, {
1583
+ maxBid: formatArgons(this.options.maxBid),
1584
+ minBid: formatArgons(this.options.minBid),
1585
+ bidIncrement: formatArgons(this.options.bidIncrement),
1586
+ maxBalance: formatArgons(this.options.maxBalance),
1587
+ bidDelay: this.options.bidDelay,
1588
+ subaccounts: this.subaccounts
1589
+ });
1590
+ const client = await this.client;
1591
+ const argonotPrice = await client.query.priceIndex.current();
1592
+ if (argonotPrice.isSome) {
1593
+ this.stats.argonotUsdPrice = convertFixedU128ToBigNumber(
1594
+ argonotPrice.unwrap().argonotUsdPrice.toBigInt()
1595
+ ).toNumber();
1596
+ }
1597
+ this.millisPerTick ??= await client.query.ticks.genesisTicker().then((x) => x.tickDurationMillis.toNumber());
1598
+ this.stats.argonotsPerSeat = await client.query.miningSlot.argonotsPerMiningSeat().then((x) => x.toBigInt());
1599
+ this.stats.cohortArgonsPerBlock = await client.query.blockRewards.argonsPerBlock().then((x) => x.toBigInt());
1600
+ this.unsubscribe = await client.queryMulti(
1601
+ [
1602
+ client.query.miningSlot.nextSlotCohort,
1603
+ client.query.miningSlot.nextCohortId
1604
+ ],
1605
+ async ([next, nextCohortId]) => {
1606
+ if (nextCohortId.toNumber() === this.cohortId) {
1607
+ await this.checkSeats(next);
1608
+ }
1609
+ }
1610
+ );
1611
+ }
1612
+ updateStats(next) {
1613
+ let seats = 0;
1614
+ let totalArgonsBid = 0n;
1615
+ for (const x of next) {
1616
+ if (this.allAddresses.has(x.accountId.toHuman())) {
1617
+ seats++;
1618
+ totalArgonsBid += x.bid.toBigInt();
1619
+ }
1620
+ }
1621
+ this.stats.seats = seats;
1622
+ this.stats.totalArgonsBid = totalArgonsBid;
1623
+ }
1624
+ async checkSeats(next) {
1625
+ if (this.isStopped || this.pendingRequest) return;
1626
+ const ticksSinceLastBid = Math.floor(
1627
+ (Date.now() - this.lastBidTime) / this.millisPerTick
1628
+ );
1629
+ if (ticksSinceLastBid < this.options.bidDelay) {
1630
+ return;
1631
+ }
1632
+ console.log(
1633
+ "Checking seats for cohort",
1634
+ this.cohortId,
1635
+ this.subaccounts.map((x) => x.index)
1636
+ );
1637
+ this.updateStats(next);
1638
+ this.needsRebid = this.subaccounts.some(
1639
+ (x) => !next.some((y) => y.accountId.toHuman() === x.address)
1640
+ );
1641
+ if (!this.needsRebid) return;
1642
+ const lowestBid = next.at(-1)?.bid.toBigInt() ?? -this.options.bidIncrement;
1643
+ const MIN_INCREMENT = 10000n;
1644
+ let nextBid = lowestBid + this.options.bidIncrement;
1645
+ if (nextBid < this.options.minBid) {
1646
+ nextBid = this.options.minBid;
1647
+ }
1648
+ if (nextBid > this.options.maxBid) {
1649
+ nextBid = this.options.maxBid;
1650
+ if (nextBid - lowestBid < MIN_INCREMENT) {
1651
+ console.log(
1652
+ `Can't make any more bids for ${this.cohortId} with given constraints.`,
1653
+ {
1654
+ lowestCurrentBid: formatArgons(lowestBid),
1655
+ nextAttemptedBid: formatArgons(nextBid),
1656
+ maxBid: formatArgons(this.options.maxBid)
1657
+ }
1658
+ );
1659
+ return;
1660
+ }
1661
+ }
1662
+ if (nextBid < lowestBid) {
1663
+ console.log(
1664
+ `Can't bid ${formatArgons(nextBid)}. Current lowest bid is ${formatArgons(
1665
+ lowestBid
1666
+ )}.`
1667
+ );
1668
+ return;
1669
+ }
1670
+ const seatsInBudget = nextBid === 0n ? this.subaccounts.length : Number(this.options.maxBalance / nextBid);
1671
+ if (seatsInBudget <= 0) {
1672
+ console.log(
1673
+ `Can't afford any seats at ${formatArgons(nextBid)}. Would exceed our max balance of ${formatArgons(this.options.maxBalance)}.`
1674
+ );
1675
+ return;
1676
+ }
1677
+ if (this.subaccounts.length > seatsInBudget) {
1678
+ const toKeep = [];
1679
+ for (const account of this.subaccounts) {
1680
+ if (toKeep.length >= seatsInBudget) break;
1681
+ if (account.isRebid) {
1682
+ toKeep.push(account);
1683
+ }
1684
+ }
1685
+ for (const account of this.subaccounts) {
1686
+ if (toKeep.length >= seatsInBudget) break;
1687
+ if (!account.isRebid) {
1688
+ toKeep.push(account);
1689
+ }
1690
+ }
1691
+ const removedIndices = this.subaccounts.filter((x) => !toKeep.some((y) => y.index === x.index)).map((x) => x.index);
1692
+ this.subaccounts = toKeep;
1693
+ console.log("Had to remove some subaccounts to fit in budget:", {
1694
+ removedIndices,
1695
+ seatsInBudget,
1696
+ budget: formatArgons(this.options.maxBalance)
1697
+ });
1698
+ }
1699
+ this.pendingRequest = this.bid(
1700
+ nextBid,
1701
+ this.subaccounts.map((x) => x.index)
1702
+ );
1703
+ this.needsRebid = false;
1704
+ }
1705
+ async bid(bidPerSeat, subaccountRange) {
1706
+ if (!subaccountRange.length) return;
1707
+ const prevLastBidTime = this.lastBidTime;
1708
+ try {
1709
+ this.lastBidTime = Date.now();
1710
+ const result = await this.accountset.createMiningBids({
1711
+ subaccountRange,
1712
+ bidAmount: bidPerSeat,
1713
+ sendRewardsToSeed: true
1714
+ });
1715
+ this.stats.fees += result.finalFee ?? 0n;
1716
+ this.stats.bids += subaccountRange.length;
1717
+ if (bidPerSeat > this.stats.maxBidPerSeat) {
1718
+ this.stats.maxBidPerSeat = bidPerSeat;
1719
+ }
1720
+ console.log("Done creating bids for cohort", this.cohortId);
1721
+ if (result.bidError) throw result.bidError;
1722
+ } catch (err) {
1723
+ this.lastBidTime = prevLastBidTime;
1724
+ console.error(`Error bidding for cohort ${this.cohortId}:`, err);
1725
+ clearTimeout(this.retryTimeout);
1726
+ this.retryTimeout = setTimeout(() => void this.checkCurrentSeats(), 1e3);
1727
+ } finally {
1728
+ this.pendingRequest = void 0;
1729
+ }
1730
+ if (this.needsRebid) {
1731
+ this.needsRebid = false;
1732
+ await this.checkCurrentSeats();
1733
+ }
1734
+ }
1735
+ async checkCurrentSeats() {
1736
+ const client = await this.client;
1737
+ const next = await client.query.miningSlot.nextSlotCohort();
1738
+ await this.checkSeats(next);
1739
+ }
1740
+ };
1741
+
1742
+ // src/BidPool.ts
1743
+ import { Table } from "console-table-printer";
1744
+ var EMPTY_TABLE = {
1745
+ headerBottom: { left: " ", mid: " ", other: "\u2500", right: " " },
1746
+ headerTop: { left: " ", mid: " ", other: " ", right: " " },
1747
+ rowSeparator: { left: " ", mid: " ", other: " ", right: " " },
1748
+ tableBottom: { left: " ", mid: " ", other: " ", right: " " },
1749
+ vertical: " "
1750
+ };
1751
+ var BidPool = class {
1752
+ constructor(client, keypair, accountRegistry = AccountRegistry.factory()) {
1753
+ this.client = client;
1754
+ this.keypair = keypair;
1755
+ this.accountRegistry = accountRegistry;
1756
+ this.blockWatch = new BlockWatch(client, { shouldLog: false });
1757
+ }
1758
+ bidPoolAmount = 0n;
1759
+ nextCohortId = 1;
1760
+ poolVaultCapitalByCohort = {};
1761
+ vaultSecuritization = [];
1762
+ printTimeout;
1763
+ blockWatch;
1764
+ vaultsById = {};
1765
+ tickDuration;
1766
+ lastDistributedCohortId;
1767
+ cohortSubscriptions = {};
1768
+ async onVaultsUpdated(blockHash, vaultIdSet) {
1769
+ const client = await this.client;
1770
+ this.tickDuration ??= (await client.query.ticks.genesisTicker()).tickDurationMillis.toNumber();
1771
+ const api = await client.at(blockHash);
1772
+ const vaultIds = [...vaultIdSet];
1773
+ const rawVaults = await api.query.vaults.vaultsById.multi(vaultIds);
1774
+ for (let i = 0; i < vaultIds.length; i += 1) {
1775
+ const rawVault = rawVaults[i];
1776
+ if (rawVault.isNone) continue;
1777
+ const vaultId = vaultIds[i];
1778
+ this.vaultsById[vaultId] = new Vault(
1779
+ vaultId,
1780
+ rawVault.unwrap(),
1781
+ this.tickDuration
1782
+ );
1783
+ }
1784
+ const vaults = Object.entries(this.vaultsById);
1785
+ const newSecuritization = [];
1786
+ for (const [vaultId, vault] of vaults) {
1787
+ const amount = vault.activatedSecuritizationPerSlot();
1788
+ newSecuritization.push({
1789
+ vaultId: Number(vaultId),
1790
+ bitcoinSpace: vault.availableBitcoinSpace(),
1791
+ activatedSecuritization: amount,
1792
+ vaultSharingPercent: vault.terms.liquidityPoolProfitSharing
1793
+ });
1794
+ }
1795
+ newSecuritization.sort((a, b) => {
1796
+ const diff2 = b.activatedSecuritization - a.activatedSecuritization;
1797
+ if (diff2 !== 0n) return Number(diff2);
1798
+ return a.vaultId - b.vaultId;
1799
+ });
1800
+ this.vaultSecuritization = newSecuritization;
1801
+ this.printDebounce();
1802
+ }
1803
+ async getBidPool() {
1804
+ const client = await this.client;
1805
+ const balanceBytes = await client.rpc.state.call(
1806
+ "MiningSlotApi_bid_pool",
1807
+ ""
1808
+ );
1809
+ const balance = client.createType("U128", balanceBytes);
1810
+ return balance.toBigInt();
1811
+ }
1812
+ async loadAt(blockHash) {
1813
+ const client = await this.client;
1814
+ blockHash ??= (await client.rpc.chain.getHeader()).hash.toU8a();
1815
+ const api = await client.at(blockHash);
1816
+ const rawVaultIds = await api.query.vaults.vaultsById.keys();
1817
+ const vaultIds = rawVaultIds.map((x) => x.args[0].toNumber());
1818
+ this.bidPoolAmount = await this.getBidPool();
1819
+ this.nextCohortId = (await api.query.miningSlot.nextCohortId()).toNumber();
1820
+ const contributors = await api.query.liquidityPools.liquidityPoolsByCohort.entries();
1821
+ for (const [cohortId, funds] of contributors) {
1822
+ const cohortIdNumber = cohortId.args[0].toNumber();
1823
+ this.loadCohortData(cohortIdNumber, funds);
1824
+ }
1825
+ for (const entrant of await api.query.liquidityPools.openLiquidityPoolCapital()) {
1826
+ this.setVaultCohortData(this.nextCohortId, entrant.vaultId.toNumber(), {
1827
+ activatedCapital: entrant.activatedCapital.toBigInt()
1828
+ });
1829
+ }
1830
+ for (const entrant of await api.query.liquidityPools.nextLiquidityPoolCapital()) {
1831
+ this.setVaultCohortData(this.nextCohortId, entrant.vaultId.toNumber(), {
1832
+ activatedCapital: entrant.activatedCapital.toBigInt()
1833
+ });
1834
+ }
1835
+ await this.onVaultsUpdated(blockHash, new Set(vaultIds));
1836
+ this.print();
1837
+ }
1838
+ async watch() {
1839
+ await this.loadAt();
1840
+ await this.blockWatch.start();
1841
+ this.blockWatch.events.on(
1842
+ "vaults-updated",
1843
+ (b, v) => this.onVaultsUpdated(b.hash, v)
1844
+ );
1845
+ const api = await this.client;
1846
+ this.blockWatch.events.on("event", async (_, event) => {
1847
+ if (api.events.liquidityPools.BidPoolDistributed.is(event)) {
1848
+ const { cohortId: rawCohortId } = event.data;
1849
+ this.lastDistributedCohortId = rawCohortId.toNumber();
1850
+ this.bidPoolAmount = await this.getBidPool();
1851
+ this.cohortSubscriptions[rawCohortId.toNumber()]?.();
1852
+ const entrant = await api.query.liquidityPools.liquidityPoolsByCohort(rawCohortId);
1853
+ this.loadCohortData(rawCohortId.toNumber(), entrant);
1854
+ this.printDebounce();
1855
+ }
1856
+ if (api.events.liquidityPools.NextBidPoolCapitalLocked.is(event)) {
1857
+ const { cohortId } = event.data;
1858
+ for (let inc = 0; inc < 2; inc++) {
1859
+ const id = cohortId.toNumber() + inc;
1860
+ if (!this.cohortSubscriptions[id]) {
1861
+ this.cohortSubscriptions[id] = await api.query.liquidityPools.liquidityPoolsByCohort(
1862
+ id,
1863
+ async (entrant) => {
1864
+ this.loadCohortData(id, entrant);
1865
+ this.printDebounce();
1866
+ }
1867
+ );
1868
+ }
1869
+ }
1870
+ }
1871
+ });
1872
+ const unsubscribe = await api.queryMulti(
1873
+ [
1874
+ api.query.miningSlot.nextSlotCohort,
1875
+ api.query.miningSlot.nextCohortId,
1876
+ api.query.liquidityPools.openLiquidityPoolCapital,
1877
+ api.query.liquidityPools.nextLiquidityPoolCapital
1878
+ ],
1879
+ async ([
1880
+ _nextSlotCohort,
1881
+ nextCohortId,
1882
+ openVaultBidPoolCapital,
1883
+ nextPoolCapital
1884
+ ]) => {
1885
+ this.bidPoolAmount = await this.getBidPool();
1886
+ this.nextCohortId = nextCohortId.toNumber();
1887
+ for (const entrant of [
1888
+ ...openVaultBidPoolCapital,
1889
+ ...nextPoolCapital
1890
+ ]) {
1891
+ this.setVaultCohortData(
1892
+ entrant.cohortId.toNumber(),
1893
+ entrant.vaultId.toNumber(),
1894
+ {
1895
+ activatedCapital: entrant.activatedCapital.toBigInt()
1896
+ }
1897
+ );
1898
+ }
1899
+ this.printDebounce();
1900
+ }
1901
+ );
1902
+ return { unsubscribe };
1903
+ }
1904
+ async bondArgons(vaultId, amount, options) {
1905
+ const client = await this.client;
1906
+ const tx = client.tx.liquidityPools.bondArgons(vaultId, amount);
1907
+ const txSubmitter = new TxSubmitter(client, tx, this.keypair);
1908
+ const affordability = await txSubmitter.canAfford({
1909
+ tip: options?.tip,
1910
+ unavailableBalance: amount
1911
+ });
1912
+ if (!affordability.canAfford) {
1913
+ console.warn("Insufficient balance to bond argons to liquidity pool", {
1914
+ ...affordability,
1915
+ argonsNeeded: amount
1916
+ });
1917
+ throw new Error("Insufficient balance to bond argons to liquidity pool");
1918
+ }
1919
+ const result = await txSubmitter.submit({
1920
+ tip: options?.tip,
1921
+ useLatestNonce: true
1922
+ });
1923
+ await result.inBlockPromise;
1924
+ return result;
1925
+ }
1926
+ printDebounce() {
1927
+ if (this.printTimeout) {
1928
+ clearTimeout(this.printTimeout);
1929
+ }
1930
+ this.printTimeout = setTimeout(() => {
1931
+ this.print();
1932
+ }, 100);
1933
+ }
1934
+ getOperatorName(vaultId) {
1935
+ const vault = this.vaultsById[vaultId];
1936
+ return this.accountRegistry.getName(vault.operatorAccountId) ?? vault.operatorAccountId;
1937
+ }
1938
+ print() {
1939
+ console.clear();
1940
+ const lastDistributedCohortId = this.lastDistributedCohortId;
1941
+ const distributedCohort = this.poolVaultCapitalByCohort[this.lastDistributedCohortId ?? -1] ?? {};
1942
+ if (Object.keys(distributedCohort).length > 0) {
1943
+ console.log(`
1944
+
1945
+ Distributed (cohort ${lastDistributedCohortId})`);
1946
+ const rows = [];
1947
+ let maxWidth2 = 0;
1948
+ for (const [key, entry] of Object.entries(distributedCohort)) {
1949
+ const { table, width } = this.createBondCapitalTable(
1950
+ entry.earnings ?? 0n,
1951
+ entry.contributors ?? [],
1952
+ `Earnings (shared = ${formatPercent(entry.vaultSharingPercent)})`
1953
+ );
1954
+ if (width > maxWidth2) {
1955
+ maxWidth2 = width;
1956
+ }
1957
+ rows.push({
1958
+ Vault: key,
1959
+ Who: this.getOperatorName(Number(key)),
1960
+ Balances: table
1961
+ });
1962
+ }
1963
+ new Table({
1964
+ columns: [
1965
+ { name: "Vault", alignment: "left" },
1966
+ { name: "Who", alignment: "left" },
1967
+ {
1968
+ name: "Balances",
1969
+ title: "Contributor Balances",
1970
+ alignment: "center",
1971
+ minLen: maxWidth2
1972
+ }
1973
+ ],
1974
+ rows
1975
+ }).printTable();
1976
+ }
1977
+ console.log(
1978
+ `
1979
+
1980
+ Active Bid Pool: ${formatArgons(this.bidPoolAmount)} (cohort ${this.nextCohortId})`
1981
+ );
1982
+ const cohort = this.poolVaultCapitalByCohort[this.nextCohortId];
1983
+ if (Object.keys(cohort ?? {}).length > 0) {
1984
+ const rows = [];
1985
+ let maxWidth2 = 0;
1986
+ for (const [key, entry] of Object.entries(cohort)) {
1987
+ const { table, width } = this.createBondCapitalTable(
1988
+ entry.activatedCapital,
1989
+ entry.contributors ?? []
1990
+ );
1991
+ if (width > maxWidth2) {
1992
+ maxWidth2 = width;
1993
+ }
1994
+ rows.push({
1995
+ Vault: key,
1996
+ Who: this.getOperatorName(Number(key)),
1997
+ "Pool Capital": table
1998
+ });
1999
+ }
2000
+ new Table({
2001
+ columns: [
2002
+ { name: "Vault", alignment: "left" },
2003
+ { name: "Who", alignment: "left" },
2004
+ { name: "Pool Capital", alignment: "left", minLen: maxWidth2 }
2005
+ ],
2006
+ rows
2007
+ }).printTable();
2008
+ }
2009
+ const nextPool = this.poolVaultCapitalByCohort[this.nextCohortId + 1] ?? [];
2010
+ let maxWidth = 0;
2011
+ const nextCapital = [];
2012
+ for (const x of this.vaultSecuritization) {
2013
+ const entry = nextPool[x.vaultId] ?? {};
2014
+ const { table, width } = this.createBondCapitalTable(
2015
+ x.activatedSecuritization,
2016
+ entry.contributors ?? []
2017
+ );
2018
+ if (width > maxWidth) {
2019
+ maxWidth = width;
2020
+ }
2021
+ nextCapital.push({
2022
+ Vault: x.vaultId,
2023
+ Owner: this.getOperatorName(x.vaultId),
2024
+ "Bitcoin Space": formatArgons(x.bitcoinSpace),
2025
+ "Activated Securitization": `${formatArgons(x.activatedSecuritization)} / slot`,
2026
+ "Liquidity Pool": `${formatPercent(x.vaultSharingPercent)} profit sharing${table}`
2027
+ });
2028
+ }
2029
+ if (nextCapital.length) {
2030
+ console.log(`
2031
+
2032
+ Next (cohort ${this.nextCohortId + 1}):`);
2033
+ new Table({
2034
+ columns: [
2035
+ { name: "Vault", alignment: "left" },
2036
+ { name: "Owner", alignment: "left" },
2037
+ { name: "Bitcoin Space", alignment: "right" },
2038
+ { name: "Activated Securitization", alignment: "right" },
2039
+ { name: "Liquidity Pool", alignment: "left", minLen: maxWidth }
2040
+ ],
2041
+ rows: nextCapital
2042
+ }).printTable();
2043
+ }
2044
+ }
2045
+ setVaultCohortData(cohortId, vaultId, data) {
2046
+ this.poolVaultCapitalByCohort ??= {};
2047
+ this.poolVaultCapitalByCohort[cohortId] ??= {};
2048
+ this.poolVaultCapitalByCohort[cohortId][vaultId] ??= {
2049
+ activatedCapital: data.activatedCapital ?? data.contributors?.reduce((a, b) => a + b.amount, 0n) ?? 0n
2050
+ };
2051
+ Object.assign(
2052
+ this.poolVaultCapitalByCohort[cohortId][vaultId],
2053
+ filterUndefined(data)
2054
+ );
2055
+ }
2056
+ createBondCapitalTable(total, contributors, title = "Total") {
2057
+ const table = new Table({
2058
+ style: EMPTY_TABLE,
2059
+ columns: [
2060
+ { name: "who", title, minLen: 10, alignment: "right" },
2061
+ {
2062
+ name: "amount",
2063
+ title: formatArgons(total),
2064
+ minLen: 7,
2065
+ alignment: "left"
2066
+ }
2067
+ ]
2068
+ });
2069
+ for (const x of contributors) {
2070
+ table.addRow({
2071
+ who: this.accountRegistry.getName(x.address) ?? x.address,
2072
+ amount: formatArgons(x.amount)
2073
+ });
2074
+ }
2075
+ const str = table.render();
2076
+ const width = str.indexOf("\n");
2077
+ return { table: str, width };
2078
+ }
2079
+ loadCohortData(cohortId, vaultFunds) {
2080
+ for (const [vaultId, fund] of vaultFunds) {
2081
+ const vaultIdNumber = vaultId.toNumber();
2082
+ const contributors = fund.contributorBalances.map(([a, b]) => ({
2083
+ address: a.toHuman(),
2084
+ amount: b.toBigInt()
2085
+ }));
2086
+ if (fund.distributedProfits.isSome) {
2087
+ if (cohortId > (this.lastDistributedCohortId ?? 0)) {
2088
+ this.lastDistributedCohortId = cohortId;
2089
+ }
2090
+ }
2091
+ this.setVaultCohortData(cohortId, vaultIdNumber, {
2092
+ earnings: fund.distributedProfits.isSome ? fund.distributedProfits.unwrap().toBigInt() : void 0,
2093
+ vaultSharingPercent: convertPermillToBigNumber(
2094
+ fund.vaultSharingPercent.toBigInt()
2095
+ ),
2096
+ contributors
2097
+ });
2098
+ }
2099
+ }
2100
+ };
2101
+
2102
+ // src/BitcoinLocks.ts
2103
+ var SATS_PER_BTC = 100000000n;
2104
+ var BitcoinLocks = class _BitcoinLocks {
2105
+ constructor(client) {
2106
+ this.client = client;
2107
+ }
2108
+ async getMarketRate(satoshis) {
2109
+ const client = await this.client;
2110
+ const sats = client.createType("U64", satoshis.toString());
2111
+ const marketRate = await client.rpc.state.call(
2112
+ "BitcoinApis_market_rate",
2113
+ sats.toHex(true)
2114
+ );
2115
+ const rate = client.createType("Option<U128>", marketRate);
2116
+ if (!rate.isSome) {
2117
+ throw new Error("Market rate not available");
2118
+ }
2119
+ return rate.value.toBigInt();
2120
+ }
2121
+ async buildBitcoinLockTx(args) {
2122
+ const { vaultId, keypair, bitcoinXpub, tip } = args;
2123
+ let amount = args.amount;
2124
+ const marketRatePerBitcoin = await this.getMarketRate(100000000n);
2125
+ const client = await this.client;
2126
+ const account = await client.query.system.account(keypair.address);
2127
+ const freeBalance = account.data.free.toBigInt();
2128
+ let availableBalance = freeBalance;
2129
+ if (args.reducedBalanceBy) {
2130
+ availableBalance -= args.reducedBalanceBy;
2131
+ }
2132
+ const satoshisNeeded = amount * SATS_PER_BTC / marketRatePerBitcoin - 500n;
2133
+ const tx = client.tx.bitcoinLocks.initialize(
2134
+ vaultId,
2135
+ satoshisNeeded,
2136
+ bitcoinXpub
2137
+ );
2138
+ const existentialDeposit = client.consts.balances.existentialDeposit.toBigInt();
2139
+ const finalTip = tip ?? 0n;
2140
+ const fees = await tx.paymentInfo(keypair.address, { tip });
2141
+ const txFee = fees.partialFee.toBigInt();
2142
+ const tickDuration = (await client.query.ticks.genesisTicker()).tickDurationMillis.toNumber();
2143
+ const rawVault = await client.query.vaults.vaultsById(vaultId);
2144
+ const vault = new Vault(vaultId, rawVault.unwrap(), tickDuration);
2145
+ const btcFee = vault.calculateBitcoinFee(amount);
2146
+ const totalCharge = txFee + finalTip + btcFee;
2147
+ if (amount + totalCharge + existentialDeposit > availableBalance) {
2148
+ throw new Error("Insufficient balance to lock bitcoins");
2149
+ }
2150
+ console.log(
2151
+ `Locking ${satoshisNeeded} satoshis in vault ${vaultId} with market rate of ${formatArgons(marketRatePerBitcoin)}/btc. Xpub: ${bitcoinXpub}`
2152
+ );
2153
+ return { tx, txFee, btcFee, satoshis: satoshisNeeded, freeBalance };
2154
+ }
2155
+ static async waitForSpace(accountset, options) {
2156
+ const { argonAmount, bitcoinXpub, maxLockFee, tip = 0n } = options;
2157
+ const vaults = new VaultMonitor(accountset, {
2158
+ bitcoinSpaceAvailable: argonAmount
2159
+ });
2160
+ return new Promise(async (resolve, reject) => {
2161
+ vaults.events.on("bitcoin-space-above", async (vaultId, amount) => {
2162
+ const vault = vaults.vaultsById[vaultId];
2163
+ const fee = vault.calculateBitcoinFee(amount);
2164
+ console.log(
2165
+ `Vault ${vaultId} has ${formatArgons(amount)} argons available for bitcoin. Lock fee is ${formatArgons(fee)}`
2166
+ );
2167
+ if (maxLockFee !== void 0 && fee > maxLockFee) {
2168
+ console.log(
2169
+ `Skipping vault ${vaultId} due to high lock fee: ${formatArgons(maxLockFee)}`
2170
+ );
2171
+ return;
2172
+ }
2173
+ try {
2174
+ const bitcoinLock = new _BitcoinLocks(accountset.client);
2175
+ const { tx, satoshis, btcFee, txFee } = await bitcoinLock.buildBitcoinLockTx({
2176
+ vaultId,
2177
+ keypair: accountset.txSubmitterPair,
2178
+ amount: argonAmount,
2179
+ bitcoinXpub,
2180
+ tip
2181
+ });
2182
+ const result = await accountset.tx(tx).then((x) => x.submit({ waitForBlock: true, tip }));
2183
+ const client = await accountset.client;
2184
+ const utxoId = result.events.find((x) => client.events.bitcoinLocks.BitcoinLockCreated.is(x))?.data.utxoId?.toNumber();
2185
+ if (!utxoId) {
2186
+ throw new Error("Failed to find UTXO ID");
2187
+ }
2188
+ resolve({
2189
+ satoshis,
2190
+ argons: argonAmount,
2191
+ vaultId,
2192
+ btcFee,
2193
+ txFee,
2194
+ finalizedPromise: result.finalizedPromise,
2195
+ utxoId
2196
+ });
2197
+ } catch (err) {
2198
+ console.error("Error submitting bitcoin lock tx:", err);
2199
+ reject(err);
2200
+ } finally {
2201
+ vaults.stop();
2202
+ }
2203
+ });
2204
+ await vaults.monitor();
2205
+ });
2206
+ }
2207
+ };
2208
+
2209
+ // src/keyringUtils.ts
2210
+ import { promises } from "node:fs";
2211
+ import os from "node:os";
2212
+ var { readFile, writeFile } = promises;
2213
+ async function keyringFromCli(opts) {
2214
+ if (opts.accountSuri) {
2215
+ return keyringFromSuri(opts.accountSuri);
2216
+ }
2217
+ if (opts.accountFilePath) {
2218
+ return keyringFromFile({
2219
+ filePath: opts.accountFilePath,
2220
+ passphrase: opts.accountPassphrase
2221
+ });
2222
+ }
2223
+ throw new Error(
2224
+ "No ACCOUNT account loaded (either ACCOUNT_SURI or ACCOUNT_JSON_PATH required)"
2225
+ );
2226
+ }
2227
+ function keyringFromSuri(suri) {
2228
+ return new Keyring({ type: "sr25519" }).createFromUri(suri);
2229
+ }
2230
+ async function keyringFromFile(opts) {
2231
+ if (!opts.filePath) {
2232
+ throw new Error(
2233
+ "No ACCOUNT account loaded (either ACCOUNT_SURI or ACCOUNT_JSON_PATH required)"
2234
+ );
2235
+ }
2236
+ const path = opts.filePath.replace("~", os.homedir());
2237
+ const json = JSON.parse(await readFile(path, "utf-8"));
2238
+ const mainAccount = new Keyring().createFromJson(json);
2239
+ mainAccount.decodePkcs8(opts.passphrase);
2240
+ return mainAccount;
2241
+ }
2242
+ async function createKeyringPair(opts) {
2243
+ const { filePath, passphrase, cryptoType } = opts;
2244
+ const type = cryptoType ?? "sr25519";
2245
+ const seed = mnemonicGenerate();
2246
+ const keyring = new Keyring().createFromUri(seed, { type });
2247
+ if (filePath) {
2248
+ const json = keyring.toJson(passphrase);
2249
+ await writeFile(filePath, JSON.stringify(json, null, 2));
2250
+ }
2251
+ return keyring;
2252
+ }
2253
+
2254
+ // src/clis/index.ts
2255
+ import { Command as Command6, Option as Option2 } from "@commander-js/extra-typings";
2256
+
2257
+ // src/clis/accountCli.ts
2258
+ import { Command } from "@commander-js/extra-typings";
2259
+ import { printTable as printTable3 } from "console-table-printer";
2260
+ import { cryptoWaitReady } from "@polkadot/util-crypto";
2261
+ import { writeFileSync } from "node:fs";
2262
+ import * as process3 from "node:process";
2263
+
2264
+ // src/clis/index.ts
2265
+ import { configDotenv } from "dotenv";
2266
+ import Path from "node:path";
2267
+
2268
+ // src/clis/vaultCli.ts
2269
+ import { Command as Command2 } from "@commander-js/extra-typings";
2270
+ import BigNumber3 from "bignumber.js";
2271
+
2272
+ // src/clis/miningCli.ts
2273
+ import { Command as Command3 } from "@commander-js/extra-typings";
2274
+ import { printTable as printTable4 } from "console-table-printer";
2275
+
2276
+ // src/clis/liquidityCli.ts
2277
+ import { Command as Command4 } from "@commander-js/extra-typings";
2278
+
2279
+ // src/clis/bitcoinCli.ts
2280
+ import { Command as Command5 } from "@commander-js/extra-typings";
2281
+
2282
+ // src/index.ts
2283
+ export * from "@polkadot/types";
2284
+ export * from "@polkadot/types/interfaces";
2285
+ async function waitForLoad() {
2286
+ await cryptoWaitReady2();
2287
+ }
2288
+ async function getClient(host) {
2289
+ let provider;
2290
+ if (host.startsWith("http:")) {
2291
+ provider = new HttpProvider(host);
2292
+ } else {
2293
+ provider = new WsProvider(host);
2294
+ }
2295
+ return await ApiPromise.create({ provider, noInitWarn: true });
2296
+ }
2297
+ export {
2298
+ AccountMiners,
2299
+ AccountRegistry,
2300
+ Accountset,
2301
+ BidPool,
2302
+ BitcoinLocks,
2303
+ BlockWatch,
2304
+ CohortBidder,
2305
+ JsonExt,
2306
+ Keyring,
2307
+ MiningBids,
2308
+ MiningRotations,
2309
+ TxSubmitter,
2310
+ Vault,
2311
+ VaultMonitor,
2312
+ WageProtector,
2313
+ checkForExtrinsicSuccess,
2314
+ convertFixedU128ToBigNumber,
2315
+ convertPermillToBigNumber,
2316
+ createKeyringPair,
2317
+ decodeAddress,
2318
+ dispatchErrorToString,
2319
+ eventDataToJson,
2320
+ filterUndefined,
2321
+ formatArgons,
2322
+ formatPercent,
2323
+ getAuthorFromHeader,
2324
+ getClient,
2325
+ getTickFromHeader,
2326
+ gettersToObject,
2327
+ keyringFromCli,
2328
+ keyringFromFile,
2329
+ keyringFromSuri,
2330
+ mnemonicGenerate,
2331
+ waitForLoad
2332
+ };
2333
+ //# sourceMappingURL=index.js.map