@argonprotocol/mainchain 1.1.0-rc.6 → 1.1.0-rc.8

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