@argonprotocol/mainchain 1.1.0-rc.7 → 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.js CHANGED
@@ -86,119 +86,6 @@ var WageProtector = class _WageProtector {
86
86
  }
87
87
  };
88
88
 
89
- // src/utils.ts
90
- import BigNumber, * as BN from "bignumber.js";
91
- var { ROUND_FLOOR } = BN;
92
- function formatArgons(x) {
93
- const isNegative = x < 0;
94
- let format = BigNumber(x.toString()).abs().div(1e6).toFormat(2, ROUND_FLOOR);
95
- if (format.endsWith(".00")) {
96
- format = format.slice(0, -3);
97
- }
98
- return `${isNegative ? "-" : ""}\u20B3${format}`;
99
- }
100
- function formatPercent(x) {
101
- if (!x) return "na";
102
- return `${x.times(100).decimalPlaces(3)}%`;
103
- }
104
- function filterUndefined(obj) {
105
- return Object.fromEntries(
106
- Object.entries(obj).filter(
107
- ([_, value]) => value !== void 0 && value !== null
108
- )
109
- );
110
- }
111
- async function gettersToObject(obj) {
112
- if (obj === null || obj === void 0 || typeof obj !== "object") return obj;
113
- const keys = [];
114
- for (const key in obj) {
115
- keys.push(key);
116
- }
117
- if (Symbol.iterator in obj) {
118
- const iterableToArray = [];
119
- for (const item of obj) {
120
- iterableToArray.push(await gettersToObject(item));
121
- }
122
- return iterableToArray;
123
- }
124
- const result = {};
125
- for (const key of keys) {
126
- const descriptor = Object.getOwnPropertyDescriptor(obj, key);
127
- if (descriptor && typeof descriptor.value === "function") {
128
- continue;
129
- }
130
- const value = descriptor && descriptor.get ? descriptor.get.call(obj) : obj[key];
131
- if (typeof value === "function") continue;
132
- result[key] = await gettersToObject(value);
133
- }
134
- return result;
135
- }
136
- function convertFixedU128ToBigNumber(fixedU128) {
137
- const decimalFactor = new BigNumber(10).pow(new BigNumber(18));
138
- const rawValue = new BigNumber(fixedU128.toString());
139
- return rawValue.div(decimalFactor);
140
- }
141
- function convertPermillToBigNumber(permill) {
142
- const decimalFactor = new BigNumber(1e6);
143
- const rawValue = new BigNumber(permill.toString());
144
- return rawValue.div(decimalFactor);
145
- }
146
- function eventDataToJson(event) {
147
- const obj = {};
148
- event.data.forEach((data, index) => {
149
- const name = event.data.names?.[index];
150
- obj[name ?? `${index}`] = data.toJSON();
151
- });
152
- return obj;
153
- }
154
- function dispatchErrorToString(client, error) {
155
- let message = error.toString();
156
- if (error.isModule) {
157
- const decoded = client.registry.findMetaError(error.asModule);
158
- const { docs, name, section } = decoded;
159
- message = `${section}.${name}: ${docs.join(" ")}`;
160
- }
161
- return message;
162
- }
163
- function checkForExtrinsicSuccess(events, client) {
164
- return new Promise((resolve, reject) => {
165
- for (const { event } of events) {
166
- if (client.events.system.ExtrinsicSuccess.is(event)) {
167
- resolve();
168
- } else if (client.events.system.ExtrinsicFailed.is(event)) {
169
- const [dispatchError] = event.data;
170
- let errorInfo = dispatchError.toString();
171
- if (dispatchError.isModule) {
172
- const decoded = client.registry.findMetaError(dispatchError.asModule);
173
- errorInfo = `${decoded.section}.${decoded.name}`;
174
- }
175
- reject(
176
- new Error(
177
- `${event.section}.${event.method}:: ExtrinsicFailed:: ${errorInfo}`
178
- )
179
- );
180
- }
181
- }
182
- });
183
- }
184
- var JsonExt = class {
185
- static stringify(obj, space) {
186
- return JSON.stringify(
187
- obj,
188
- (_, v) => typeof v === "bigint" ? `${v}n` : v,
189
- space
190
- );
191
- }
192
- static parse(str) {
193
- return JSON.parse(str, (_, v) => {
194
- if (typeof v === "string" && v.endsWith("n")) {
195
- return BigInt(v.slice(0, -1));
196
- }
197
- return v;
198
- });
199
- }
200
- };
201
-
202
89
  // src/TxSubmitter.ts
203
90
  function logExtrinsicResult(result) {
204
91
  if (process.env.DEBUG) {
@@ -330,13 +217,12 @@ var TxResult = class {
330
217
  }
331
218
  }
332
219
  if (encounteredError) {
333
- const error = dispatchErrorToString(this.client, encounteredError);
334
- if (batchErrorIndex) {
335
- this.reject(
336
- new Error(`Error in batch#${batchErrorIndex}: ${error.toString()}`)
337
- );
338
- }
339
- this.reject(new Error(`Transaction failed: ${error}`));
220
+ const error = dispatchErrorToExtrinsicError(
221
+ this.client,
222
+ encounteredError,
223
+ batchErrorIndex
224
+ );
225
+ this.reject(error);
340
226
  } else {
341
227
  this.inBlockResolve(status.asInBlock.toU8a());
342
228
  }
@@ -351,6 +237,147 @@ var TxResult = class {
351
237
  }
352
238
  };
