@argonprotocol/mainchain 1.1.0-rc.7 → 1.1.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.
package/lib/index.cjs CHANGED
@@ -38,8 +38,11 @@ __export(index_exports, {
38
38
  BitcoinLocks: () => BitcoinLocks,
39
39
  BlockWatch: () => BlockWatch,
40
40
  CohortBidder: () => CohortBidder,
41
+ CohortBidderHistory: () => CohortBidderHistory,
42
+ ExtrinsicError: () => ExtrinsicError2,
41
43
  JsonExt: () => JsonExt,
42
44
  Keyring: () => import_api.Keyring,
45
+ MICROGONS_PER_ARGON: () => MICROGONS_PER_ARGON,
43
46
  MiningBids: () => MiningBids,
44
47
  MiningRotations: () => MiningRotations,
45
48
  TxSubmitter: () => TxSubmitter,
@@ -51,6 +54,7 @@ __export(index_exports, {
51
54
  convertPermillToBigNumber: () => convertPermillToBigNumber,
52
55
  createKeyringPair: () => createKeyringPair,
53
56
  decodeAddress: () => import_util_crypto.decodeAddress,
57
+ dispatchErrorToExtrinsicError: () => dispatchErrorToExtrinsicError,
54
58
  dispatchErrorToString: () => dispatchErrorToString,
55
59
  eventDataToJson: () => eventDataToJson,
56
60
  filterUndefined: () => filterUndefined,
@@ -150,119 +154,6 @@ var WageProtector = class _WageProtector {
150
154
  }
151
155
  };
152
156
 
153
- // src/utils.ts
154
- var BN = __toESM(require("bignumber.js"), 1);
155
- var { ROUND_FLOOR } = BN;
156
- function formatArgons(x) {
157
- const isNegative = x < 0;
158
- let format = (0, BN.default)(x.toString()).abs().div(1e6).toFormat(2, ROUND_FLOOR);
159
- if (format.endsWith(".00")) {
160
- format = format.slice(0, -3);
161
- }
162
- return `${isNegative ? "-" : ""}\u20B3${format}`;
163
- }
164
- function formatPercent(x) {
165
- if (!x) return "na";
166
- return `${x.times(100).decimalPlaces(3)}%`;
167
- }
168
- function filterUndefined(obj) {
169
- return Object.fromEntries(
170
- Object.entries(obj).filter(
171
- ([_, value]) => value !== void 0 && value !== null
172
- )
173
- );
174
- }
175
- async function gettersToObject(obj) {
176
- if (obj === null || obj === void 0 || typeof obj !== "object") return obj;
177
- const keys = [];
178
- for (const key in obj) {
179
- keys.push(key);
180
- }
181
- if (Symbol.iterator in obj) {
182
- const iterableToArray = [];
183
- for (const item of obj) {
184
- iterableToArray.push(await gettersToObject(item));
185
- }
186
- return iterableToArray;
187
- }
188
- const result = {};
189
- for (const key of keys) {
190
- const descriptor = Object.getOwnPropertyDescriptor(obj, key);
191
- if (descriptor && typeof descriptor.value === "function") {
192
- continue;
193
- }
194
- const value = descriptor && descriptor.get ? descriptor.get.call(obj) : obj[key];
195
- if (typeof value === "function") continue;
196
- result[key] = await gettersToObject(value);
197
- }
198
- return result;
199
- }
200
- function convertFixedU128ToBigNumber(fixedU128) {
201
- const decimalFactor = new BN.default(10).pow(new BN.default(18));
202
- const rawValue = new BN.default(fixedU128.toString());
203
- return rawValue.div(decimalFactor);
204
- }
205
- function convertPermillToBigNumber(permill) {
206
- const decimalFactor = new BN.default(1e6);
207
- const rawValue = new BN.default(permill.toString());
208
- return rawValue.div(decimalFactor);
209
- }
210
- function eventDataToJson(event) {
211
- const obj = {};
212
- event.data.forEach((data, index) => {
213
- const name = event.data.names?.[index];
214
- obj[name ?? `${index}`] = data.toJSON();
215
- });
216
- return obj;
217
- }
218
- function dispatchErrorToString(client, error) {
219
- let message = error.toString();
220
- if (error.isModule) {
221
- const decoded = client.registry.findMetaError(error.asModule);
222
- const { docs, name, section } = decoded;
223
- message = `${section}.${name}: ${docs.join(" ")}`;
224
- }
225
- return message;
226
- }
227
- function checkForExtrinsicSuccess(events, client) {
228
- return new Promise((resolve, reject) => {
229
- for (const { event } of events) {
230
- if (client.events.system.ExtrinsicSuccess.is(event)) {
231
- resolve();
232
- } else if (client.events.system.ExtrinsicFailed.is(event)) {
233
- const [dispatchError] = event.data;
234
- let errorInfo = dispatchError.toString();
235
- if (dispatchError.isModule) {
236
- const decoded = client.registry.findMetaError(dispatchError.asModule);
237
- errorInfo = `${decoded.section}.${decoded.name}`;
238
- }
239
- reject(
240
- new Error(
241
- `${event.section}.${event.method}:: ExtrinsicFailed:: ${errorInfo}`
242
- )
243
- );
244
- }
245
- }
246
- });
247
- }
248
- var JsonExt = class {
249
- static stringify(obj, space) {
250
- return JSON.stringify(
251
- obj,
252
- (_, v) => typeof v === "bigint" ? `${v}n` : v,
253
- space
254
- );
255
- }
256
- static parse(str) {
257
- return JSON.parse(str, (_, v) => {
258
- if (typeof v === "string" && v.endsWith("n")) {
259
- return BigInt(v.slice(0, -1));
260
- }
261
- return v;
262
- });
263
- }
264
- };
265
-
266
157
  // src/TxSubmitter.ts
267
158
  function logExtrinsicResult(result) {
268
159
  if (process.env.DEBUG) {
@@ -394,13 +285,12 @@ var TxResult = class {
394
285
  }
395
286
  }
396
287
  if (encounteredError) {
397
- const error = dispatchErrorToString(this.client, encounteredError);
398
- if (batchErrorIndex) {
399
- this.reject(
400
- new Error(`Error in batch#${batchErrorIndex}: ${error.toString()}`)
401
- );
402
- }
403
- this.reject(new Error(`Transaction failed: ${error}`));
288
+ const error = dispatchErrorToExtrinsicError(
289
+ this.client,
290
+ encounteredError,
291
+ batchErrorIndex
292
+ );
293
+ this.reject(error);
404
294
  } else {
405
295
  this.inBlockResolve(status.asInBlock.toU8a());
406
296
  }
@@ -415,6 +305,147 @@ var TxResult = class {
415
305
  }
416
306
  };
417
307
 
308
+ // src/utils.ts
309
+ var BN = __toESM(require("bignumber.js"), 1);
310
+ var { ROUND_FLOOR } = BN;
311
+ var MICROGONS_PER_ARGON = 1e6;
312
+ function formatArgons(x) {
313
+ if (x === void 0 || x === null) return "na";
314
+ const isNegative = x < 0;
315
+ let format = (0, BN.default)(x.toString()).abs().div(MICROGONS_PER_ARGON).toFormat(2, ROUND_FLOOR);
316
+ if (format.endsWith(".00")) {
317
+ format = format.slice(0, -3);
318
+ }
319
+ return `${isNegative ? "-" : ""}\u20B3${format}`;
320
+ }
321
+ function formatPercent(x) {
322
+ if (!x) return "na";
323
+ return `${x.times(100).decimalPlaces(3)}%`;
324
+ }
325
+ function filterUndefined(obj) {
326
+ return Object.fromEntries(
327
+ Object.entries(obj).filter(
328
+ ([_, value]) => value !== void 0 && value !== null
329
+ )
330
+ );
331
+ }
332
+ async function gettersToObject(obj) {
333
+ if (obj === null || obj === void 0 || typeof obj !== "object") return obj;
334
+ const keys = [];
335
+ for (const key in obj) {
336
+ keys.push(key);
337
+ }
338
+ if (Symbol.iterator in obj) {
339
+ const iterableToArray = [];
340
+ for (const item of obj) {
341
+ iterableToArray.push(await gettersToObject(item));
342
+ }
343
+ return iterableToArray;
344
+ }
345
+ const result = {};
346
+ for (const key of keys) {
347
+ const descriptor = Object.getOwnPropertyDescriptor(obj, key);
348
+ if (descriptor && typeof descriptor.value === "function") {
349
+ continue;
350
+ }
351
+ const value = descriptor && descriptor.get ? descriptor.get.call(obj) : obj[key];
352
+ if (typeof value === "function") continue;
353
+ result[key] = await gettersToObject(value);
354
+ }
355
+ return result;
356
+ }
357
+ function convertFixedU128ToBigNumber(fixedU128) {
358
+ const decimalFactor = new BN.default(10).pow(new BN.default(18));
359
+ const rawValue = new BN.default(fixedU128.toString());
360
+ return rawValue.div(decimalFactor);
361
+ }
362
+ function convertPermillToBigNumber(permill) {
363
+ const decimalFactor = new BN.default(1e6);
364
+ const rawValue = new BN.default(permill.toString());
365
+ return rawValue.div(decimalFactor);
366
+ }
367
+ function eventDataToJson(event) {
368
+ const obj = {};
369
+ event.data.forEach((data, index) => {
370
+ const name = event.data.names?.[index];
371
+ obj[name ?? `${index}`] = data.toJSON();
372
+ });
373
+ return obj;
374
+ }
375
+ function dispatchErrorToString(client, error) {
376
+ let message = error.toString();
377
+ if (error.isModule) {
378
+ const decoded = client.registry.findMetaError(error.asModule);
379
+ const { docs, name, section } = decoded;
380
+ message = `${section}.${name}: ${docs.join(" ")}`;
381
+ }
382
+ return message;
383
+ }
384
+ var ExtrinsicError2 = class extends Error {
385
+ constructor(errorCode, details, batchInterruptedIndex) {
386
+ super(errorCode);
387
+ this.errorCode = errorCode;
388
+ this.details = details;
389
+ this.batchInterruptedIndex = batchInterruptedIndex;
390
+ }
391
+ toString() {
392
+ if (this.batchInterruptedIndex !== void 0) {
393
+ return `${this.errorCode} ${this.details ?? ""} (Batch interrupted at index ${this.batchInterruptedIndex})`;
394
+ }
395
+ return `${this.errorCode} ${this.details ?? ""}`;
396
+ }
397
+ };
398
+ function dispatchErrorToExtrinsicError(client, error, batchInterruptedIndex) {
399
+ if (error.isModule) {
400
+ const decoded = client.registry.findMetaError(error.asModule);
401
+ const { docs, name, section } = decoded;
402
+ return new ExtrinsicError2(
403
+ `${section}.${name}`,
404
+ docs.join(" "),
405
+ batchInterruptedIndex
406
+ );
407
+ }
408
+ return new ExtrinsicError2(error.toString(), void 0, batchInterruptedIndex);
409
+ }
410
+ function checkForExtrinsicSuccess(events, client) {
411
+ return new Promise((resolve, reject) => {
412
+ for (const { event } of events) {
413
+ if (client.events.system.ExtrinsicSuccess.is(event)) {
414
+ resolve();
415
+ } else if (client.events.system.ExtrinsicFailed.is(event)) {
416
+ const [dispatchError] = event.data;
417
+ let errorInfo = dispatchError.toString();
418
+ if (dispatchError.isModule) {
419
+ const decoded = client.registry.findMetaError(dispatchError.asModule);
420
+ errorInfo = `${decoded.section}.${decoded.name}`;
421
+ }
422
+ reject(
423
+ new Error(
424
+ `${event.section}.${event.method}:: ExtrinsicFailed:: ${errorInfo}`
425
+ )
426
+ );
427
+ }
428
+ }
429
+ });
430
+ }
431
+ var JsonExt = class {
432
+ static stringify(obj, space) {
433
+ return JSON.stringify(
434
+ obj,
435
+ (_, v) => typeof v === "bigint" ? `${v}n` : v,
436
+ space
437
+ );
438
+ }
439
+ static parse(str) {
440
+ return JSON.parse(str, (_, v) => {
441
+ if (typeof v === "string" && v.endsWith("n")) {
442
+ return BigInt(v.slice(0, -1));
443
+ }
444
+ return v;
445
+ });
446
+ }
447
+ };
448
+
418
449
  // src/AccountRegistry.ts
419
450
  var AccountRegistry = class _AccountRegistry {
420
451
  namedAccounts = /* @__PURE__ */ new Map();
@@ -827,6 +858,14 @@ var Accountset = class {
827
858
  );
828
859
  }
829
860
  }
861
+ async submitterBalance(blockHash) {
862
+ const client = await this.client;
863
+ const api = blockHash ? await client.at(blockHash) : client;
864
+ const accountData = await api.query.system.account(
865
+ this.txSubmitterPair.address
866
+ );
867
+ return accountData.data.free.toBigInt();
868
+ }
830
869
  async balance(blockHash) {
831
870
  const client = await this.client;
832
871
  const api = blockHash ? await client.at(blockHash) : client;
@@ -1089,18 +1128,18 @@ var Accountset = class {
1089
1128
  return new TxSubmitter(client, tx, this.txSubmitterPair);
1090
1129
  }
1091
1130
  /**
1092
- * Create a mining bid. This will create a bid for each account in the given range from the seed account as funding.
1131
+ * Create but don't submit a mining bid transaction.
1132
+ * @param options
1093
1133
  */
1094
- async createMiningBids(options) {
1095
- const accounts = this.getAccountsInRange(options.subaccountRange);
1134
+ async createMiningBidTx(options) {
1096
1135
  const client = await this.client;
1097
- let tip = options.tip ?? 0n;
1136
+ const { bidAmount, subaccounts, sendRewardsToSeed } = options;
1098
1137
  const batch = client.tx.utility.batch(
1099
- accounts.map((x) => {
1138
+ subaccounts.map((x) => {
1100
1139
  const keys = this.keys();
1101
- const rewards = options.sendRewardsToSeed ? { Account: this.seedAddress } : { Owner: null };
1140
+ const rewards = sendRewardsToSeed ? { Account: this.seedAddress } : { Owner: null };
1102
1141
  return client.tx.miningSlot.bid(
1103
- options.bidAmount,
1142
+ bidAmount,
1104
1143
  rewards,
1105
1144
  {
1106
1145
  grandpa: keys.gran.rawPublicKey,
@@ -1114,7 +1153,19 @@ var Accountset = class {
1114
1153
  if (this.isProxy) {
1115
1154
  tx = client.tx.proxy.proxy(this.seedAddress, "MiningBid", batch);
1116
1155
  }
1117
- const submitter = new TxSubmitter(client, tx, this.txSubmitterPair);
1156
+ return new TxSubmitter(client, tx, this.txSubmitterPair);
1157
+ }
1158
+ /**
1159
+ * Create a mining bid. This will create a bid for each account in the given range from the seed account as funding.
1160
+ */
1161
+ async createMiningBids(options) {
1162
+ const accounts = this.getAccountsInRange(options.subaccountRange);
1163
+ const client = await this.client;
1164
+ const submitter = await this.createMiningBidTx({
1165
+ ...options,
1166
+ subaccounts: accounts
1167
+ });
1168
+ const { tip = 0n } = options;
1118
1169
  const txFee = await submitter.feeEstimate(tip);
1119
1170
  let minBalance = options.bidAmount * BigInt(accounts.length);
1120
1171
  let totalFees = tip + 1n + txFee;
@@ -1558,27 +1609,24 @@ var VaultMonitor = class {
1558
1609
  }
1559
1610
  };
1560
1611
 
1561
- // src/CohortBidder.ts
1562
- var CohortBidder = class _CohortBidder {
1563
- constructor(accountset, cohortId, subaccounts, options) {
1564
- this.accountset = accountset;
1612
+ // src/CohortBidderHistory.ts
1613
+ var CohortBidderHistory = class _CohortBidderHistory {
1614
+ constructor(cohortId, subaccounts) {
1565
1615
  this.cohortId = cohortId;
1566
1616
  this.subaccounts = subaccounts;
1567
- this.options = options;
1617
+ this.maxSeatsInPlay = this.subaccounts.length;
1568
1618
  this.subaccounts.forEach((x) => {
1569
- this.allAddresses.add(x.address);
1619
+ this.myAddresses.add(x.address);
1570
1620
  });
1571
1621
  }
1572
- get client() {
1573
- return this.accountset.client;
1574
- }
1622
+ bidHistory = [];
1575
1623
  stats = {
1576
1624
  // number of seats won
1577
- seats: 0,
1625
+ seatsWon: 0,
1578
1626
  // sum of argons bid in successful bids
1579
1627
  totalArgonsBid: 0n,
1580
1628
  // total number of bids placed (includes 1 per seat)
1581
- bids: 0,
1629
+ bidsAttempted: 0,
1582
1630
  // fees including the tip
1583
1631
  fees: 0n,
1584
1632
  // Max bid per seat
@@ -1590,16 +1638,157 @@ var CohortBidder = class _CohortBidder {
1590
1638
  // The cohort expected argons per block
1591
1639
  cohortArgonsPerBlock: 0n,
1592
1640
  // The last block that bids are synced to
1593
- lastBlock: 0
1641
+ lastBlockNumber: 0
1594
1642
  };
1643
+ lastBids = [];
1644
+ myAddresses = /* @__PURE__ */ new Set();
1645
+ maxSeatsInPlay = 0;
1646
+ async init(client) {
1647
+ if (!this.stats.argonotsPerSeat) {
1648
+ const startingStats = await _CohortBidderHistory.getStartingData(client);
1649
+ Object.assign(this.stats, startingStats);
1650
+ }
1651
+ }
1652
+ maybeReducingSeats(maxSeats, reason, historyEntry) {
1653
+ if (this.maxSeatsInPlay > maxSeats) {
1654
+ historyEntry.maxSeatsReductionReason = reason;
1655
+ }
1656
+ this.maxSeatsInPlay = maxSeats;
1657
+ historyEntry.maxSeatsInPlay = maxSeats;
1658
+ }
1659
+ trackChange(next, blockNumber, tick, isLastEntry = false) {
1660
+ let winningBids = 0;
1661
+ let totalArgonsBid = 0n;
1662
+ const nextEntrants = [];
1663
+ for (const x of next) {
1664
+ const bid = x.bid.toBigInt();
1665
+ const address = x.accountId.toHuman();
1666
+ nextEntrants.push({ address, bid });
1667
+ if (this.myAddresses.has(address)) {
1668
+ winningBids++;
1669
+ totalArgonsBid += bid;
1670
+ }
1671
+ }
1672
+ this.stats.seatsWon = winningBids;
1673
+ this.stats.totalArgonsBid = totalArgonsBid;
1674
+ this.stats.lastBlockNumber = Math.max(
1675
+ blockNumber,
1676
+ this.stats.lastBlockNumber
1677
+ );
1678
+ const historyEntry = {
1679
+ cohortId: this.cohortId,
1680
+ blockNumber,
1681
+ tick,
1682
+ bidChanges: [],
1683
+ winningSeats: winningBids,
1684
+ maxSeatsInPlay: this.maxSeatsInPlay
1685
+ };
1686
+ const hasDiffs = JsonExt.stringify(nextEntrants) !== JsonExt.stringify(this.lastBids);
1687
+ if (!isLastEntry || hasDiffs) {
1688
+ this.bidHistory.unshift(historyEntry);
1689
+ }
1690
+ if (hasDiffs) {
1691
+ nextEntrants.forEach(({ address, bid }, i) => {
1692
+ const prevBidIndex = this.lastBids.findIndex(
1693
+ (y) => y.address === address
1694
+ );
1695
+ const entry = {
1696
+ address,
1697
+ bidAmount: bid,
1698
+ bidPosition: i,
1699
+ prevPosition: prevBidIndex === -1 ? null : prevBidIndex
1700
+ };
1701
+ if (prevBidIndex !== -1) {
1702
+ const prevBidAmount = this.lastBids[prevBidIndex].bid;
1703
+ if (prevBidAmount !== bid) {
1704
+ entry.prevBidAmount = prevBidAmount;
1705
+ }
1706
+ }
1707
+ historyEntry.bidChanges.push(entry);
1708
+ });
1709
+ this.lastBids.forEach(({ address, bid }, i) => {
1710
+ const nextBid = nextEntrants.some((y) => y.address === address);
1711
+ if (!nextBid) {
1712
+ historyEntry.bidChanges.push({
1713
+ address,
1714
+ bidAmount: bid,
1715
+ bidPosition: null,
1716
+ prevPosition: i
1717
+ });
1718
+ }
1719
+ });
1720
+ this.lastBids = nextEntrants;
1721
+ }
1722
+ return historyEntry;
1723
+ }
1724
+ onBidResult(historyEntry, param) {
1725
+ const {
1726
+ txFeePlusTip,
1727
+ bidPerSeat,
1728
+ bidsAttempted,
1729
+ successfulBids,
1730
+ blockNumber,
1731
+ bidError
1732
+ } = param;
1733
+ this.stats.fees += txFeePlusTip;
1734
+ this.stats.bidsAttempted += bidsAttempted;
1735
+ if (bidPerSeat > this.stats.maxBidPerSeat) {
1736
+ this.stats.maxBidPerSeat = bidPerSeat;
1737
+ }
1738
+ if (blockNumber !== void 0) {
1739
+ this.stats.lastBlockNumber = Math.max(
1740
+ blockNumber,
1741
+ this.stats.lastBlockNumber
1742
+ );
1743
+ }
1744
+ historyEntry.myBidsPlaced.failureReason = bidError;
1745
+ historyEntry.myBidsPlaced.successfulBids = successfulBids;
1746
+ historyEntry.myBidsPlaced.txFeePlusTip = txFeePlusTip;
1747
+ }
1748
+ static async getStartingData(api) {
1749
+ const argonotPrice = await api.query.priceIndex.current();
1750
+ let argonotUsdPrice = 0;
1751
+ if (argonotPrice.isSome) {
1752
+ argonotUsdPrice = convertFixedU128ToBigNumber(
1753
+ argonotPrice.unwrap().argonotUsdPrice.toBigInt()
1754
+ ).toNumber();
1755
+ }
1756
+ const argonotsPerSeat = await api.query.miningSlot.argonotsPerMiningSeat().then((x) => x.toBigInt());
1757
+ const cohortArgonsPerBlock = await api.query.blockRewards.argonsPerBlock().then((x) => x.toBigInt());
1758
+ return { argonotsPerSeat, argonotUsdPrice, cohortArgonsPerBlock };
1759
+ }
1760
+ };
1761
+
1762
+ // src/CohortBidder.ts
1763
+ var CohortBidder = class {
1764
+ constructor(accountset, cohortId, subaccounts, options) {
1765
+ this.accountset = accountset;
1766
+ this.cohortId = cohortId;
1767
+ this.subaccounts = subaccounts;
1768
+ this.options = options;
1769
+ this.history = new CohortBidderHistory(cohortId, subaccounts);
1770
+ this.subaccounts.forEach((x) => {
1771
+ this.myAddresses.add(x.address);
1772
+ });
1773
+ }
1774
+ get client() {
1775
+ return this.accountset.client;
1776
+ }
1777
+ get stats() {
1778
+ return this.history.stats;
1779
+ }
1780
+ get bidHistory() {
1781
+ return this.history.bidHistory;
1782
+ }
1595
1783
  unsubscribe;
1596
1784
  pendingRequest;
1597
1785
  retryTimeout;
1598
1786
  isStopped = false;
1599
1787
  needsRebid = false;
1600
1788
  lastBidTime = 0;
1789
+ history;
1601
1790
  millisPerTick;
1602
- allAddresses = /* @__PURE__ */ new Set();
1791
+ myAddresses = /* @__PURE__ */ new Set();
1603
1792
  async stop() {
1604
1793
  if (this.isStopped) return this.stats;
1605
1794
  this.isStopped = true;
@@ -1609,70 +1798,59 @@ var CohortBidder = class _CohortBidder {
1609
1798
  this.unsubscribe();
1610
1799
  }
1611
1800
  const client = await this.client;
1612
- const [nextCohort, nextCohortId, blockNumber] = await new Promise(async (resolve) => {
1613
- const unsub = await client.queryMulti(
1614
- [
1615
- client.query.miningSlot.nextSlotCohort,
1616
- client.query.miningSlot.nextCohortId,
1617
- client.query.miningSlot.isNextSlotBiddingOpen,
1618
- client.query.system.number
1619
- ],
1620
- ([nextCohort2, nextCohortId2, isBiddingStillOpen, blockNumber2]) => {
1621
- if (nextCohortId2.toNumber() !== this.cohortId || isBiddingStillOpen.isFalse) {
1622
- unsub();
1623
- resolve([nextCohort2, nextCohortId2, blockNumber2]);
1801
+ const [nextCohortId, isBiddingOpen] = await client.queryMulti([
1802
+ client.query.miningSlot.nextCohortId,
1803
+ client.query.miningSlot.isNextSlotBiddingOpen
1804
+ ]);
1805
+ if (nextCohortId.toNumber() === this.cohortId && isBiddingOpen.isTrue) {
1806
+ console.log("Bidding is still open, waiting for it to close");
1807
+ await new Promise(async (resolve) => {
1808
+ const unsub = await client.query.miningSlot.isNextSlotBiddingOpen(
1809
+ (isOpen) => {
1810
+ if (isOpen.isFalse) {
1811
+ unsub();
1812
+ resolve();
1813
+ }
1624
1814
  }
1625
- }
1626
- );
1627
- });
1628
- void await this.pendingRequest;
1629
- if (nextCohortId.toNumber() === this.cohortId) {
1630
- console.log("Bidder updating stats with bid queue");
1631
- this.updateStats(nextCohort);
1632
- } else {
1633
- const bestBlock = await client.rpc.chain.getBlockHash();
1634
- const api = await client.at(bestBlock);
1635
- const wonIndices = await api.query.miningSlot.accountIndexLookup.multi([...this.allAddresses]).then((x) => x.filter((x2) => x2.isSome).map((x2) => x2.value));
1636
- const wonSeats = await api.query.miningSlot.activeMinersByIndex.multi(wonIndices).then(
1637
- (x) => x.filter(
1638
- (x2) => x2.isSome && x2.value.cohortId.toNumber() === this.cohortId
1639
- ).map((x2) => x2.value)
1640
- );
1641
- console.log("Bidder updating stats with finalized cohort");
1642
- this.updateStats(wonSeats);
1815
+ );
1816
+ });
1643
1817
  }
1644
- this.stats.lastBlock = Math.max(
1645
- blockNumber.toNumber(),
1646
- this.stats.lastBlock
1647
- );
1818
+ void await this.pendingRequest;
1819
+ let header = await client.rpc.chain.getHeader();
1820
+ while (true) {
1821
+ const api2 = await client.at(header.hash);
1822
+ const cohortId = await api2.query.miningSlot.nextCohortId();
1823
+ if (cohortId.toNumber() === this.cohortId) {
1824
+ break;
1825
+ }
1826
+ header = await client.rpc.chain.getHeader(header.parentHash);
1827
+ }
1828
+ const api = await client.at(header.hash);
1829
+ const tick = await api.query.ticks.currentTick().then((x) => x.toNumber());
1830
+ const cohort = await api.query.miningSlot.nextSlotCohort();
1831
+ this.history.trackChange(cohort, header.number.toNumber(), tick, true);
1832
+ console.log("Bidder stopped", {
1833
+ cohortId: this.cohortId,
1834
+ blockNumber: header.number.toNumber(),
1835
+ tick,
1836
+ cohort: cohort.map((x) => ({
1837
+ address: x.accountId.toHuman(),
1838
+ bid: x.bid.toBigInt()
1839
+ }))
1840
+ });
1648
1841
  return this.stats;
1649
1842
  }
1650
- static async getStartingData(api) {
1651
- const argonotPrice = await api.query.priceIndex.current();
1652
- let argonotUsdPrice = 0;
1653
- if (argonotPrice.isSome) {
1654
- argonotUsdPrice = convertFixedU128ToBigNumber(
1655
- argonotPrice.unwrap().argonotUsdPrice.toBigInt()
1656
- ).toNumber();
1657
- }
1658
- const argonotsPerSeat = await api.query.miningSlot.argonotsPerMiningSeat().then((x) => x.toBigInt());
1659
- const cohortArgonsPerBlock = await api.query.blockRewards.argonsPerBlock().then((x) => x.toBigInt());
1660
- return { argonotsPerSeat, argonotUsdPrice, cohortArgonsPerBlock };
1661
- }
1662
1843
  async start() {
1663
1844
  console.log(`Starting cohort ${this.cohortId} bidder`, {
1664
1845
  maxBid: formatArgons(this.options.maxBid),
1665
1846
  minBid: formatArgons(this.options.minBid),
1666
1847
  bidIncrement: formatArgons(this.options.bidIncrement),
1667
- maxBalance: formatArgons(this.options.maxBalance),
1848
+ maxBudget: formatArgons(this.options.maxBudget),
1668
1849
  bidDelay: this.options.bidDelay,
1669
1850
  subaccounts: this.subaccounts
1670
1851
  });
1671
1852
  const client = await this.client;
1672
- if (!this.stats.argonotsPerSeat) {
1673
- const startingStats = await _CohortBidder.getStartingData(client);
1674
- Object.assign(this.stats, startingStats);
1675
- }
1853
+ await this.history.init(client);
1676
1854
  this.millisPerTick ??= await client.query.ticks.genesisTicker().then((x) => x.tickDurationMillis.toNumber());
1677
1855
  this.unsubscribe = await client.queryMulti(
1678
1856
  [
@@ -1686,24 +1864,27 @@ var CohortBidder = class _CohortBidder {
1686
1864
  }
1687
1865
  );
1688
1866
  }
1689
- updateStats(next) {
1690
- let seats = 0;
1691
- let totalArgonsBid = 0n;
1692
- for (const x of next) {
1693
- if (this.allAddresses.has(x.accountId.toHuman())) {
1694
- seats++;
1695
- totalArgonsBid += x.bid.toBigInt();
1696
- }
1697
- }
1698
- this.stats.seats = seats;
1699
- this.stats.totalArgonsBid = totalArgonsBid;
1700
- }
1701
1867
  async checkSeats(next) {
1702
- if (this.isStopped || this.pendingRequest) return;
1868
+ if (this.isStopped) return;
1869
+ clearTimeout(this.retryTimeout);
1870
+ const client = await this.client;
1871
+ const bestBlock = await client.rpc.chain.getBlockHash();
1872
+ const api = await client.at(bestBlock);
1873
+ const blockNumber = await api.query.system.number().then((x) => x.toNumber());
1874
+ if (this.bidHistory[0]?.blockNumber >= blockNumber) {
1875
+ return;
1876
+ }
1877
+ const tick = await api.query.ticks.currentTick().then((x) => x.toNumber());
1878
+ const historyEntry = this.history.trackChange(next, blockNumber, tick);
1879
+ if (this.pendingRequest) return;
1703
1880
  const ticksSinceLastBid = Math.floor(
1704
1881
  (Date.now() - this.lastBidTime) / this.millisPerTick
1705
1882
  );
1706
1883
  if (ticksSinceLastBid < this.options.bidDelay) {
1884
+ this.retryTimeout = setTimeout(
1885
+ () => void this.checkCurrentSeats(),
1886
+ this.millisPerTick
1887
+ );
1707
1888
  return;
1708
1889
  }
1709
1890
  console.log(
@@ -1711,12 +1892,19 @@ var CohortBidder = class _CohortBidder {
1711
1892
  this.cohortId,
1712
1893
  this.subaccounts.map((x) => x.index)
1713
1894
  );
1714
- this.updateStats(next);
1715
- this.needsRebid = this.subaccounts.some(
1716
- (x) => !next.some((y) => y.accountId.toHuman() === x.address)
1717
- );
1895
+ const winningBids = historyEntry.winningSeats;
1896
+ this.needsRebid = winningBids < this.subaccounts.length;
1718
1897
  if (!this.needsRebid) return;
1719
- const lowestBid = next.at(-1)?.bid.toBigInt() ?? -this.options.bidIncrement;
1898
+ const winningAddresses = new Set(next.map((x) => x.accountId.toHuman()));
1899
+ let lowestBid = -this.options.bidIncrement;
1900
+ if (next.length) {
1901
+ for (let i = next.length - 1; i >= 0; i--) {
1902
+ if (!this.myAddresses.has(next[i].accountId.toHuman())) {
1903
+ lowestBid = next.at(i).bid.toBigInt();
1904
+ break;
1905
+ }
1906
+ }
1907
+ }
1720
1908
  const MIN_INCREMENT = 10000n;
1721
1909
  let nextBid = lowestBid + this.options.bidIncrement;
1722
1910
  if (nextBid < this.options.minBid) {
@@ -1724,83 +1912,119 @@ var CohortBidder = class _CohortBidder {
1724
1912
  }
1725
1913
  if (nextBid > this.options.maxBid) {
1726
1914
  nextBid = this.options.maxBid;
1727
- if (nextBid - lowestBid < MIN_INCREMENT) {
1728
- console.log(
1729
- `Can't make any more bids for ${this.cohortId} with given constraints.`,
1730
- {
1731
- lowestCurrentBid: formatArgons(lowestBid),
1732
- nextAttemptedBid: formatArgons(nextBid),
1733
- maxBid: formatArgons(this.options.maxBid)
1734
- }
1735
- );
1736
- return;
1915
+ }
1916
+ const fakeTx = await this.accountset.createMiningBidTx({
1917
+ subaccounts: this.subaccounts,
1918
+ bidAmount: nextBid,
1919
+ sendRewardsToSeed: true
1920
+ });
1921
+ let availableBalanceForBids = await api.query.system.account(this.accountset.txSubmitterPair.address).then((x) => x.data.free.toBigInt());
1922
+ for (const bid of next) {
1923
+ if (this.myAddresses.has(bid.accountId.toHuman())) {
1924
+ availableBalanceForBids += bid.bid.toBigInt();
1737
1925
  }
1738
1926
  }
1927
+ const tip = this.options.tipPerTransaction ?? 0n;
1928
+ const feeEstimate = await fakeTx.feeEstimate(tip);
1929
+ const feePlusTip = feeEstimate + tip;
1930
+ let budgetForSeats = this.options.maxBudget - feePlusTip;
1931
+ if (budgetForSeats > availableBalanceForBids) {
1932
+ budgetForSeats = availableBalanceForBids - feePlusTip;
1933
+ }
1739
1934
  if (nextBid < lowestBid) {
1740
1935
  console.log(
1741
1936
  `Can't bid ${formatArgons(nextBid)}. Current lowest bid is ${formatArgons(
1742
1937
  lowestBid
1743
1938
  )}.`
1744
1939
  );
1940
+ this.history.maybeReducingSeats(
1941
+ winningBids,
1942
+ "MaxBidTooLow" /* MaxBidTooLow */,
1943
+ historyEntry
1944
+ );
1745
1945
  return;
1746
1946
  }
1747
- const seatsInBudget = nextBid === 0n ? this.subaccounts.length : Number(this.options.maxBalance / nextBid);
1748
- if (seatsInBudget <= 0) {
1947
+ if (nextBid - lowestBid < MIN_INCREMENT) {
1749
1948
  console.log(
1750
- `Can't afford any seats at ${formatArgons(nextBid)}. Would exceed our max balance of ${formatArgons(this.options.maxBalance)}.`
1949
+ `Can't make any more bids for ${this.cohortId} with given constraints.`,
1950
+ {
1951
+ lowestCurrentBid: formatArgons(lowestBid),
1952
+ nextAttemptedBid: formatArgons(nextBid),
1953
+ maxBid: formatArgons(this.options.maxBid)
1954
+ }
1955
+ );
1956
+ this.history.maybeReducingSeats(
1957
+ winningBids,
1958
+ "MaxBidTooLow" /* MaxBidTooLow */,
1959
+ historyEntry
1751
1960
  );
1752
1961
  return;
1753
1962
  }
1754
- if (this.subaccounts.length > seatsInBudget) {
1755
- const toKeep = [];
1756
- for (const account of this.subaccounts) {
1757
- if (toKeep.length >= seatsInBudget) break;
1758
- if (account.isRebid) {
1759
- toKeep.push(account);
1760
- }
1761
- }
1762
- for (const account of this.subaccounts) {
1763
- if (toKeep.length >= seatsInBudget) break;
1764
- if (!account.isRebid) {
1765
- toKeep.push(account);
1766
- }
1767
- }
1768
- const removedIndices = this.subaccounts.filter((x) => !toKeep.some((y) => y.index === x.index)).map((x) => x.index);
1769
- this.subaccounts = toKeep;
1770
- console.log("Had to remove some subaccounts to fit in budget:", {
1771
- removedIndices,
1772
- seatsInBudget,
1773
- budget: formatArgons(this.options.maxBalance)
1963
+ const seatsInBudget = nextBid === 0n ? this.subaccounts.length : Number(budgetForSeats / nextBid);
1964
+ let accountsToUse = [...this.subaccounts];
1965
+ if (accountsToUse.length > seatsInBudget) {
1966
+ const reason = availableBalanceForBids - feePlusTip < nextBid * BigInt(seatsInBudget) ? "InsufficientFunds" /* InsufficientFunds */ : "MaxBudgetTooLow" /* MaxBudgetTooLow */;
1967
+ this.history.maybeReducingSeats(seatsInBudget, reason, historyEntry);
1968
+ accountsToUse.sort((a, b) => {
1969
+ const isWinningA = winningAddresses.has(a.address);
1970
+ const isWinningB = winningAddresses.has(b.address);
1971
+ if (isWinningA && !isWinningB) return -1;
1972
+ if (!isWinningA && isWinningB) return 1;
1973
+ if (a.isRebid && !b.isRebid) return -1;
1974
+ if (!a.isRebid && b.isRebid) return 1;
1975
+ return a.index - b.index;
1774
1976
  });
1977
+ accountsToUse.length = seatsInBudget;
1978
+ }
1979
+ if (accountsToUse.length > winningBids) {
1980
+ historyEntry.myBidsPlaced = {
1981
+ bids: accountsToUse.length,
1982
+ bidPerSeat: nextBid,
1983
+ txFeePlusTip: feePlusTip,
1984
+ successfulBids: 0
1985
+ };
1986
+ this.pendingRequest = this.bid(nextBid, accountsToUse, historyEntry);
1987
+ } else if (historyEntry.bidChanges.length === 0) {
1988
+ this.history.bidHistory.shift();
1775
1989
  }
1776
- this.pendingRequest = this.bid(
1777
- nextBid,
1778
- this.subaccounts.map((x) => x.index)
1779
- );
1780
1990
  this.needsRebid = false;
1781
1991
  }
1782
- async bid(bidPerSeat, subaccountRange) {
1783
- if (!subaccountRange.length) return;
1992
+ async bid(bidPerSeat, subaccounts, historyEntry) {
1784
1993
  const prevLastBidTime = this.lastBidTime;
1785
1994
  try {
1786
1995
  this.lastBidTime = Date.now();
1787
- const result = await this.accountset.createMiningBids({
1788
- subaccountRange,
1996
+ const submitter = await this.accountset.createMiningBidTx({
1997
+ subaccounts,
1789
1998
  bidAmount: bidPerSeat,
1790
1999
  sendRewardsToSeed: true
1791
2000
  });
1792
- if (result.blockHash) {
2001
+ const tip = this.options.tipPerTransaction ?? 0n;
2002
+ const txResult = await submitter.submit({
2003
+ tip,
2004
+ useLatestNonce: true
2005
+ });
2006
+ const bidError = await txResult.inBlockPromise.then(() => void 0).catch((x) => x);
2007
+ let blockNumber;
2008
+ if (txResult.includedInBlock) {
1793
2009
  const client = await this.client;
1794
- const api = await client.at(result.blockHash);
1795
- this.stats.lastBlock = await api.query.system.number().then((x) => x.toNumber());
1796
- }
1797
- this.stats.fees += result.finalFee ?? 0n;
1798
- this.stats.bids += subaccountRange.length;
1799
- if (bidPerSeat > this.stats.maxBidPerSeat) {
1800
- this.stats.maxBidPerSeat = bidPerSeat;
2010
+ const api = await client.at(txResult.includedInBlock);
2011
+ blockNumber = await api.query.system.number().then((x) => x.toNumber());
1801
2012
  }
1802
- console.log("Done creating bids for cohort", this.cohortId);
1803
- if (result.bidError) throw result.bidError;
2013
+ const successfulBids = txResult.batchInterruptedIndex ?? subaccounts.length;
2014
+ this.history.onBidResult(historyEntry, {
2015
+ blockNumber,
2016
+ successfulBids,
2017
+ bidPerSeat,
2018
+ txFeePlusTip: txResult.finalFee ?? 0n,
2019
+ bidsAttempted: subaccounts.length,
2020
+ bidError
2021
+ });
2022
+ console.log("Done creating bids for cohort", {
2023
+ successfulBids,
2024
+ bidPerSeat,
2025
+ blockNumber
2026
+ });
2027
+ if (bidError) throw bidError;
1804
2028
  } catch (err) {
1805
2029
  this.lastBidTime = prevLastBidTime;
1806
2030
  console.error(`Error bidding for cohort ${this.cohortId}:`, err);
@@ -2322,8 +2546,11 @@ async function getClient(host) {
2322
2546
  BitcoinLocks,
2323
2547
  BlockWatch,
2324
2548
  CohortBidder,
2549
+ CohortBidderHistory,
2550
+ ExtrinsicError,
2325
2551
  JsonExt,
2326
2552
  Keyring,
2553
+ MICROGONS_PER_ARGON,
2327
2554
  MiningBids,
2328
2555
  MiningRotations,
2329
2556
  TxSubmitter,
@@ -2335,6 +2562,7 @@ async function getClient(host) {
2335
2562
  convertPermillToBigNumber,
2336
2563
  createKeyringPair,
2337
2564
  decodeAddress,
2565
+ dispatchErrorToExtrinsicError,
2338
2566
  dispatchErrorToString,
2339
2567
  eventDataToJson,
2340
2568
  filterUndefined,