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