353
239
 
240
+ // src/utils.ts
241
+ import BigNumber, * as BN from "bignumber.js";
242
+ var { ROUND_FLOOR } = BN;
243
+ var MICROGONS_PER_ARGON = 1e6;
244
+ function formatArgons(x) {
245
+ if (x === void 0 || x === null) return "na";
246
+ const isNegative = x < 0;
247
+ let format = BigNumber(x.toString()).abs().div(MICROGONS_PER_ARGON).toFormat(2, ROUND_FLOOR);
248
+ if (format.endsWith(".00")) {
249
+ format = format.slice(0, -3);
250
+ }
251
+ return `${isNegative ? "-" : ""}\u20B3${format}`;
252
+ }
253
+ function formatPercent(x) {
254
+ if (!x) return "na";
255
+ return `${x.times(100).decimalPlaces(3)}%`;
256
+ }
257
+ function filterUndefined(obj) {
258
+ return Object.fromEntries(
259
+ Object.entries(obj).filter(
260
+ ([_, value]) => value !== void 0 && value !== null
261
+ )
262
+ );
263
+ }
264
+ async function gettersToObject(obj) {
265
+ if (obj === null || obj === void 0 || typeof obj !== "object") return obj;
266
+ const keys = [];
267
+ for (const key in obj) {
268
+ keys.push(key);
269
+ }
270
+ if (Symbol.iterator in obj) {
271
+ const iterableToArray = [];
272
+ for (const item of obj) {
273
+ iterableToArray.push(await gettersToObject(item));
274
+ }
275
+ return iterableToArray;
276
+ }
277
+ const result = {};
278
+ for (const key of keys) {
279
+ const descriptor = Object.getOwnPropertyDescriptor(obj, key);
280
+ if (descriptor && typeof descriptor.value === "function") {
281
+ continue;
282
+ }
283
+ const value = descriptor && descriptor.get ? descriptor.get.call(obj) : obj[key];
284
+ if (typeof value === "function") continue;
285
+ result[key] = await gettersToObject(value);
286
+ }
287
+ return result;
288
+ }
289
+ function convertFixedU128ToBigNumber(fixedU128) {
290
+ const decimalFactor = new BigNumber(10).pow(new BigNumber(18));
291
+ const rawValue = new BigNumber(fixedU128.toString());
292
+ return rawValue.div(decimalFactor);
293
+ }
294
+ function convertPermillToBigNumber(permill) {
295
+ const decimalFactor = new BigNumber(1e6);
296
+ const rawValue = new BigNumber(permill.toString());
297
+ return rawValue.div(decimalFactor);
298
+ }
299
+ function eventDataToJson(event) {
300
+ const obj = {};
301
+ event.data.forEach((data, index) => {
302
+ const name = event.data.names?.[index];
303
+ obj[name ?? `${index}`] = data.toJSON();
304
+ });
305
+ return obj;
306
+ }
307
+ function dispatchErrorToString(client, error) {
308
+ let message = error.toString();
309
+ if (error.isModule) {
310
+ const decoded = client.registry.findMetaError(error.asModule);
311
+ const { docs, name, section } = decoded;
312
+ message = `${section}.${name}: ${docs.join(" ")}`;
313
+ }
314
+ return message;
315
+ }
316
+ var ExtrinsicError2 = class extends Error {
317
+ constructor(errorCode, details, batchInterruptedIndex) {
318
+ super(errorCode);
319
+ this.errorCode = errorCode;
320
+ this.details = details;
321
+ this.batchInterruptedIndex = batchInterruptedIndex;
322
+ }
323
+ toString() {
324
+ if (this.batchInterruptedIndex !== void 0) {
325
+ return `${this.errorCode} ${this.details ?? ""} (Batch interrupted at index ${this.batchInterruptedIndex})`;
326
+ }
327
+ return `${this.errorCode} ${this.details ?? ""}`;
328
+ }
329
+ };
330
+ function dispatchErrorToExtrinsicError(client, error, batchInterruptedIndex) {
331
+ if (error.isModule) {
332
+ const decoded = client.registry.findMetaError(error.asModule);
333
+ const { docs, name, section } = decoded;
334
+ return new ExtrinsicError2(
335
+ `${section}.${name}`,
336
+ docs.join(" "),
337
+ batchInterruptedIndex
338
+ );
339
+ }
340
+ return new ExtrinsicError2(error.toString(), void 0, batchInterruptedIndex);
341
+ }
342
+ function checkForExtrinsicSuccess(events, client) {
343
+ return new Promise((resolve, reject) => {
344
+ for (const { event } of events) {
345
+ if (client.events.system.ExtrinsicSuccess.is(event)) {
346
+ resolve();
347
+ } else if (client.events.system.ExtrinsicFailed.is(event)) {
348
+ const [dispatchError] = event.data;
349
+ let errorInfo = dispatchError.toString();
350
+ if (dispatchError.isModule) {
351
+ const decoded = client.registry.findMetaError(dispatchError.asModule);
352
+ errorInfo = `${decoded.section}.${decoded.name}`;
353
+ }
354
+ reject(
355
+ new Error(
356
+ `${event.section}.${event.method}:: ExtrinsicFailed:: ${errorInfo}`
357
+ )
358
+ );
359
+ }
360
+ }
361
+ });
362
+ }
363
+ var JsonExt = class {
364
+ static stringify(obj, space) {
365
+ return JSON.stringify(
366
+ obj,
367
+ (_, v) => typeof v === "bigint" ? `${v}n` : v,
368
+ space
369
+ );
370
+ }
371
+ static parse(str) {
372
+ return JSON.parse(str, (_, v) => {
373
+ if (typeof v === "string" && v.endsWith("n")) {
374
+ return BigInt(v.slice(0, -1));
375
+ }
376
+ return v;
377
+ });
378
+ }
379
+ };
380
+
354
381
  // src/AccountRegistry.ts
355
382
  var AccountRegistry = class _AccountRegistry {
356
383
  namedAccounts = /* @__PURE__ */ new Map();
@@ -763,6 +790,14 @@ var Accountset = class {
763
790
  );
764
791
  }
765
792
  }
793
+ async submitterBalance(blockHash) {
794
+ const client = await this.client;
795
+ const api = blockHash ? await client.at(blockHash) : client;
796
+ const accountData = await api.query.system.account(
797
+ this.txSubmitterPair.address
798
+ );
799
+ return accountData.data.free.toBigInt();
800
+ }
766
801
  async balance(blockHash) {
767
802
  const client = await this.client;
768
803
  const api = blockHash ? await client.at(blockHash) : client;
@@ -1025,18 +1060,18 @@ var Accountset = class {
1025
1060
  return new TxSubmitter(client, tx, this.txSubmitterPair);
1026
1061
  }
1027
1062
  /**
1028
- * Create a mining bid. This will create a bid for each account in the given range from the seed account as funding.
1063
+ * Create but don't submit a mining bid transaction.
1064
+ * @param options
1029
1065
  */
1030
- async createMiningBids(options) {
1031
- const accounts = this.getAccountsInRange(options.subaccountRange);
1066
+ async createMiningBidTx(options) {
1032
1067
  const client = await this.client;
1033
- let tip = options.tip ?? 0n;
1068
+ const { bidAmount, subaccounts, sendRewardsToSeed } = options;
1034
1069
  const batch = client.tx.utility.batch(
1035
- accounts.map((x) => {
1070
+ subaccounts.map((x) => {
1036
1071
  const keys = this.keys();
1037
- const rewards = options.sendRewardsToSeed ? { Account: this.seedAddress } : { Owner: null };
1072
+ const rewards = sendRewardsToSeed ? { Account: this.seedAddress } : { Owner: null };
1038
1073
  return client.tx.miningSlot.bid(
1039
- options.bidAmount,
1074
+ bidAmount,
1040
1075
  rewards,
1041
1076
  {
1042
1077
  grandpa: keys.gran.rawPublicKey,
@@ -1050,7 +1085,19 @@ var Accountset = class {
1050
1085
  if (this.isProxy) {
1051
1086
  tx = client.tx.proxy.proxy(this.seedAddress, "MiningBid", batch);
1052
1087
  }
1053
- const submitter = new TxSubmitter(client, tx, this.txSubmitterPair);
1088
+ return new TxSubmitter(client, tx, this.txSubmitterPair);
1089
+ }
1090
+ /**
1091
+ * Create a mining bid. This will create a bid for each account in the given range from the seed account as funding.
1092
+ */
1093
+ async createMiningBids(options) {
1094
+ const accounts = this.getAccountsInRange(options.subaccountRange);
1095
+ const client = await this.client;
1096
+ const submitter = await this.createMiningBidTx({
1097
+ ...options,
1098
+ subaccounts: accounts
1099
+ });
1100
+ const { tip = 0n } = options;
1054
1101
  const txFee = await submitter.feeEstimate(tip);
1055
1102
  let minBalance = options.bidAmount * BigInt(accounts.length);
1056
1103
  let totalFees = tip + 1n + txFee;
@@ -1494,27 +1541,24 @@ var VaultMonitor = class {
1494
1541
  }
1495
1542
  };
1496
1543
 
1497
- // src/CohortBidder.ts
1498
- var CohortBidder = class _CohortBidder {
1499
- constructor(accountset, cohortId, subaccounts, options) {
1500
- this.accountset = accountset;
1544
+ // src/CohortBidderHistory.ts
1545
+ var CohortBidderHistory = class _CohortBidderHistory {
1546
+ constructor(cohortId, subaccounts) {
1501
1547
  this.cohortId = cohortId;
1502
1548
  this.subaccounts = subaccounts;
1503
- this.options = options;
1549
+ this.maxSeatsInPlay = this.subaccounts.length;
1504
1550
  this.subaccounts.forEach((x) => {
1505
- this.allAddresses.add(x.address);
1551
+ this.myAddresses.add(x.address);
1506
1552
  });
1507
1553
  }
1508
- get client() {
1509
- return this.accountset.client;
1510
- }
1554
+ bidHistory = [];
1511
1555
  stats = {
1512
1556
  // number of seats won
1513
- seats: 0,
1557
+ seatsWon: 0,
1514
1558
  // sum of argons bid in successful bids
1515
1559
  totalArgonsBid: 0n,
1516
1560
  // total number of bids placed (includes 1 per seat)
1517
- bids: 0,
1561
+ bidsAttempted: 0,
1518
1562
  // fees including the tip
1519
1563
  fees: 0n,
1520
1564
  // Max bid per seat
@@ -1526,16 +1570,157 @@ var CohortBidder = class _CohortBidder {
1526
1570
  // The cohort expected argons per block
1527
1571
  cohortArgonsPerBlock: 0n,
1528
1572
  // The last block that bids are synced to
1529
- lastBlock: 0
1573
+ lastBlockNumber: 0
1530
1574
  };
1575
+ lastBids = [];
1576
+ myAddresses = /* @__PURE__ */ new Set();
1577
+ maxSeatsInPlay = 0;
1578
+ async init(client) {
1579
+ if (!this.stats.argonotsPerSeat) {
1580
+ const startingStats = await _CohortBidderHistory.getStartingData(client);
1581
+ Object.assign(this.stats, startingStats);
1582
+ }
1583
+ }
1584
+ maybeReducingSeats(maxSeats, reason, historyEntry) {
1585
+ if (this.maxSeatsInPlay > maxSeats) {
1586
+ historyEntry.maxSeatsReductionReason = reason;
1587
+ }
1588
+ this.maxSeatsInPlay = maxSeats;
1589
+ historyEntry.maxSeatsInPlay = maxSeats;
1590
+ }
1591
+ trackChange(next, blockNumber, tick, isLastEntry = false) {
1592
+ let winningBids = 0;
1593
+ let totalArgonsBid = 0n;
1594
+ const nextEntrants = [];
1595
+ for (const x of next) {
1596
+ const bid = x.bid.toBigInt();
1597
+ const address = x.accountId.toHuman();
1598
+ nextEntrants.push({ address, bid });
1599
+ if (this.myAddresses.has(address)) {
1600
+ winningBids++;
1601
+ totalArgonsBid += bid;
1602
+ }
1603
+ }
1604
+ this.stats.seatsWon = winningBids;
1605
+ this.stats.totalArgonsBid = totalArgonsBid;
1606
+ this.stats.lastBlockNumber = Math.max(
1607
+ blockNumber,
1608
+ this.stats.lastBlockNumber
1609
+ );
1610
+ const historyEntry = {
1611
+ cohortId: this.cohortId,
1612
+ blockNumber,
1613
+ tick,
1614
+ bidChanges: [],
1615
+ winningSeats: winningBids,
1616
+ maxSeatsInPlay: this.maxSeatsInPlay
1617
+ };
1618
+ const hasDiffs = JsonExt.stringify(nextEntrants) !== JsonExt.stringify(this.lastBids);
1619
+ if (!isLastEntry || hasDiffs) {
1620
+ this.bidHistory.unshift(historyEntry);
1621
+ }
1622
+ if (hasDiffs) {
1623
+ nextEntrants.forEach(({ address, bid }, i) => {
1624
+ const prevBidIndex = this.lastBids.findIndex(
1625
+ (y) => y.address === address
1626
+ );
1627
+ const entry = {
1628
+ address,
1629
+ bidAmount: bid,
1630
+ bidPosition: i,
1631
+ prevPosition: prevBidIndex === -1 ? null : prevBidIndex
1632
+ };
1633
+ if (prevBidIndex !== -1) {
1634
+ const prevBidAmount = this.lastBids[prevBidIndex].bid;
1635
+ if (prevBidAmount !== bid) {
1636
+ entry.prevBidAmount = prevBidAmount;
1637
+ }
1638
+ }
1639
+ historyEntry.bidChanges.push(entry);
1640
+ });
1641
+ this.lastBids.forEach(({ address, bid }, i) => {
1642
+ const nextBid = nextEntrants.some((y) => y.address === address);
1643
+ if (!nextBid) {
1644
+ historyEntry.bidChanges.push({
1645
+ address,
1646
+ bidAmount: bid,
1647
+ bidPosition: null,
1648
+ prevPosition: i
1649
+ });
1650
+ }
1651
+ });
1652
+ this.lastBids = nextEntrants;
1653
+ }
1654
+ return historyEntry;
1655
+ }
1656
+ onBidResult(historyEntry, param) {
1657
+ const {
1658
+ txFeePlusTip,
1659
+ bidPerSeat,
1660
+ bidsAttempted,
1661
+ successfulBids,
1662
+ blockNumber,
1663
+ bidError
1664
+ } = param;
1665
+ this.stats.fees += txFeePlusTip;
1666
+ this.stats.bidsAttempted += bidsAttempted;
1667
+ if (bidPerSeat > this.stats.maxBidPerSeat) {
1668
+ this.stats.maxBidPerSeat = bidPerSeat;
1669
+ }
1670
+ if (blockNumber !== void 0) {
1671
+ this.stats.lastBlockNumber = Math.max(
1672
+ blockNumber,
1673
+ this.stats.lastBlockNumber
1674
+ );
1675
+ }
1676
+ historyEntry.myBidsPlaced.failureReason = bidError;
1677
+ historyEntry.myBidsPlaced.successfulBids = successfulBids;
1678
+ historyEntry.myBidsPlaced.txFeePlusTip = txFeePlusTip;
1679
+ }
1680
+ static async getStartingData(api) {
1681
+ const argonotPrice = await api.query.priceIndex.current();
1682
+ let argonotUsdPrice = 0;
1683
+ if (argonotPrice.isSome) {
1684
+ argonotUsdPrice = convertFixedU128ToBigNumber(
1685
+ argonotPrice.unwrap().argonotUsdPrice.toBigInt()
1686
+ ).toNumber();
1687
+ }
1688
+ const argonotsPerSeat = await api.query.miningSlot.argonotsPerMiningSeat().then((x) => x.toBigInt());
1689
+ const cohortArgonsPerBlock = await api.query.blockRewards.argonsPerBlock().then((x) => x.toBigInt());
1690
+ return { argonotsPerSeat, argonotUsdPrice, cohortArgonsPerBlock };
1691
+ }
1692
+ };
1693
+
1694
+ // src/CohortBidder.ts
1695
+ var CohortBidder = class {
1696
+ constructor(accountset, cohortId, subaccounts, options) {
1697
+ this.accountset = accountset;
1698
+ this.cohortId = cohortId;
1699
+ this.subaccounts = subaccounts;
1700
+ this.options = options;
1701
+ this.history = new CohortBidderHistory(cohortId, subaccounts);
1702
+ this.subaccounts.forEach((x) => {
1703
+ this.myAddresses.add(x.address);
1704
+ });
1705
+ }
1706
+ get client() {
1707
+ return this.accountset.client;
1708
+ }
1709
+ get stats() {
1710
+ return this.history.stats;
1711
+ }
1712
+ get bidHistory() {
1713
+ return this.history.bidHistory;
1714
+ }
1531
1715
  unsubscribe;
1532
1716
  pendingRequest;
1533
1717
  retryTimeout;
1534
1718
  isStopped = false;
1535
1719
  needsRebid = false;
1536
1720
  lastBidTime = 0;
1721
+ history;
1537
1722
  millisPerTick;
1538
- allAddresses = /* @__PURE__ */ new Set();
1723
+ myAddresses = /* @__PURE__ */ new Set();
1539
1724
  async stop() {
1540
1725
  if (this.isStopped) return this.stats;
1541
1726
  this.isStopped = true;
@@ -1545,70 +1730,59 @@ var CohortBidder = class _CohortBidder {
1545
1730
  this.unsubscribe();
1546
1731
  }
1547
1732
  const client = await this.client;
1548
- const [nextCohort, nextCohortId, blockNumber] = await new Promise(async (resolve) => {
1549
- const unsub = await client.queryMulti(
1550
- [
1551
- client.query.miningSlot.nextSlotCohort,
1552
- client.query.miningSlot.nextCohortId,
1553
- client.query.miningSlot.isNextSlotBiddingOpen,
1554
- client.query.system.number
1555
- ],
1556
- ([nextCohort2, nextCohortId2, isBiddingStillOpen, blockNumber2]) => {
1557
- if (nextCohortId2.toNumber() !== this.cohortId || isBiddingStillOpen.isFalse) {
1558
- unsub();
1559
- resolve([nextCohort2, nextCohortId2, blockNumber2]);
1733
+ const [nextCohortId, isBiddingOpen] = await client.queryMulti([
1734
+ client.query.miningSlot.nextCohortId,
1735
+ client.query.miningSlot.isNextSlotBiddingOpen
1736
+ ]);
1737
+ if (nextCohortId.toNumber() === this.cohortId && isBiddingOpen.isTrue) {
1738
+ console.log("Bidding is still open, waiting for it to close");
1739
+ await new Promise(async (resolve) => {
1740
+ const unsub = await client.query.miningSlot.isNextSlotBiddingOpen(
1741
+ (isOpen) => {
1742
+ if (isOpen.isFalse) {
1743
+ unsub();
1744
+ resolve();
1745
+ }
1560
1746
  }
1561
- }
1562
- );
1563
- });
1564
- void await this.pendingRequest;
1565
- if (nextCohortId.toNumber() === this.cohortId) {
1566
- console.log("Bidder updating stats with bid queue");
1567
- this.updateStats(nextCohort);
1568
- } else {
1569
- const bestBlock = await client.rpc.chain.getBlockHash();
1570
- const api = await client.at(bestBlock);
1571
- const wonIndices = await api.query.miningSlot.accountIndexLookup.multi([...this.allAddresses]).then((x) => x.filter((x2) => x2.isSome).map((x2) => x2.value));
1572
- const wonSeats = await api.query.miningSlot.activeMinersByIndex.multi(wonIndices).then(
1573
- (x) => x.filter(
1574
- (x2) => x2.isSome && x2.value.cohortId.toNumber() === this.cohortId
1575
- ).map((x2) => x2.value)
1576
- );
1577
- console.log("Bidder updating stats with finalized cohort");
1578
- this.updateStats(wonSeats);
1747
+ );
1748
+ });
1579
1749
  }
1580
- this.stats.lastBlock = Math.max(
1581
- blockNumber.toNumber(),
1582
- this.stats.lastBlock
1583
- );
1750
+ void await this.pendingRequest;
1751
+ let header = await client.rpc.chain.getHeader();
1752
+ while (true) {
1753
+ const api2 = await client.at(header.hash);
1754
+ const cohortId = await api2.query.miningSlot.nextCohortId();
1755
+ if (cohortId.toNumber() === this.cohortId) {
1756
+ break;
1757
+ }
1758
+ header = await client.rpc.chain.getHeader(header.parentHash);
1759
+ }
1760
+ const api = await client.at(header.hash);
1761
+ const tick = await api.query.ticks.currentTick().then((x) => x.toNumber());
1762
+ const cohort = await api.query.miningSlot.nextSlotCohort();
1763
+ this.history.trackChange(cohort, header.number.toNumber(), tick, true);
1764
+ console.log("Bidder stopped", {
1765
+ cohortId: this.cohortId,
1766
+ blockNumber: header.number.toNumber(),
1767
+ tick,
1768
+ cohort: cohort.map((x) => ({
1769
+ address: x.accountId.toHuman(),
1770
+ bid: x.bid.toBigInt()
1771
+ }))
1772
+ });
1584
1773
  return this.stats;
1585
1774
  }
1586
- static async getStartingData(api) {
1587
- const argonotPrice = await api.query.priceIndex.current();
1588
- let argonotUsdPrice = 0;
1589
- if (argonotPrice.isSome) {
1590
- argonotUsdPrice = convertFixedU128ToBigNumber(
1591
- argonotPrice.unwrap().argonotUsdPrice.toBigInt()
1592
- ).toNumber();
1593
- }
1594
- const argonotsPerSeat = await api.query.miningSlot.argonotsPerMiningSeat().then((x) => x.toBigInt());
1595
- const cohortArgonsPerBlock = await api.query.blockRewards.argonsPerBlock().then((x) => x.toBigInt());
1596
- return { argonotsPerSeat, argonotUsdPrice, cohortArgonsPerBlock };
1597
- }
1598
1775
  async start() {
1599
1776
  console.log(`Starting cohort ${this.cohortId} bidder`, {
1600
1777
  maxBid: formatArgons(this.options.maxBid),
1601
1778
  minBid: formatArgons(this.options.minBid),
1602
1779
  bidIncrement: formatArgons(this.options.bidIncrement),
1603
- maxBalance: formatArgons(this.options.maxBalance),
1780
+ maxBudget: formatArgons(this.options.maxBudget),
1604
1781
  bidDelay: this.options.bidDelay,
1605
1782
  subaccounts: this.subaccounts
1606
1783
  });
1607
1784
  const client = await this.client;
1608
- if (!this.stats.argonotsPerSeat) {
1609
- const startingStats = await _CohortBidder.getStartingData(client);
1610
- Object.assign(this.stats, startingStats);
1611
- }
1785
+ await this.history.init(client);
1612
1786
  this.millisPerTick ??= await client.query.ticks.genesisTicker().then((x) => x.tickDurationMillis.toNumber());
1613
1787
  this.unsubscribe = await client.queryMulti(
1614
1788
  [
@@ -1622,24 +1796,27 @@ var CohortBidder = class _CohortBidder {
1622
1796
  }
1623
1797
  );
1624
1798
  }
1625
- updateStats(next) {
1626
- let seats = 0;
1627
- let totalArgonsBid = 0n;
1628
- for (const x of next) {
1629
- if (this.allAddresses.has(x.accountId.toHuman())) {
1630
- seats++;
1631
- totalArgonsBid += x.bid.toBigInt();
1632
- }
1633
- }
1634
- this.stats.seats = seats;
1635
- this.stats.totalArgonsBid = totalArgonsBid;
1636
- }
1637
1799
  async checkSeats(next) {
1638
- if (this.isStopped || this.pendingRequest) return;
1800
+ if (this.isStopped) return;
1801
+ clearTimeout(this.retryTimeout);
1802
+ const client = await this.client;
1803
+ const bestBlock = await client.rpc.chain.getBlockHash();
1804
+ const api = await client.at(bestBlock);
1805
+ const blockNumber = await api.query.system.number().then((x) => x.toNumber());
1806
+ if (this.bidHistory[0]?.blockNumber >= blockNumber) {
1807
+ return;
1808
+ }
1809
+ const tick = await api.query.ticks.currentTick().then((x) => x.toNumber());
1810
+ const historyEntry = this.history.trackChange(next, blockNumber, tick);
1811
+ if (this.pendingRequest) return;
1639
1812
  const ticksSinceLastBid = Math.floor(
1640
1813
  (Date.now() - this.lastBidTime) / this.millisPerTick
1641
1814
  );
1642
1815
  if (ticksSinceLastBid < this.options.bidDelay) {
1816
+ this.retryTimeout = setTimeout(
1817
+ () => void this.checkCurrentSeats(),
1818
+ this.millisPerTick
1819
+ );
1643
1820
  return;
1644
1821
  }
1645
1822
  console.log(
@@ -1647,12 +1824,19 @@ var CohortBidder = class _CohortBidder {
1647
1824
  this.cohortId,
1648
1825
  this.subaccounts.map((x) => x.index)
1649
1826
  );
1650
- this.updateStats(next);
1651
- this.needsRebid = this.subaccounts.some(
1652
- (x) => !next.some((y) => y.accountId.toHuman() === x.address)
1653
- );
1827
+ const winningBids = historyEntry.winningSeats;
1828
+ this.needsRebid = winningBids < this.subaccounts.length;
1654
1829
  if (!this.needsRebid) return;
1655
- const lowestBid = next.at(-1)?.bid.toBigInt() ?? -this.options.bidIncrement;
1830
+ const winningAddresses = new Set(next.map((x) => x.accountId.toHuman()));
1831
+ let lowestBid = -this.options.bidIncrement;
1832
+ if (next.length) {
1833
+ for (let i = next.length - 1; i >= 0; i--) {
1834
+ if (!this.myAddresses.has(next[i].accountId.toHuman())) {
1835
+ lowestBid = next.at(i).bid.toBigInt();
1836
+ break;
1837
+ }
1838
+ }
1839
+ }
1656
1840
  const MIN_INCREMENT = 10000n;
1657
1841
  let nextBid = lowestBid + this.options.bidIncrement;
1658
1842
  if (nextBid < this.options.minBid) {
@@ -1660,83 +1844,119 @@ var CohortBidder = class _CohortBidder {
1660
1844
  }
1661
1845
  if (nextBid > this.options.maxBid) {
1662
1846
  nextBid = this.options.maxBid;
1663
- if (nextBid - lowestBid < MIN_INCREMENT) {
1664
- console.log(
1665
- `Can't make any more bids for ${this.cohortId} with given constraints.`,
1666
- {
1667
- lowestCurrentBid: formatArgons(lowestBid),
1668
- nextAttemptedBid: formatArgons(nextBid),
1669
- maxBid: formatArgons(this.options.maxBid)
1670
- }
1671
- );
1672
- return;
1847
+ }
1848
+ const fakeTx = await this.accountset.createMiningBidTx({
1849
+ subaccounts: this.subaccounts,
1850
+ bidAmount: nextBid,
1851
+ sendRewardsToSeed: true
1852
+ });
1853
+ let availableBalanceForBids = await api.query.system.account(this.accountset.txSubmitterPair.address).then((x) => x.data.free.toBigInt());
1854
+ for (const bid of next) {
1855
+ if (this.myAddresses.has(bid.accountId.toHuman())) {
1856
+ availableBalanceForBids += bid.bid.toBigInt();
1673
1857
  }
1674
1858
  }
1859
+ const tip = this.options.tipPerTransaction ?? 0n;
1860
+ const feeEstimate = await fakeTx.feeEstimate(tip);
1861
+ const feePlusTip = feeEstimate + tip;
1862
+ let budgetForSeats = this.options.maxBudget - feePlusTip;
1863
+ if (budgetForSeats > availableBalanceForBids) {
1864
+ budgetForSeats = availableBalanceForBids - feePlusTip;
1865
+ }
1675
1866
  if (nextBid < lowestBid) {
1676
1867
  console.log(
1677
1868
  `Can't bid ${formatArgons(nextBid)}. Current lowest bid is ${formatArgons(
1678
1869
  lowestBid
1679
1870
  )}.`
1680
1871
  );
1872
+ this.history.maybeReducingSeats(
1873
+ winningBids,
1874
+ "MaxBidTooLow" /* MaxBidTooLow */,
1875
+ historyEntry
1876
+ );
1681
1877
  return;
1682
1878
  }
1683
- const seatsInBudget = nextBid === 0n ? this.subaccounts.length : Number(this.options.maxBalance / nextBid);
1684
- if (seatsInBudget <= 0) {
1879
+ if (nextBid - lowestBid < MIN_INCREMENT) {
1685
1880
  console.log(
1686
- `Can't afford any seats at ${formatArgons(nextBid)}. Would exceed our max balance of ${formatArgons(this.options.maxBalance)}.`
1881
+ `Can't make any more bids for ${this.cohortId} with given constraints.`,
1882
+ {
1883
+ lowestCurrentBid: formatArgons(lowestBid),
1884
+ nextAttemptedBid: formatArgons(nextBid),
1885
+ maxBid: formatArgons(this.options.maxBid)
1886
+ }
1887
+ );
1888
+ this.history.maybeReducingSeats(
1889
+ winningBids,
1890
+ "MaxBidTooLow" /* MaxBidTooLow */,
1891
+ historyEntry
1687
1892
  );
1688
1893
  return;
1689
1894
  }
1690
- if (this.subaccounts.length > seatsInBudget) {
1691
- const toKeep = [];
1692
- for (const account of this.subaccounts) {
1693
- if (toKeep.length >= seatsInBudget) break;
1694
- if (account.isRebid) {
1695
- toKeep.push(account);
1696
- }
1697
- }
1698
- for (const account of this.subaccounts) {
1699
- if (toKeep.length >= seatsInBudget) break;
1700
- if (!account.isRebid) {
1701
- toKeep.push(account);
1702
- }
1703
- }
1704
- const removedIndices = this.subaccounts.filter((x) => !toKeep.some((y) => y.index === x.index)).map((x) => x.index);
1705
- this.subaccounts = toKeep;
1706
- console.log("Had to remove some subaccounts to fit in budget:", {
1707
- removedIndices,
1708
- seatsInBudget,
1709
- budget: formatArgons(this.options.maxBalance)
1895
+ const seatsInBudget = nextBid === 0n ? this.subaccounts.length : Number(budgetForSeats / nextBid);
1896
+ let accountsToUse = [...this.subaccounts];
1897
+ if (accountsToUse.length > seatsInBudget) {
1898
+ const reason = availableBalanceForBids - feePlusTip < nextBid * BigInt(seatsInBudget) ? "InsufficientFunds" /* InsufficientFunds */ : "MaxBudgetTooLow" /* MaxBudgetTooLow */;
1899
+ this.history.maybeReducingSeats(seatsInBudget, reason, historyEntry);
1900
+ accountsToUse.sort((a, b) => {
1901
+ const isWinningA = winningAddresses.has(a.address);
1902
+ const isWinningB = winningAddresses.has(b.address);
1903
+ if (isWinningA && !isWinningB) return -1;
1904
+ if (!isWinningA && isWinningB) return 1;
1905
+ if (a.isRebid && !b.isRebid) return -1;
1906
+ if (!a.isRebid && b.isRebid) return 1;
1907
+ return a.index - b.index;
1710
1908
  });
1909
+ accountsToUse.length = seatsInBudget;
1910
+ }
1911
+ if (accountsToUse.length > winningBids) {
1912
+ historyEntry.myBidsPlaced = {
1913
+ bids: accountsToUse.length,
1914
+ bidPerSeat: nextBid,
1915
+ txFeePlusTip: feePlusTip,
1916
+ successfulBids: 0
1917
+ };
1918
+ this.pendingRequest = this.bid(nextBid, accountsToUse, historyEntry);
1919
+ } else if (historyEntry.bidChanges.length === 0) {
1920
+ this.history.bidHistory.shift();
1711
1921
  }
1712
- this.pendingRequest = this.bid(
1713
- nextBid,
1714
- this.subaccounts.map((x) => x.index)
1715
- );
1716
1922
  this.needsRebid = false;
1717
1923
  }
1718
- async bid(bidPerSeat, subaccountRange) {
1719
- if (!subaccountRange.length) return;
1924
+ async bid(bidPerSeat, subaccounts, historyEntry) {
1720
1925
  const prevLastBidTime = this.lastBidTime;
1721
1926
  try {
1722
1927
  this.lastBidTime = Date.now();
1723
- const result = await this.accountset.createMiningBids({
1724
- subaccountRange,
1928
+ const submitter = await this.accountset.createMiningBidTx({
1929
+ subaccounts,
1725
1930
  bidAmount: bidPerSeat,
1726
1931
  sendRewardsToSeed: true
1727
1932
  });
1728
- if (result.blockHash) {
1933
+ const tip = this.options.tipPerTransaction ?? 0n;
1934
+ const txResult = await submitter.submit({
1935
+ tip,
1936
+ useLatestNonce: true
1937
+ });
1938
+ const bidError = await txResult.inBlockPromise.then(() => void 0).catch((x) => x);
1939
+ let blockNumber;
1940
+ if (txResult.includedInBlock) {
1729
1941
  const client = await this.client;
1730
- const api = await client.at(result.blockHash);
1731
- this.stats.lastBlock = await api.query.system.number().then((x) => x.toNumber());
1732
- }
1733
- this.stats.fees += result.finalFee ?? 0n;
1734
- this.stats.bids += subaccountRange.length;
1735
- if (bidPerSeat > this.stats.maxBidPerSeat) {
1736
- this.stats.maxBidPerSeat = bidPerSeat;
1942
+ const api = await client.at(txResult.includedInBlock);
1943
+ blockNumber = await api.query.system.number().then((x) => x.toNumber());
1737
1944
  }
1738
- console.log("Done creating bids for cohort", this.cohortId);
1739
- if (result.bidError) throw result.bidError;
1945
+ const successfulBids = txResult.batchInterruptedIndex ?? subaccounts.length;
1946
+ this.history.onBidResult(historyEntry, {
1947
+ blockNumber,
1948
+ successfulBids,
1949
+ bidPerSeat,
1950
+ txFeePlusTip: txResult.finalFee ?? 0n,
1951
+ bidsAttempted: subaccounts.length,
1952
+ bidError
1953
+ });
1954
+ console.log("Done creating bids for cohort", {
1955
+ successfulBids,
1956
+ bidPerSeat,
1957
+ blockNumber
1958
+ });
1959
+ if (bidError) throw bidError;
1740
1960
  } catch (err) {
1741
1961
  this.lastBidTime = prevLastBidTime;
1742
1962
  console.error(`Error bidding for cohort ${this.cohortId}:`, err);
@@ -2257,8 +2477,10 @@ export {
2257
2477
  BitcoinLocks,
2258
2478
  BlockWatch,
2259
2479
  CohortBidder,
2480
+ ExtrinsicError2 as ExtrinsicError,
2260
2481
  JsonExt,
2261
2482
  Keyring,
2483
+ MICROGONS_PER_ARGON,
2262
2484
  MiningBids,
2263
2485
  MiningRotations,
2264
2486
  TxSubmitter,
@@ -2270,6 +2492,7 @@ export {
2270
2492
  convertPermillToBigNumber,
2271
2493
  createKeyringPair,
2272
2494
  decodeAddress,
2495
+ dispatchErrorToExtrinsicError,
2273
2496
  dispatchErrorToString,
2274
2497
  eventDataToJson,
2275
2498
  filterUndefined,