@argonprotocol/mainchain 1.3.7 → 1.3.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/README.md +7 -190
- package/browser/index.d.ts +483 -1978
- package/browser/index.js +110 -1984
- package/browser/index.js.map +1 -1
- package/lib/index.cjs +1006 -70
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +483 -1978
- package/lib/index.d.ts +483 -1978
- package/lib/index.js +1018 -82
- package/lib/index.js.map +1 -1
- package/package.json +6 -14
- package/lib/chunk-4ZBJ5W3P.cjs +0 -743
- package/lib/chunk-4ZBJ5W3P.cjs.map +0 -1
- package/lib/chunk-PE7HFDHD.js +0 -743
- package/lib/chunk-PE7HFDHD.js.map +0 -1
- package/lib/chunk-PXZPYJ4P.js +0 -2982
- package/lib/chunk-PXZPYJ4P.js.map +0 -1
- package/lib/chunk-VXYWJG66.cjs +0 -2982
- package/lib/chunk-VXYWJG66.cjs.map +0 -1
- package/lib/cli.cjs +0 -19
- package/lib/cli.cjs.map +0 -1
- package/lib/cli.d.cts +0 -1
- package/lib/cli.d.ts +0 -1
- package/lib/cli.js +0 -19
- package/lib/cli.js.map +0 -1
- package/lib/clis/index.cjs +0 -30
- package/lib/clis/index.cjs.map +0 -1
- package/lib/clis/index.d.cts +0 -129
- package/lib/clis/index.d.ts +0 -129
- package/lib/clis/index.js +0 -30
- package/lib/clis/index.js.map +0 -1
package/lib/chunk-PXZPYJ4P.js
DELETED
|
@@ -1,2982 +0,0 @@
|
|
|
1
|
-
// src/interfaces/augment-api-consts.ts
|
|
2
|
-
import "@polkadot/api-base/types/consts";
|
|
3
|
-
|
|
4
|
-
// src/interfaces/augment-api-errors.ts
|
|
5
|
-
import "@polkadot/api-base/types/errors";
|
|
6
|
-
|
|
7
|
-
// src/interfaces/augment-api-events.ts
|
|
8
|
-
import "@polkadot/api-base/types/events";
|
|
9
|
-
|
|
10
|
-
// src/interfaces/augment-api-query.ts
|
|
11
|
-
import "@polkadot/api-base/types/storage";
|
|
12
|
-
|
|
13
|
-
// src/interfaces/augment-api-tx.ts
|
|
14
|
-
import "@polkadot/api-base/types/submittable";
|
|
15
|
-
|
|
16
|
-
// src/interfaces/augment-api-rpc.ts
|
|
17
|
-
import "@polkadot/rpc-core/types/jsonrpc";
|
|
18
|
-
|
|
19
|
-
// src/interfaces/augment-api-runtime.ts
|
|
20
|
-
import "@polkadot/api-base/types/calls";
|
|
21
|
-
|
|
22
|
-
// src/interfaces/augment-types.ts
|
|
23
|
-
import "@polkadot/types/types/registry";
|
|
24
|
-
|
|
25
|
-
// src/index.ts
|
|
26
|
-
import { ApiPromise, HttpProvider, Keyring, WsProvider } from "@polkadot/api";
|
|
27
|
-
import { cryptoWaitReady, decodeAddress, mnemonicGenerate } from "@polkadot/util-crypto";
|
|
28
|
-
|
|
29
|
-
// src/WageProtector.ts
|
|
30
|
-
var WageProtector = class _WageProtector {
|
|
31
|
-
constructor(latestCpi) {
|
|
32
|
-
this.latestCpi = latestCpi;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Converts the base wage to the current wage using the latest CPI snapshot
|
|
36
|
-
*
|
|
37
|
-
* @param baseWage The base wage to convert
|
|
38
|
-
* @returns The protected wage
|
|
39
|
-
*/
|
|
40
|
-
getProtectedWage(baseWage) {
|
|
41
|
-
return baseWage * this.latestCpi.argonUsdTargetPrice / this.latestCpi.argonUsdPrice;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Subscribes to the current CPI and calls the callback function whenever the CPI changes
|
|
45
|
-
* @param client The ArgonClient to use
|
|
46
|
-
* @param callback The callback function to call when the CPI changes
|
|
47
|
-
* @returns An object with an unsubscribe function that can be called to stop the subscription
|
|
48
|
-
*/
|
|
49
|
-
static async subscribe(client, callback) {
|
|
50
|
-
const unsubscribe = await client.query.priceIndex.current(async (cpi) => {
|
|
51
|
-
if (cpi.isNone) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
const finalizedBlock = await client.rpc.chain.getFinalizedHead();
|
|
55
|
-
callback(
|
|
56
|
-
new _WageProtector({
|
|
57
|
-
argonUsdTargetPrice: cpi.value.argonUsdTargetPrice.toBigInt(),
|
|
58
|
-
argonUsdPrice: cpi.value.argonUsdPrice.toBigInt(),
|
|
59
|
-
finalizedBlock: new Uint8Array(finalizedBlock),
|
|
60
|
-
tick: cpi.value.tick.toBigInt()
|
|
61
|
-
})
|
|
62
|
-
);
|
|
63
|
-
});
|
|
64
|
-
return { unsubscribe };
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Creates a new WageProtector instance by subscribing to the current CPI and waiting for the first value
|
|
68
|
-
* @param client The ArgonClient to use
|
|
69
|
-
*/
|
|
70
|
-
static async create(client) {
|
|
71
|
-
return new Promise(async (resolve, reject) => {
|
|
72
|
-
try {
|
|
73
|
-
const { unsubscribe } = await _WageProtector.subscribe(client, (x) => {
|
|
74
|
-
resolve(x);
|
|
75
|
-
unsubscribe();
|
|
76
|
-
});
|
|
77
|
-
} catch (e) {
|
|
78
|
-
reject(e);
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
// src/config.ts
|
|
85
|
-
var config = {};
|
|
86
|
-
function getEnvVar(key) {
|
|
87
|
-
if (typeof process !== "undefined" && process.env) {
|
|
88
|
-
return process.env[key];
|
|
89
|
-
}
|
|
90
|
-
return void 0;
|
|
91
|
-
}
|
|
92
|
-
function setConfig(newConfig) {
|
|
93
|
-
config = { ...config, ...newConfig };
|
|
94
|
-
}
|
|
95
|
-
function getConfig() {
|
|
96
|
-
return {
|
|
97
|
-
debug: config.debug ?? getEnvVar("DEBUG") === "true",
|
|
98
|
-
keysVersion: config.keysVersion ?? (getEnvVar("KEYS_VERSION") ? parseInt(getEnvVar("KEYS_VERSION")) : void 0),
|
|
99
|
-
keySeedOrMnemonic: config.keySeedOrMnemonic ?? getEnvVar("KEYS_MNEMONIC"),
|
|
100
|
-
subaccountRange: config.subaccountRange ?? getEnvVar("SUBACCOUNT_RANGE") ?? "0-9"
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// src/TxSubmitter.ts
|
|
105
|
-
function logExtrinsicResult(result) {
|
|
106
|
-
if (getConfig().debug) {
|
|
107
|
-
const json = result.status.toJSON();
|
|
108
|
-
const status = Object.keys(json)[0];
|
|
109
|
-
console.debug('Transaction update: "%s"', status, json[status]);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
var TxSubmitter = class {
|
|
113
|
-
constructor(client, tx, pair) {
|
|
114
|
-
this.client = client;
|
|
115
|
-
this.tx = tx;
|
|
116
|
-
this.pair = pair;
|
|
117
|
-
}
|
|
118
|
-
async feeEstimate(tip) {
|
|
119
|
-
const { partialFee } = await this.tx.paymentInfo(this.pair, { tip });
|
|
120
|
-
return partialFee.toBigInt();
|
|
121
|
-
}
|
|
122
|
-
async canAfford(options = {}) {
|
|
123
|
-
const { tip, unavailableBalance } = options;
|
|
124
|
-
const account = await this.client.query.system.account(this.pair.address);
|
|
125
|
-
let availableBalance = account.data.free.toBigInt();
|
|
126
|
-
const userBalance = availableBalance;
|
|
127
|
-
if (unavailableBalance) {
|
|
128
|
-
availableBalance -= unavailableBalance;
|
|
129
|
-
}
|
|
130
|
-
const existentialDeposit = options.includeExistentialDeposit ? this.client.consts.balances.existentialDeposit.toBigInt() : 0n;
|
|
131
|
-
const fees = await this.feeEstimate(tip);
|
|
132
|
-
const totalCharge = fees + (tip ?? 0n);
|
|
133
|
-
const canAfford = availableBalance >= totalCharge + existentialDeposit;
|
|
134
|
-
return { canAfford, availableBalance: userBalance, txFee: fees };
|
|
135
|
-
}
|
|
136
|
-
async submit(options = {}) {
|
|
137
|
-
const { logResults, waitForBlock, useLatestNonce, ...apiOptions } = options;
|
|
138
|
-
await waitForLoad();
|
|
139
|
-
const result = new TxResult(this.client, logResults);
|
|
140
|
-
result.txProgressCallback = options.txProgressCallback;
|
|
141
|
-
let toHuman = this.tx.toHuman().method;
|
|
142
|
-
let txString = [];
|
|
143
|
-
let api = formatCall(toHuman);
|
|
144
|
-
const args = [];
|
|
145
|
-
if (api === "proxy.proxy") {
|
|
146
|
-
toHuman = toHuman.args.call;
|
|
147
|
-
txString.push("Proxy");
|
|
148
|
-
api = formatCall(toHuman);
|
|
149
|
-
}
|
|
150
|
-
if (api.startsWith("utility.batch")) {
|
|
151
|
-
const calls = toHuman.args.calls.map(formatCall).join(", ");
|
|
152
|
-
txString.push(`Batch[${calls}]`);
|
|
153
|
-
} else {
|
|
154
|
-
txString.push(api);
|
|
155
|
-
args.push(toHuman.args);
|
|
156
|
-
}
|
|
157
|
-
args.unshift(txString.join("->"));
|
|
158
|
-
if (useLatestNonce && !apiOptions.nonce) {
|
|
159
|
-
apiOptions.nonce = await this.client.rpc.system.accountNextIndex(this.pair.address);
|
|
160
|
-
}
|
|
161
|
-
console.log("Submitting transaction from %s:", this.pair.address, ...args);
|
|
162
|
-
await this.tx.signAndSend(this.pair, apiOptions, result.onResult.bind(result));
|
|
163
|
-
if (waitForBlock) {
|
|
164
|
-
await result.inBlockPromise;
|
|
165
|
-
}
|
|
166
|
-
return result;
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
function formatCall(call) {
|
|
170
|
-
return `${call.section}.${call.method}`;
|
|
171
|
-
}
|
|
172
|
-
var TxResult = class {
|
|
173
|
-
constructor(client, shouldLog = false) {
|
|
174
|
-
this.client = client;
|
|
175
|
-
this.shouldLog = shouldLog;
|
|
176
|
-
this.inBlockPromise = new Promise((resolve, reject) => {
|
|
177
|
-
this.inBlockResolve = resolve;
|
|
178
|
-
this.inBlockReject = reject;
|
|
179
|
-
});
|
|
180
|
-
this.finalizedPromise = new Promise((resolve, reject) => {
|
|
181
|
-
this.finalizedResolve = resolve;
|
|
182
|
-
this.finalizedReject = reject;
|
|
183
|
-
});
|
|
184
|
-
this.inBlockPromise.catch(() => {
|
|
185
|
-
});
|
|
186
|
-
this.finalizedPromise.catch(() => {
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
inBlockPromise;
|
|
190
|
-
finalizedPromise;
|
|
191
|
-
status;
|
|
192
|
-
events = [];
|
|
193
|
-
/**
|
|
194
|
-
* The index of the batch that was interrupted, if any.
|
|
195
|
-
*/
|
|
196
|
-
batchInterruptedIndex;
|
|
197
|
-
includedInBlock;
|
|
198
|
-
/**
|
|
199
|
-
* The final fee paid for the transaction, including the fee tip.
|
|
200
|
-
*/
|
|
201
|
-
finalFee;
|
|
202
|
-
/**
|
|
203
|
-
* The fee tip paid for the transaction.
|
|
204
|
-
*/
|
|
205
|
-
finalFeeTip;
|
|
206
|
-
txProgressCallback;
|
|
207
|
-
inBlockResolve;
|
|
208
|
-
inBlockReject;
|
|
209
|
-
finalizedResolve;
|
|
210
|
-
finalizedReject;
|
|
211
|
-
onResult(result) {
|
|
212
|
-
this.status = result.status;
|
|
213
|
-
if (this.shouldLog) {
|
|
214
|
-
logExtrinsicResult(result);
|
|
215
|
-
}
|
|
216
|
-
const { events, status, dispatchError, isFinalized } = result;
|
|
217
|
-
if (status.isInBlock) {
|
|
218
|
-
this.includedInBlock = new Uint8Array(status.asInBlock);
|
|
219
|
-
let encounteredError = dispatchError;
|
|
220
|
-
let batchErrorIndex;
|
|
221
|
-
for (const event of events) {
|
|
222
|
-
this.events.push(event.event);
|
|
223
|
-
if (this.client.events.utility.BatchInterrupted.is(event.event)) {
|
|
224
|
-
batchErrorIndex = event.event.data[0].toNumber();
|
|
225
|
-
this.batchInterruptedIndex = batchErrorIndex;
|
|
226
|
-
encounteredError = event.event.data[1];
|
|
227
|
-
}
|
|
228
|
-
if (this.client.events.transactionPayment.TransactionFeePaid.is(event.event)) {
|
|
229
|
-
const [_who, actualFee, tip] = event.event.data;
|
|
230
|
-
this.finalFee = actualFee.toBigInt();
|
|
231
|
-
this.finalFeeTip = tip.toBigInt();
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
if (encounteredError) {
|
|
235
|
-
const error = dispatchErrorToExtrinsicError(this.client, encounteredError, batchErrorIndex);
|
|
236
|
-
this.reject(error);
|
|
237
|
-
} else {
|
|
238
|
-
this.inBlockResolve(new Uint8Array(status.asInBlock));
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
if (isFinalized) {
|
|
242
|
-
this.finalizedResolve(status.asFinalized);
|
|
243
|
-
}
|
|
244
|
-
if (this.txProgressCallback) {
|
|
245
|
-
let percent = 0;
|
|
246
|
-
if (result.status.isBroadcast) {
|
|
247
|
-
percent = 50;
|
|
248
|
-
} else if (result.status.isInBlock) {
|
|
249
|
-
percent = 100;
|
|
250
|
-
}
|
|
251
|
-
this.txProgressCallback(percent, this);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
reject(error) {
|
|
255
|
-
this.inBlockReject(error);
|
|
256
|
-
this.finalizedReject(error);
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
// src/utils.ts
|
|
261
|
-
import BigNumber, * as BN from "bignumber.js";
|
|
262
|
-
import { ed25519DeriveHard, keyExtractSuri, mnemonicToMiniSecret } from "@polkadot/util-crypto";
|
|
263
|
-
import { u8aToHex } from "@polkadot/util";
|
|
264
|
-
var { ROUND_FLOOR } = BN;
|
|
265
|
-
var MICROGONS_PER_ARGON = 1e6;
|
|
266
|
-
function miniSecretFromUri(uri, password) {
|
|
267
|
-
const { phrase, path } = keyExtractSuri(uri);
|
|
268
|
-
let mini = mnemonicToMiniSecret(phrase, password);
|
|
269
|
-
for (const j of path) {
|
|
270
|
-
if (!j.isHard) throw new Error("ed25519 soft derivation not supported");
|
|
271
|
-
mini = ed25519DeriveHard(mini, j.chainCode);
|
|
272
|
-
}
|
|
273
|
-
return u8aToHex(mini);
|
|
274
|
-
}
|
|
275
|
-
function formatArgons(microgons) {
|
|
276
|
-
if (microgons === void 0 || microgons === null) return "na";
|
|
277
|
-
const isNegative = microgons < 0;
|
|
278
|
-
let format = BigNumber(microgons.toString()).abs().div(MICROGONS_PER_ARGON).toFormat(2, ROUND_FLOOR);
|
|
279
|
-
if (format.endsWith(".00")) {
|
|
280
|
-
format = format.slice(0, -3);
|
|
281
|
-
}
|
|
282
|
-
return `${isNegative ? "-" : ""}\u20B3${format}`;
|
|
283
|
-
}
|
|
284
|
-
function formatPercent(x) {
|
|
285
|
-
if (!x) return "na";
|
|
286
|
-
return `${x.times(100).decimalPlaces(3)}%`;
|
|
287
|
-
}
|
|
288
|
-
function filterUndefined(obj) {
|
|
289
|
-
return Object.fromEntries(
|
|
290
|
-
Object.entries(obj).filter(([_, value]) => value !== void 0 && value !== null)
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
async function gettersToObject(obj) {
|
|
294
|
-
if (obj === null || obj === void 0 || typeof obj !== "object") return obj;
|
|
295
|
-
const keys = [];
|
|
296
|
-
for (const key in obj) {
|
|
297
|
-
keys.push(key);
|
|
298
|
-
}
|
|
299
|
-
if (Symbol.iterator in obj) {
|
|
300
|
-
const iterableToArray = [];
|
|
301
|
-
for (const item of obj) {
|
|
302
|
-
iterableToArray.push(await gettersToObject(item));
|
|
303
|
-
}
|
|
304
|
-
return iterableToArray;
|
|
305
|
-
}
|
|
306
|
-
const result = {};
|
|
307
|
-
for (const key of keys) {
|
|
308
|
-
const descriptor = Object.getOwnPropertyDescriptor(obj, key);
|
|
309
|
-
if (descriptor && typeof descriptor.value === "function") {
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
const value = descriptor && descriptor.get ? descriptor.get.call(obj) : obj[key];
|
|
313
|
-
if (typeof value === "function") continue;
|
|
314
|
-
result[key] = await gettersToObject(value);
|
|
315
|
-
}
|
|
316
|
-
return result;
|
|
317
|
-
}
|
|
318
|
-
function toFixedNumber(value, decimals) {
|
|
319
|
-
const factor = new BigNumber(10).pow(decimals);
|
|
320
|
-
const bn = new BigNumber(value);
|
|
321
|
-
const int = bn.times(factor).integerValue(BigNumber.ROUND_DOWN);
|
|
322
|
-
return BigInt(int.toFixed(0));
|
|
323
|
-
}
|
|
324
|
-
function convertNumberToFixedU128(value) {
|
|
325
|
-
return toFixedNumber(value, 18);
|
|
326
|
-
}
|
|
327
|
-
function convertFixedU128ToBigNumber(fixedU128) {
|
|
328
|
-
const decimalFactor = new BigNumber(10).pow(new BigNumber(18));
|
|
329
|
-
const rawValue = new BigNumber(fixedU128.toString());
|
|
330
|
-
return rawValue.div(decimalFactor);
|
|
331
|
-
}
|
|
332
|
-
function convertPermillToBigNumber(permill) {
|
|
333
|
-
const decimalFactor = new BigNumber(1e6);
|
|
334
|
-
const rawValue = new BigNumber(permill.toString());
|
|
335
|
-
return rawValue.div(decimalFactor);
|
|
336
|
-
}
|
|
337
|
-
function convertNumberToPermill(value) {
|
|
338
|
-
return toFixedNumber(value, 6);
|
|
339
|
-
}
|
|
340
|
-
function eventDataToJson(event) {
|
|
341
|
-
const obj = {};
|
|
342
|
-
event.data.forEach((data, index) => {
|
|
343
|
-
const name = event.data.names?.[index];
|
|
344
|
-
obj[name ?? `${index}`] = data.toJSON();
|
|
345
|
-
});
|
|
346
|
-
return obj;
|
|
347
|
-
}
|
|
348
|
-
function dispatchErrorToString(client, error) {
|
|
349
|
-
let message = error.toString();
|
|
350
|
-
if (error.isModule) {
|
|
351
|
-
const decoded = client.registry.findMetaError(error.asModule);
|
|
352
|
-
const { docs, name, section } = decoded;
|
|
353
|
-
message = `${section}.${name}: ${docs.join(" ")}`;
|
|
354
|
-
}
|
|
355
|
-
return message;
|
|
356
|
-
}
|
|
357
|
-
var ExtrinsicError2 = class extends Error {
|
|
358
|
-
constructor(errorCode, details, batchInterruptedIndex) {
|
|
359
|
-
super(errorCode);
|
|
360
|
-
this.errorCode = errorCode;
|
|
361
|
-
this.details = details;
|
|
362
|
-
this.batchInterruptedIndex = batchInterruptedIndex;
|
|
363
|
-
}
|
|
364
|
-
toString() {
|
|
365
|
-
if (this.batchInterruptedIndex !== void 0) {
|
|
366
|
-
return `${this.errorCode} ${this.details ?? ""} (Batch interrupted at index ${this.batchInterruptedIndex})`;
|
|
367
|
-
}
|
|
368
|
-
return `${this.errorCode} ${this.details ?? ""}`;
|
|
369
|
-
}
|
|
370
|
-
};
|
|
371
|
-
function dispatchErrorToExtrinsicError(client, error, batchInterruptedIndex) {
|
|
372
|
-
if (error.isModule) {
|
|
373
|
-
const decoded = client.registry.findMetaError(error.asModule);
|
|
374
|
-
const { docs, name, section } = decoded;
|
|
375
|
-
return new ExtrinsicError2(`${section}.${name}`, docs.join(" "), batchInterruptedIndex);
|
|
376
|
-
}
|
|
377
|
-
return new ExtrinsicError2(error.toString(), void 0, batchInterruptedIndex);
|
|
378
|
-
}
|
|
379
|
-
function checkForExtrinsicSuccess(events, client) {
|
|
380
|
-
return new Promise((resolve, reject) => {
|
|
381
|
-
for (const { event } of events) {
|
|
382
|
-
if (client.events.system.ExtrinsicSuccess.is(event)) {
|
|
383
|
-
resolve();
|
|
384
|
-
} else if (client.events.system.ExtrinsicFailed.is(event)) {
|
|
385
|
-
const [dispatchError] = event.data;
|
|
386
|
-
let errorInfo = dispatchError.toString();
|
|
387
|
-
if (dispatchError.isModule) {
|
|
388
|
-
const decoded = client.registry.findMetaError(dispatchError.asModule);
|
|
389
|
-
errorInfo = `${decoded.section}.${decoded.name}`;
|
|
390
|
-
}
|
|
391
|
-
reject(new Error(`${event.section}.${event.method}:: ExtrinsicFailed:: ${errorInfo}`));
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
});
|
|
395
|
-
}
|
|
396
|
-
var JsonExt = class {
|
|
397
|
-
static stringify(obj, space) {
|
|
398
|
-
return JSON.stringify(
|
|
399
|
-
obj,
|
|
400
|
-
(_, v) => {
|
|
401
|
-
if (typeof v === "bigint") {
|
|
402
|
-
return `${v}n`;
|
|
403
|
-
}
|
|
404
|
-
if (v instanceof Uint8Array) {
|
|
405
|
-
return {
|
|
406
|
-
type: "Buffer",
|
|
407
|
-
data: Array.from(v)
|
|
408
|
-
// Convert Uint8Array to an array of numbers
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
return v;
|
|
412
|
-
},
|
|
413
|
-
space
|
|
414
|
-
);
|
|
415
|
-
}
|
|
416
|
-
static parse(str) {
|
|
417
|
-
return JSON.parse(str, (_, v) => {
|
|
418
|
-
if (typeof v === "string" && v.match(/^-?\d+n$/)) {
|
|
419
|
-
return BigInt(v.slice(0, -1));
|
|
420
|
-
}
|
|
421
|
-
if (typeof v === "object" && v !== null && v.type === "Buffer" && Array.isArray(v.data)) {
|
|
422
|
-
return Uint8Array.from(v.data);
|
|
423
|
-
}
|
|
424
|
-
return v;
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
};
|
|
428
|
-
function createNanoEvents() {
|
|
429
|
-
return new TypedEmitter();
|
|
430
|
-
}
|
|
431
|
-
var TypedEmitter = class {
|
|
432
|
-
events = {};
|
|
433
|
-
emit(event, ...args) {
|
|
434
|
-
for (const cb of this.events[event] || []) {
|
|
435
|
-
cb(...args);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
on(event, cb) {
|
|
439
|
-
(this.events[event] ||= []).push(cb);
|
|
440
|
-
return () => {
|
|
441
|
-
this.events[event] = this.events[event]?.filter((i) => cb !== i);
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
};
|
|
445
|
-
|
|
446
|
-
// src/AccountRegistry.ts
|
|
447
|
-
var AccountRegistry = class _AccountRegistry {
|
|
448
|
-
namedAccounts = /* @__PURE__ */ new Map();
|
|
449
|
-
me = "me";
|
|
450
|
-
constructor(name) {
|
|
451
|
-
if (name) {
|
|
452
|
-
this.me = name;
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
getName(address) {
|
|
456
|
-
return this.namedAccounts.get(address);
|
|
457
|
-
}
|
|
458
|
-
register(address, name) {
|
|
459
|
-
this.namedAccounts.set(address, name);
|
|
460
|
-
}
|
|
461
|
-
static factory = (name) => new _AccountRegistry(name);
|
|
462
|
-
};
|
|
463
|
-
|
|
464
|
-
// src/BlockWatch.ts
|
|
465
|
-
function getTickFromHeader(client, header) {
|
|
466
|
-
for (const x of header.digest.logs) {
|
|
467
|
-
if (x.isPreRuntime) {
|
|
468
|
-
const [engineId, data] = x.asPreRuntime;
|
|
469
|
-
if (engineId.toString() === "aura") {
|
|
470
|
-
return client.createType("u64", data).toNumber();
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
return void 0;
|
|
475
|
-
}
|
|
476
|
-
function getAuthorFromHeader(client, header) {
|
|
477
|
-
for (const x of header.digest.logs) {
|
|
478
|
-
if (x.isPreRuntime) {
|
|
479
|
-
const [engineId, data] = x.asPreRuntime;
|
|
480
|
-
if (engineId.toString() === "pow_") {
|
|
481
|
-
return client.createType("AccountId32", data).toHuman();
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
return void 0;
|
|
486
|
-
}
|
|
487
|
-
var BlockWatch = class {
|
|
488
|
-
constructor(mainchain, options = {}) {
|
|
489
|
-
this.mainchain = mainchain;
|
|
490
|
-
this.options = options;
|
|
491
|
-
this.options.shouldLog ??= true;
|
|
492
|
-
this.options.finalizedBlocks ??= false;
|
|
493
|
-
}
|
|
494
|
-
events = createNanoEvents();
|
|
495
|
-
locksById = {};
|
|
496
|
-
unsubscribe;
|
|
497
|
-
stop() {
|
|
498
|
-
if (this.unsubscribe) {
|
|
499
|
-
this.unsubscribe();
|
|
500
|
-
this.unsubscribe = void 0;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
async start() {
|
|
504
|
-
await this.watchBlocks();
|
|
505
|
-
}
|
|
506
|
-
async watchBlocks() {
|
|
507
|
-
const client = await this.mainchain;
|
|
508
|
-
const onBlock = async (header) => {
|
|
509
|
-
try {
|
|
510
|
-
await this.processBlock(header);
|
|
511
|
-
} catch (e) {
|
|
512
|
-
console.error("Error processing block", e);
|
|
513
|
-
}
|
|
514
|
-
};
|
|
515
|
-
if (this.options.finalizedBlocks) {
|
|
516
|
-
this.unsubscribe = await client.rpc.chain.subscribeFinalizedHeads(onBlock);
|
|
517
|
-
} else {
|
|
518
|
-
this.unsubscribe = await client.rpc.chain.subscribeNewHeads(onBlock);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
async processBlock(header) {
|
|
522
|
-
const client = await this.mainchain;
|
|
523
|
-
if (this.options.shouldLog) {
|
|
524
|
-
console.log(`-------------------------------------
|
|
525
|
-
BLOCK #${header.number}, ${header.hash.toHuman()}`);
|
|
526
|
-
}
|
|
527
|
-
const blockHash = header.hash;
|
|
528
|
-
const api = await client.at(blockHash);
|
|
529
|
-
const isBlockVote = await api.query.blockSeal.isBlockFromVoteSeal();
|
|
530
|
-
if (!isBlockVote) {
|
|
531
|
-
console.warn("> Compute reactivated!");
|
|
532
|
-
}
|
|
533
|
-
const events = await api.query.system.events();
|
|
534
|
-
const reloadVaults = /* @__PURE__ */ new Set();
|
|
535
|
-
let block = void 0;
|
|
536
|
-
for (const { event, phase } of events) {
|
|
537
|
-
const data = eventDataToJson(event);
|
|
538
|
-
if (data.vaultId) {
|
|
539
|
-
const vaultId = data.vaultId;
|
|
540
|
-
reloadVaults.add(vaultId);
|
|
541
|
-
}
|
|
542
|
-
let logEvent = false;
|
|
543
|
-
if (event.section === "liquidityPools") {
|
|
544
|
-
if (client.events.liquidityPools.BidPoolDistributed.is(event)) {
|
|
545
|
-
const { bidPoolBurned, bidPoolDistributed } = event.data;
|
|
546
|
-
data.burned = formatArgons(bidPoolBurned.toBigInt());
|
|
547
|
-
data.distributed = formatArgons(bidPoolDistributed.toBigInt());
|
|
548
|
-
logEvent = true;
|
|
549
|
-
} else if (client.events.liquidityPools.NextBidPoolCapitalLocked.is(event)) {
|
|
550
|
-
const { totalActivatedCapital } = event.data;
|
|
551
|
-
data.totalActivatedCapital = formatArgons(totalActivatedCapital.toBigInt());
|
|
552
|
-
logEvent = true;
|
|
553
|
-
}
|
|
554
|
-
} else if (event.section === "bitcoinLocks") {
|
|
555
|
-
if (client.events.bitcoinLocks.BitcoinLockCreated.is(event)) {
|
|
556
|
-
const { lockPrice, utxoId, accountId, vaultId } = event.data;
|
|
557
|
-
this.locksById[utxoId.toNumber()] = {
|
|
558
|
-
vaultId: vaultId.toNumber(),
|
|
559
|
-
lockPrice: lockPrice.toBigInt()
|
|
560
|
-
};
|
|
561
|
-
data.lockPrice = formatArgons(lockPrice.toBigInt());
|
|
562
|
-
data.accountId = accountId.toHuman();
|
|
563
|
-
reloadVaults.add(vaultId.toNumber());
|
|
564
|
-
}
|
|
565
|
-
logEvent = true;
|
|
566
|
-
} else if (event.section === "mint") {
|
|
567
|
-
logEvent = true;
|
|
568
|
-
if (client.events.mint.MiningMint.is(event)) {
|
|
569
|
-
const { amount } = event.data;
|
|
570
|
-
data.amount = formatArgons(amount.toBigInt());
|
|
571
|
-
}
|
|
572
|
-
} else if (event.section === "miningSlot") {
|
|
573
|
-
logEvent = true;
|
|
574
|
-
if (client.events.miningSlot.SlotBidderAdded.is(event)) {
|
|
575
|
-
data.amount = formatArgons(event.data.bidAmount.toBigInt());
|
|
576
|
-
this.events.emit("mining-bid", header, {
|
|
577
|
-
amount: event.data.bidAmount.toBigInt(),
|
|
578
|
-
accountId: event.data.accountId.toString()
|
|
579
|
-
});
|
|
580
|
-
} else if (client.events.miningSlot.SlotBidderDropped.is(event)) {
|
|
581
|
-
this.events.emit("mining-bid-ousted", header, {
|
|
582
|
-
accountId: event.data.accountId.toString(),
|
|
583
|
-
preservedArgonotHold: event.data.preservedArgonotHold.toPrimitive()
|
|
584
|
-
});
|
|
585
|
-
}
|
|
586
|
-
} else if (event.section === "bitcoinUtxos") {
|
|
587
|
-
logEvent = true;
|
|
588
|
-
if (client.events.bitcoinUtxos.UtxoVerified.is(event)) {
|
|
589
|
-
const { utxoId } = event.data;
|
|
590
|
-
const details = await this.getBitcoinLockDetails(utxoId.toNumber(), blockHash);
|
|
591
|
-
this.events.emit("bitcoin-verified", header, {
|
|
592
|
-
utxoId: utxoId.toNumber(),
|
|
593
|
-
vaultId: details.vaultId,
|
|
594
|
-
lockPrice: details.lockPrice
|
|
595
|
-
});
|
|
596
|
-
data.lockPrice = formatArgons(details.lockPrice);
|
|
597
|
-
reloadVaults.add(details.vaultId);
|
|
598
|
-
}
|
|
599
|
-
} else if (event.section === "system") {
|
|
600
|
-
if (client.events.system.ExtrinsicFailed.is(event)) {
|
|
601
|
-
const { dispatchError } = event.data;
|
|
602
|
-
if (dispatchError.isModule) {
|
|
603
|
-
const decoded = api.registry.findMetaError(dispatchError.asModule);
|
|
604
|
-
const { name, section } = decoded;
|
|
605
|
-
block ??= await client.rpc.chain.getBlock(header.hash);
|
|
606
|
-
const extrinsicIndex = phase.asApplyExtrinsic.toNumber();
|
|
607
|
-
const ext = block.block.extrinsics[extrinsicIndex];
|
|
608
|
-
if (this.options.shouldLog) {
|
|
609
|
-
console.log(
|
|
610
|
-
`> [Failed Tx] ${section}.${name} -> ${ext.method.section}.${ext.method.method} (nonce=${ext.nonce})`,
|
|
611
|
-
ext.toHuman()?.method?.args
|
|
612
|
-
);
|
|
613
|
-
}
|
|
614
|
-
} else {
|
|
615
|
-
if (this.options.shouldLog) {
|
|
616
|
-
console.log(`x [Failed Tx] ${dispatchError.toJSON()}`);
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
if (this.options.shouldLog && logEvent) {
|
|
622
|
-
console.log(`> ${event.section}.${event.method}`, data);
|
|
623
|
-
}
|
|
624
|
-
this.events.emit("event", header, event);
|
|
625
|
-
}
|
|
626
|
-
if (reloadVaults.size) this.events.emit("vaults-updated", header, reloadVaults);
|
|
627
|
-
const tick = getTickFromHeader(client, header);
|
|
628
|
-
const author = getAuthorFromHeader(client, header);
|
|
629
|
-
this.events.emit(
|
|
630
|
-
"block",
|
|
631
|
-
header,
|
|
632
|
-
{ tick, author },
|
|
633
|
-
events.map((x) => x.event)
|
|
634
|
-
);
|
|
635
|
-
}
|
|
636
|
-
async getBitcoinLockDetails(utxoId, blockHash) {
|
|
637
|
-
const client = await this.mainchain;
|
|
638
|
-
const api = await client.at(blockHash);
|
|
639
|
-
if (!this.locksById[utxoId]) {
|
|
640
|
-
const lock = await api.query.bitcoinLocks.locksByUtxoId(utxoId);
|
|
641
|
-
this.locksById[utxoId] = {
|
|
642
|
-
vaultId: lock.value.vaultId.toNumber(),
|
|
643
|
-
lockPrice: lock.value.lockPrice.toBigInt()
|
|
644
|
-
};
|
|
645
|
-
}
|
|
646
|
-
return this.locksById[utxoId];
|
|
647
|
-
}
|
|
648
|
-
};
|
|
649
|
-
|
|
650
|
-
// src/FrameCalculator.ts
|
|
651
|
-
var FrameCalculator = class _FrameCalculator {
|
|
652
|
-
miningConfig;
|
|
653
|
-
genesisTick;
|
|
654
|
-
tickMillis;
|
|
655
|
-
async load(client) {
|
|
656
|
-
return await this.getConfig(client);
|
|
657
|
-
}
|
|
658
|
-
async getForTick(client, tick) {
|
|
659
|
-
const { ticksBetweenFrames, biddingStartTick } = await this.getConfig(client);
|
|
660
|
-
const ticksSinceMiningStart = tick - biddingStartTick;
|
|
661
|
-
return Math.floor(ticksSinceMiningStart / ticksBetweenFrames);
|
|
662
|
-
}
|
|
663
|
-
async getTickRangeForFrame(client, frameId) {
|
|
664
|
-
const { ticksBetweenFrames, biddingStartTick } = await this.getConfig(client);
|
|
665
|
-
return _FrameCalculator.calculateTickRangeForFrame(frameId, {
|
|
666
|
-
ticksBetweenFrames,
|
|
667
|
-
biddingStartTick
|
|
668
|
-
});
|
|
669
|
-
}
|
|
670
|
-
async getForHeader(client, header) {
|
|
671
|
-
if (header.number.toNumber() === 0) return 0;
|
|
672
|
-
const tick = getTickFromHeader(client, header);
|
|
673
|
-
if (tick === void 0) return void 0;
|
|
674
|
-
return this.getForTick(client, tick);
|
|
675
|
-
}
|
|
676
|
-
static frameToDateRange(frameId, config2) {
|
|
677
|
-
const [start, end] = _FrameCalculator.calculateTickRangeForFrame(frameId, config2);
|
|
678
|
-
return [new Date(start * config2.tickMillis), new Date(end * config2.tickMillis)];
|
|
679
|
-
}
|
|
680
|
-
static calculateTickRangeForFrame(frameId, config2) {
|
|
681
|
-
const { ticksBetweenFrames, biddingStartTick } = config2;
|
|
682
|
-
const startingTick = biddingStartTick + Math.floor(frameId * ticksBetweenFrames);
|
|
683
|
-
const endingTick = startingTick + ticksBetweenFrames - 1;
|
|
684
|
-
return [startingTick, endingTick];
|
|
685
|
-
}
|
|
686
|
-
async getConfig(client) {
|
|
687
|
-
this.miningConfig ??= await client.query.miningSlot.miningConfig().then((x) => ({
|
|
688
|
-
ticksBetweenSlots: x.ticksBetweenSlots.toNumber(),
|
|
689
|
-
slotBiddingStartAfterTicks: x.slotBiddingStartAfterTicks.toNumber()
|
|
690
|
-
}));
|
|
691
|
-
this.genesisTick ??= await client.query.ticks.genesisTick().then((x) => x.toNumber());
|
|
692
|
-
this.tickMillis ??= await client.query.ticks.genesisTicker().then((x) => x.tickDurationMillis.toNumber());
|
|
693
|
-
const config2 = this.miningConfig;
|
|
694
|
-
const genesisTick = this.genesisTick;
|
|
695
|
-
return {
|
|
696
|
-
ticksBetweenFrames: config2.ticksBetweenSlots,
|
|
697
|
-
slotBiddingStartAfterTicks: config2.slotBiddingStartAfterTicks,
|
|
698
|
-
genesisTick,
|
|
699
|
-
tickMillis: this.tickMillis,
|
|
700
|
-
biddingStartTick: genesisTick + config2.slotBiddingStartAfterTicks
|
|
701
|
-
};
|
|
702
|
-
}
|
|
703
|
-
};
|
|
704
|
-
|
|
705
|
-
// src/AccountMiners.ts
|
|
706
|
-
var AccountMiners = class _AccountMiners {
|
|
707
|
-
constructor(accountset, registeredMiners, options = { shouldLog: false }) {
|
|
708
|
-
this.accountset = accountset;
|
|
709
|
-
this.options = options;
|
|
710
|
-
this.frameCalculator = new FrameCalculator();
|
|
711
|
-
for (const miner of registeredMiners) {
|
|
712
|
-
this.trackedAccountsByAddress[miner.address] = {
|
|
713
|
-
startingFrameId: miner.seat.startingFrameId,
|
|
714
|
-
subaccountIndex: miner.subaccountIndex
|
|
715
|
-
};
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
events = createNanoEvents();
|
|
719
|
-
frameCalculator;
|
|
720
|
-
trackedAccountsByAddress = {};
|
|
721
|
-
async watch() {
|
|
722
|
-
const blockWatch = new BlockWatch(this.accountset.client, {
|
|
723
|
-
shouldLog: this.options.shouldLog
|
|
724
|
-
});
|
|
725
|
-
blockWatch.events.on("block", this.onBlock.bind(this));
|
|
726
|
-
await blockWatch.start();
|
|
727
|
-
return blockWatch;
|
|
728
|
-
}
|
|
729
|
-
async onBlock(header, digests, events) {
|
|
730
|
-
const { author, tick } = digests;
|
|
731
|
-
if (author) {
|
|
732
|
-
const voteAuthor = this.trackedAccountsByAddress[author];
|
|
733
|
-
if (voteAuthor && this.options.shouldLog) {
|
|
734
|
-
console.log("> Our vote author", this.accountset.accountRegistry.getName(author));
|
|
735
|
-
}
|
|
736
|
-
} else {
|
|
737
|
-
console.warn("> No vote author found");
|
|
738
|
-
}
|
|
739
|
-
const client = await this.accountset.client;
|
|
740
|
-
const currentFrameId = await this.frameCalculator.getForTick(client, tick);
|
|
741
|
-
let newMiners;
|
|
742
|
-
const dataByCohort = { duringFrameId: currentFrameId };
|
|
743
|
-
for (const event of events) {
|
|
744
|
-
if (client.events.miningSlot.NewMiners.is(event)) {
|
|
745
|
-
newMiners = {
|
|
746
|
-
frameId: event.data.frameId.toNumber(),
|
|
747
|
-
addresses: event.data.newMiners.map((x) => x.accountId.toHuman())
|
|
748
|
-
};
|
|
749
|
-
}
|
|
750
|
-
if (client.events.blockRewards.RewardCreated.is(event)) {
|
|
751
|
-
const { rewards } = event.data;
|
|
752
|
-
for (const reward of rewards) {
|
|
753
|
-
const { argons, ownership } = reward;
|
|
754
|
-
const entry = this.trackedAccountsByAddress[author];
|
|
755
|
-
if (entry) {
|
|
756
|
-
dataByCohort[entry.startingFrameId] ??= {
|
|
757
|
-
argonsMinted: 0n,
|
|
758
|
-
argonsMined: 0n,
|
|
759
|
-
argonotsMined: 0n
|
|
760
|
-
};
|
|
761
|
-
dataByCohort[entry.startingFrameId].argonotsMined += ownership.toBigInt();
|
|
762
|
-
dataByCohort[entry.startingFrameId].argonsMined += argons.toBigInt();
|
|
763
|
-
this.events.emit("mined", header, {
|
|
764
|
-
author,
|
|
765
|
-
argons: argons.toBigInt(),
|
|
766
|
-
argonots: ownership.toBigInt(),
|
|
767
|
-
forCohortWithStartingFrameId: entry.startingFrameId,
|
|
768
|
-
duringFrameId: currentFrameId
|
|
769
|
-
});
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
if (client.events.mint.MiningMint.is(event)) {
|
|
774
|
-
const { perMiner } = event.data;
|
|
775
|
-
const amountPerMiner = perMiner.toBigInt();
|
|
776
|
-
if (amountPerMiner > 0n) {
|
|
777
|
-
for (const [address, info] of Object.entries(this.trackedAccountsByAddress)) {
|
|
778
|
-
const { startingFrameId } = info;
|
|
779
|
-
dataByCohort[startingFrameId] ??= {
|
|
780
|
-
argonsMinted: 0n,
|
|
781
|
-
argonsMined: 0n,
|
|
782
|
-
argonotsMined: 0n
|
|
783
|
-
};
|
|
784
|
-
dataByCohort[startingFrameId].argonsMinted += amountPerMiner;
|
|
785
|
-
this.events.emit("minted", header, {
|
|
786
|
-
accountId: address,
|
|
787
|
-
argons: amountPerMiner,
|
|
788
|
-
forCohortWithStartingFrameId: startingFrameId,
|
|
789
|
-
duringFrameId: currentFrameId
|
|
790
|
-
});
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
if (newMiners) {
|
|
796
|
-
this.newCohortMiners(newMiners.frameId, newMiners.addresses);
|
|
797
|
-
}
|
|
798
|
-
return dataByCohort;
|
|
799
|
-
}
|
|
800
|
-
newCohortMiners(frameId, addresses) {
|
|
801
|
-
for (const [address, info] of Object.entries(this.trackedAccountsByAddress)) {
|
|
802
|
-
if (info.startingFrameId === frameId - 10) {
|
|
803
|
-
delete this.trackedAccountsByAddress[address];
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
for (const address of addresses) {
|
|
807
|
-
const entry = this.accountset.subAccountsByAddress[address];
|
|
808
|
-
if (entry) {
|
|
809
|
-
this.trackedAccountsByAddress[address] = {
|
|
810
|
-
startingFrameId: frameId,
|
|
811
|
-
subaccountIndex: entry.index
|
|
812
|
-
};
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
static async loadAt(accountset, options = {}) {
|
|
817
|
-
const seats = await accountset.miningSeats(options.blockHash);
|
|
818
|
-
const registered = seats.filter((x) => x.seat !== void 0);
|
|
819
|
-
return new _AccountMiners(accountset, registered, {
|
|
820
|
-
shouldLog: options.shouldLog ?? false
|
|
821
|
-
});
|
|
822
|
-
}
|
|
823
|
-
};
|
|
824
|
-
|
|
825
|
-
// src/Accountset.ts
|
|
826
|
-
import { u8aToHex as u8aToHex2 } from "@polkadot/util";
|
|
827
|
-
var Accountset = class {
|
|
828
|
-
txSubmitterPair;
|
|
829
|
-
isProxy = false;
|
|
830
|
-
seedAddress;
|
|
831
|
-
subAccountsByAddress = {};
|
|
832
|
-
accountRegistry;
|
|
833
|
-
client;
|
|
834
|
-
get addresses() {
|
|
835
|
-
return [this.seedAddress, ...Object.keys(this.subAccountsByAddress)];
|
|
836
|
-
}
|
|
837
|
-
get namedAccounts() {
|
|
838
|
-
return this.accountRegistry.namedAccounts;
|
|
839
|
-
}
|
|
840
|
-
sessionKeySeed;
|
|
841
|
-
constructor(options) {
|
|
842
|
-
if ("seedAccount" in options) {
|
|
843
|
-
this.txSubmitterPair = options.seedAccount;
|
|
844
|
-
this.seedAddress = options.seedAccount.address;
|
|
845
|
-
this.isProxy = false;
|
|
846
|
-
} else {
|
|
847
|
-
this.isProxy = options.isProxy;
|
|
848
|
-
this.txSubmitterPair = options.txSubmitter;
|
|
849
|
-
this.seedAddress = options.seedAddress;
|
|
850
|
-
}
|
|
851
|
-
this.sessionKeySeed = options.sessionKeySeedOrMnemonic;
|
|
852
|
-
this.accountRegistry = options.accountRegistry ?? AccountRegistry.factory(options.name);
|
|
853
|
-
this.client = options.client;
|
|
854
|
-
const defaultRange = options.subaccountRange ?? getDefaultSubaccountRange();
|
|
855
|
-
this.accountRegistry.register(this.seedAddress, `${this.accountRegistry.me}//seed`);
|
|
856
|
-
for (const i of defaultRange) {
|
|
857
|
-
const pair = this.txSubmitterPair.derive(`//${i}`);
|
|
858
|
-
this.subAccountsByAddress[pair.address] = { pair, index: i };
|
|
859
|
-
this.accountRegistry.register(pair.address, `${this.accountRegistry.me}//${i}`);
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
async submitterBalance(blockHash) {
|
|
863
|
-
const client = await this.client;
|
|
864
|
-
const api = blockHash ? await client.at(blockHash) : client;
|
|
865
|
-
const accountData = await api.query.system.account(this.txSubmitterPair.address);
|
|
866
|
-
return accountData.data.free.toBigInt();
|
|
867
|
-
}
|
|
868
|
-
async balance(blockHash) {
|
|
869
|
-
const client = await this.client;
|
|
870
|
-
const api = blockHash ? await client.at(blockHash) : client;
|
|
871
|
-
const accountData = await api.query.system.account(this.seedAddress);
|
|
872
|
-
return accountData.data.free.toBigInt();
|
|
873
|
-
}
|
|
874
|
-
async totalArgonsAt(blockHash) {
|
|
875
|
-
const client = await this.client;
|
|
876
|
-
const api = blockHash ? await client.at(blockHash) : client;
|
|
877
|
-
const addresses = this.addresses;
|
|
878
|
-
const results = await api.query.system.account.multi(addresses);
|
|
879
|
-
return results.map((account, i) => {
|
|
880
|
-
const address = addresses[i];
|
|
881
|
-
return {
|
|
882
|
-
address,
|
|
883
|
-
amount: account.data.free.toBigInt(),
|
|
884
|
-
index: this.subAccountsByAddress[address]?.index ?? Number.NaN
|
|
885
|
-
};
|
|
886
|
-
});
|
|
887
|
-
}
|
|
888
|
-
async totalArgonotsAt(blockHash) {
|
|
889
|
-
const client = await this.client;
|
|
890
|
-
const api = blockHash ? await client.at(blockHash) : client;
|
|
891
|
-
const addresses = this.addresses;
|
|
892
|
-
const results = await api.query.ownership.account.multi(addresses);
|
|
893
|
-
return results.map((account, i) => {
|
|
894
|
-
const address = addresses[i];
|
|
895
|
-
return {
|
|
896
|
-
address,
|
|
897
|
-
amount: account.free.toBigInt(),
|
|
898
|
-
index: this.subAccountsByAddress[address]?.index ?? Number.NaN
|
|
899
|
-
};
|
|
900
|
-
});
|
|
901
|
-
}
|
|
902
|
-
async getAvailableMinerAccounts(maxSeats) {
|
|
903
|
-
const miningSeats = await this.miningSeats();
|
|
904
|
-
const subaccountRange = [];
|
|
905
|
-
for (const seat of miningSeats) {
|
|
906
|
-
if (seat.hasWinningBid) {
|
|
907
|
-
continue;
|
|
908
|
-
}
|
|
909
|
-
if (seat.isLastDay || seat.seat === void 0) {
|
|
910
|
-
subaccountRange.push({
|
|
911
|
-
index: seat.subaccountIndex,
|
|
912
|
-
isRebid: seat.seat !== void 0,
|
|
913
|
-
address: seat.address
|
|
914
|
-
});
|
|
915
|
-
if (subaccountRange.length >= maxSeats) {
|
|
916
|
-
break;
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
return subaccountRange;
|
|
921
|
-
}
|
|
922
|
-
async loadRegisteredMiners(api) {
|
|
923
|
-
const addresses = Object.keys(this.subAccountsByAddress);
|
|
924
|
-
const rawIndices = await api.query.miningSlot.accountIndexLookup.multi(addresses);
|
|
925
|
-
const frameIds = [
|
|
926
|
-
...new Set(
|
|
927
|
-
rawIndices.map((x) => x.isNone ? void 0 : x.value[0].toNumber()).filter((x) => x !== void 0)
|
|
928
|
-
)
|
|
929
|
-
];
|
|
930
|
-
const bidAmountsByFrame = {};
|
|
931
|
-
if (frameIds.length) {
|
|
932
|
-
const cohorts = await api.query.miningSlot.minersByCohort.multi(frameIds);
|
|
933
|
-
for (let i = 0; i < frameIds.length; i++) {
|
|
934
|
-
const cohort = cohorts[i];
|
|
935
|
-
const frameId = frameIds[i];
|
|
936
|
-
bidAmountsByFrame[frameId] = cohort.map((x) => x.bid.toBigInt());
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
const addressToMiningIndex = {};
|
|
940
|
-
for (let i = 0; i < addresses.length; i++) {
|
|
941
|
-
const address = addresses[i];
|
|
942
|
-
if (rawIndices[i].isNone) continue;
|
|
943
|
-
const [frameIdRaw, indexRaw] = rawIndices[i].value;
|
|
944
|
-
const frameId = frameIdRaw.toNumber();
|
|
945
|
-
const index = indexRaw.toNumber();
|
|
946
|
-
const bidAmount = bidAmountsByFrame[frameId]?.[index];
|
|
947
|
-
addressToMiningIndex[address] = {
|
|
948
|
-
startingFrameId: frameId,
|
|
949
|
-
index,
|
|
950
|
-
bidAmount: bidAmount ?? 0n
|
|
951
|
-
};
|
|
952
|
-
}
|
|
953
|
-
const nextFrameId = await api.query.miningSlot.nextFrameId();
|
|
954
|
-
return addresses.map((address) => {
|
|
955
|
-
const cohort = addressToMiningIndex[address];
|
|
956
|
-
let isLastDay = false;
|
|
957
|
-
if (cohort !== void 0) {
|
|
958
|
-
isLastDay = nextFrameId.toNumber() - cohort.startingFrameId === 10;
|
|
959
|
-
}
|
|
960
|
-
return {
|
|
961
|
-
address,
|
|
962
|
-
seat: cohort,
|
|
963
|
-
isLastDay,
|
|
964
|
-
subaccountIndex: this.subAccountsByAddress[address]?.index ?? Number.NaN
|
|
965
|
-
};
|
|
966
|
-
});
|
|
967
|
-
}
|
|
968
|
-
async miningSeats(blockHash) {
|
|
969
|
-
const client = await this.client;
|
|
970
|
-
const api = blockHash ? await client.at(blockHash) : client;
|
|
971
|
-
const miners = await this.loadRegisteredMiners(api);
|
|
972
|
-
const nextCohort = await api.query.miningSlot.bidsForNextSlotCohort();
|
|
973
|
-
return miners.map((miner) => {
|
|
974
|
-
const bid = nextCohort.find((x) => x.accountId.toHuman() === miner.address);
|
|
975
|
-
return {
|
|
976
|
-
...miner,
|
|
977
|
-
hasWinningBid: !!bid,
|
|
978
|
-
bidAmount: bid?.bid.toBigInt() ?? miner.seat?.bidAmount ?? 0n
|
|
979
|
-
};
|
|
980
|
-
});
|
|
981
|
-
}
|
|
982
|
-
async bids(blockHash) {
|
|
983
|
-
const client = await this.client;
|
|
984
|
-
const api = blockHash ? await client.at(blockHash) : client;
|
|
985
|
-
const addresses = Object.keys(this.subAccountsByAddress);
|
|
986
|
-
const nextCohort = await api.query.miningSlot.bidsForNextSlotCohort();
|
|
987
|
-
const registrationsByAddress = Object.fromEntries(
|
|
988
|
-
nextCohort.map((x, i) => [x.accountId.toHuman(), { ...x, index: i }])
|
|
989
|
-
);
|
|
990
|
-
return addresses.map((address) => {
|
|
991
|
-
const entry = registrationsByAddress[address];
|
|
992
|
-
return {
|
|
993
|
-
address,
|
|
994
|
-
bidPlace: entry?.index,
|
|
995
|
-
bidAmount: entry?.bid?.toBigInt(),
|
|
996
|
-
index: this.subAccountsByAddress[address]?.index ?? Number.NaN
|
|
997
|
-
};
|
|
998
|
-
});
|
|
999
|
-
}
|
|
1000
|
-
async consolidate(subaccounts) {
|
|
1001
|
-
const client = await this.client;
|
|
1002
|
-
const accounts = this.getAccountsInRange(subaccounts);
|
|
1003
|
-
const results = [];
|
|
1004
|
-
await Promise.allSettled(
|
|
1005
|
-
accounts.map(({ pair, index }) => {
|
|
1006
|
-
if (!pair) {
|
|
1007
|
-
results.push({
|
|
1008
|
-
index,
|
|
1009
|
-
failedError: new Error(`No keypair for //${index}`)
|
|
1010
|
-
});
|
|
1011
|
-
return Promise.resolve();
|
|
1012
|
-
}
|
|
1013
|
-
return new Promise((resolve) => {
|
|
1014
|
-
client.tx.utility.batchAll([
|
|
1015
|
-
client.tx.balances.transferAll(this.seedAddress, true),
|
|
1016
|
-
client.tx.ownership.transferAll(this.seedAddress, true)
|
|
1017
|
-
]).signAndSend(pair, (cb) => {
|
|
1018
|
-
logExtrinsicResult(cb);
|
|
1019
|
-
if (cb.dispatchError) {
|
|
1020
|
-
const error = dispatchErrorToString(client, cb.dispatchError);
|
|
1021
|
-
results.push({
|
|
1022
|
-
index,
|
|
1023
|
-
failedError: new Error(`Error consolidating //${index}: ${error}`)
|
|
1024
|
-
});
|
|
1025
|
-
resolve();
|
|
1026
|
-
}
|
|
1027
|
-
if (cb.isInBlock) {
|
|
1028
|
-
results.push({ index, inBlock: cb.status.asInBlock.toHex() });
|
|
1029
|
-
resolve();
|
|
1030
|
-
}
|
|
1031
|
-
}).catch((e) => {
|
|
1032
|
-
results.push({ index, failedError: e });
|
|
1033
|
-
resolve();
|
|
1034
|
-
});
|
|
1035
|
-
});
|
|
1036
|
-
})
|
|
1037
|
-
);
|
|
1038
|
-
return results;
|
|
1039
|
-
}
|
|
1040
|
-
status(opts) {
|
|
1041
|
-
const { argons, argonots, accountSubset, bids, seats } = opts;
|
|
1042
|
-
const accounts = [
|
|
1043
|
-
{
|
|
1044
|
-
index: "main",
|
|
1045
|
-
address: this.seedAddress,
|
|
1046
|
-
argons: formatArgons(argons.find((x) => x.address === this.seedAddress)?.amount ?? 0n),
|
|
1047
|
-
argonots: formatArgons(argonots.find((x) => x.address === this.seedAddress)?.amount ?? 0n)
|
|
1048
|
-
}
|
|
1049
|
-
];
|
|
1050
|
-
for (const [address, { index }] of Object.entries(this.subAccountsByAddress)) {
|
|
1051
|
-
const argonAmount = argons.find((x) => x.address === address)?.amount ?? 0n;
|
|
1052
|
-
const argonotAmount = argonots.find((x) => x.address === address)?.amount ?? 0n;
|
|
1053
|
-
const bid = bids.find((x) => x.address === address);
|
|
1054
|
-
const seat = seats.find((x) => x.address === address)?.seat;
|
|
1055
|
-
const entry = {
|
|
1056
|
-
index: ` //${index}`,
|
|
1057
|
-
address,
|
|
1058
|
-
argons: formatArgons(argonAmount),
|
|
1059
|
-
argonots: formatArgons(argonotAmount),
|
|
1060
|
-
seat,
|
|
1061
|
-
bidPlace: bid?.bidPlace,
|
|
1062
|
-
bidAmount: bid?.bidAmount ?? 0n
|
|
1063
|
-
};
|
|
1064
|
-
if (accountSubset) {
|
|
1065
|
-
entry.isWorkingOn = accountSubset.some((x) => x.address === address);
|
|
1066
|
-
}
|
|
1067
|
-
accounts.push(entry);
|
|
1068
|
-
}
|
|
1069
|
-
return accounts;
|
|
1070
|
-
}
|
|
1071
|
-
async registerKeys(url) {
|
|
1072
|
-
const client = await getClient(url.replace("ws:", "http:"));
|
|
1073
|
-
const keys = this.keys();
|
|
1074
|
-
for (const [name, key] of Object.entries(keys)) {
|
|
1075
|
-
console.log("Registering key", name, key.publicKey);
|
|
1076
|
-
const result = await client.rpc.author.insertKey(name, key.privateKey, key.publicKey);
|
|
1077
|
-
const saved = await client.rpc.author.hasKey(key.publicKey, name);
|
|
1078
|
-
if (!saved) {
|
|
1079
|
-
console.error("Failed to register key", name, key.publicKey);
|
|
1080
|
-
throw new Error(`Failed to register ${name} key ${key.publicKey}`);
|
|
1081
|
-
}
|
|
1082
|
-
console.log(`Registered ${name} key`, result.toHuman());
|
|
1083
|
-
}
|
|
1084
|
-
await client.disconnect();
|
|
1085
|
-
}
|
|
1086
|
-
keys(keysVersion) {
|
|
1087
|
-
const config2 = getConfig();
|
|
1088
|
-
let version = keysVersion ?? config2.keysVersion ?? 0;
|
|
1089
|
-
const seed = this.sessionKeySeed ?? config2.keySeedOrMnemonic;
|
|
1090
|
-
if (!seed) {
|
|
1091
|
-
throw new Error("KEYS_MNEMONIC environment variable not set. Cannot derive keys.");
|
|
1092
|
-
}
|
|
1093
|
-
const blockSealKey = `${seed}//block-seal//${version}`;
|
|
1094
|
-
const granKey = `${seed}//grandpa//${version}`;
|
|
1095
|
-
const blockSealAccount = new Keyring().createFromUri(blockSealKey, {
|
|
1096
|
-
type: "ed25519"
|
|
1097
|
-
});
|
|
1098
|
-
const grandpaAccount = new Keyring().createFromUri(granKey, {
|
|
1099
|
-
type: "ed25519"
|
|
1100
|
-
});
|
|
1101
|
-
return {
|
|
1102
|
-
seal: {
|
|
1103
|
-
privateKey: blockSealKey,
|
|
1104
|
-
publicKey: u8aToHex2(blockSealAccount.publicKey),
|
|
1105
|
-
rawPublicKey: blockSealAccount.publicKey
|
|
1106
|
-
},
|
|
1107
|
-
gran: {
|
|
1108
|
-
privateKey: granKey,
|
|
1109
|
-
publicKey: u8aToHex2(grandpaAccount.publicKey),
|
|
1110
|
-
rawPublicKey: grandpaAccount.publicKey
|
|
1111
|
-
}
|
|
1112
|
-
};
|
|
1113
|
-
}
|
|
1114
|
-
async tx(tx) {
|
|
1115
|
-
const client = await this.client;
|
|
1116
|
-
return new TxSubmitter(client, tx, this.txSubmitterPair);
|
|
1117
|
-
}
|
|
1118
|
-
/**
|
|
1119
|
-
* Create but don't submit a mining bid transaction.
|
|
1120
|
-
* @param options
|
|
1121
|
-
*/
|
|
1122
|
-
async createMiningBidTx(options) {
|
|
1123
|
-
const client = await this.client;
|
|
1124
|
-
const { bidAmount, subaccounts } = options;
|
|
1125
|
-
const batch = client.tx.utility.batch(
|
|
1126
|
-
subaccounts.map((x) => {
|
|
1127
|
-
const keys = this.keys();
|
|
1128
|
-
return client.tx.miningSlot.bid(
|
|
1129
|
-
bidAmount,
|
|
1130
|
-
{
|
|
1131
|
-
grandpa: keys.gran.rawPublicKey,
|
|
1132
|
-
blockSealAuthority: keys.seal.rawPublicKey
|
|
1133
|
-
},
|
|
1134
|
-
x.address
|
|
1135
|
-
);
|
|
1136
|
-
})
|
|
1137
|
-
);
|
|
1138
|
-
let tx = batch;
|
|
1139
|
-
if (this.isProxy) {
|
|
1140
|
-
tx = client.tx.proxy.proxy(this.seedAddress, "MiningBid", batch);
|
|
1141
|
-
}
|
|
1142
|
-
return new TxSubmitter(client, tx, this.txSubmitterPair);
|
|
1143
|
-
}
|
|
1144
|
-
/**
|
|
1145
|
-
* Create a mining bid. This will create a bid for each account in the given range from the seed account as funding.
|
|
1146
|
-
*/
|
|
1147
|
-
async createMiningBids(options) {
|
|
1148
|
-
const accounts = this.getAccountsInRange(options.subaccountRange);
|
|
1149
|
-
const client = await this.client;
|
|
1150
|
-
const submitter = await this.createMiningBidTx({
|
|
1151
|
-
...options,
|
|
1152
|
-
subaccounts: accounts
|
|
1153
|
-
});
|
|
1154
|
-
const { tip = 0n } = options;
|
|
1155
|
-
const txFee = await submitter.feeEstimate(tip);
|
|
1156
|
-
let minBalance = options.bidAmount * BigInt(accounts.length);
|
|
1157
|
-
let totalFees = tip + 1n + txFee;
|
|
1158
|
-
const seedBalance = await client.query.system.account(this.seedAddress).then((x) => x.data.free.toBigInt());
|
|
1159
|
-
if (!this.isProxy) {
|
|
1160
|
-
minBalance += totalFees;
|
|
1161
|
-
}
|
|
1162
|
-
if (seedBalance < minBalance) {
|
|
1163
|
-
throw new Error(
|
|
1164
|
-
`Insufficient balance to create mining bids. Seed account has ${formatArgons(
|
|
1165
|
-
seedBalance
|
|
1166
|
-
)} but needs ${formatArgons(minBalance)}`
|
|
1167
|
-
);
|
|
1168
|
-
}
|
|
1169
|
-
if (this.isProxy) {
|
|
1170
|
-
const { canAfford, availableBalance } = await submitter.canAfford({
|
|
1171
|
-
tip
|
|
1172
|
-
});
|
|
1173
|
-
if (!canAfford) {
|
|
1174
|
-
throw new Error(
|
|
1175
|
-
`Insufficient balance to pay proxy fees. Proxy account has ${formatArgons(
|
|
1176
|
-
availableBalance
|
|
1177
|
-
)} but needs ${formatArgons(totalFees)}`
|
|
1178
|
-
);
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
console.log("Creating bids", {
|
|
1182
|
-
perSeatBid: options.bidAmount,
|
|
1183
|
-
subaccounts: options.subaccountRange,
|
|
1184
|
-
txFee
|
|
1185
|
-
});
|
|
1186
|
-
const txResult = await submitter.submit({
|
|
1187
|
-
tip,
|
|
1188
|
-
useLatestNonce: true
|
|
1189
|
-
});
|
|
1190
|
-
const bidError = await txResult.inBlockPromise.then(() => void 0).catch((x) => x);
|
|
1191
|
-
return {
|
|
1192
|
-
finalFee: txResult.finalFee,
|
|
1193
|
-
bidError,
|
|
1194
|
-
blockHash: txResult.includedInBlock,
|
|
1195
|
-
successfulBids: txResult.batchInterruptedIndex !== void 0 ? txResult.batchInterruptedIndex : accounts.length
|
|
1196
|
-
};
|
|
1197
|
-
}
|
|
1198
|
-
getAccountsInRange(range) {
|
|
1199
|
-
const entries = new Set(range ?? getDefaultSubaccountRange());
|
|
1200
|
-
return Object.entries(this.subAccountsByAddress).filter(([_, account]) => {
|
|
1201
|
-
return entries.has(account.index);
|
|
1202
|
-
}).map(([address, { pair, index }]) => ({ pair, index, address }));
|
|
1203
|
-
}
|
|
1204
|
-
async watchBlocks(shouldLog = false) {
|
|
1205
|
-
const accountMiners = await AccountMiners.loadAt(this, { shouldLog });
|
|
1206
|
-
await accountMiners.watch();
|
|
1207
|
-
return accountMiners;
|
|
1208
|
-
}
|
|
1209
|
-
};
|
|
1210
|
-
function getDefaultSubaccountRange() {
|
|
1211
|
-
try {
|
|
1212
|
-
const config2 = getConfig();
|
|
1213
|
-
return parseSubaccountRange(config2.subaccountRange ?? "0-9");
|
|
1214
|
-
} catch {
|
|
1215
|
-
console.error(
|
|
1216
|
-
"Failed to parse SUBACCOUNT_RANGE configuration. Defaulting to 0-9. Please check the format of the subaccountRange config value."
|
|
1217
|
-
);
|
|
1218
|
-
return Array.from({ length: 10 }, (_, i) => i);
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
function parseSubaccountRange(range) {
|
|
1222
|
-
if (!range) {
|
|
1223
|
-
return void 0;
|
|
1224
|
-
}
|
|
1225
|
-
const indices = [];
|
|
1226
|
-
for (const entry of range.split(",")) {
|
|
1227
|
-
if (entry.includes("-")) {
|
|
1228
|
-
const [start, end] = entry.split("-").map((x) => parseInt(x, 10));
|
|
1229
|
-
for (let i = start; i <= end; i++) {
|
|
1230
|
-
indices.push(i);
|
|
1231
|
-
}
|
|
1232
|
-
continue;
|
|
1233
|
-
}
|
|
1234
|
-
const record = parseInt(entry.trim(), 10);
|
|
1235
|
-
if (Number.isNaN(record) || !Number.isInteger(record)) {
|
|
1236
|
-
throw new Error(`Invalid range entry: ${entry}`);
|
|
1237
|
-
}
|
|
1238
|
-
if (Number.isInteger(record)) {
|
|
1239
|
-
indices.push(record);
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
return indices;
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
// src/MiningBids.ts
|
|
1246
|
-
import { printTable } from "console-table-printer";
|
|
1247
|
-
var MiningBids = class {
|
|
1248
|
-
constructor(client, shouldLog = true) {
|
|
1249
|
-
this.client = client;
|
|
1250
|
-
this.shouldLog = shouldLog;
|
|
1251
|
-
}
|
|
1252
|
-
nextCohort = [];
|
|
1253
|
-
async maxCohortSize() {
|
|
1254
|
-
const client = await this.client;
|
|
1255
|
-
return client.query.miningSlot.nextCohortSize().then((x) => x.toNumber());
|
|
1256
|
-
}
|
|
1257
|
-
async onCohortChange(options) {
|
|
1258
|
-
const { onBiddingStart, onBiddingEnd } = options;
|
|
1259
|
-
const client = await this.client;
|
|
1260
|
-
let openCohortStartingFrameId = 0;
|
|
1261
|
-
const unsubscribe = await client.queryMulti(
|
|
1262
|
-
[
|
|
1263
|
-
client.query.miningSlot.isNextSlotBiddingOpen,
|
|
1264
|
-
client.query.miningSlot.nextFrameId
|
|
1265
|
-
],
|
|
1266
|
-
async ([isBiddingOpen, rawNextCohortStartingFrameId]) => {
|
|
1267
|
-
const nextFrameId = rawNextCohortStartingFrameId.toNumber();
|
|
1268
|
-
if (isBiddingOpen.isTrue) {
|
|
1269
|
-
if (openCohortStartingFrameId !== 0) {
|
|
1270
|
-
await onBiddingEnd?.(openCohortStartingFrameId);
|
|
1271
|
-
}
|
|
1272
|
-
openCohortStartingFrameId = nextFrameId;
|
|
1273
|
-
await onBiddingStart?.(nextFrameId);
|
|
1274
|
-
} else {
|
|
1275
|
-
await onBiddingEnd?.(nextFrameId);
|
|
1276
|
-
openCohortStartingFrameId = 0;
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1279
|
-
);
|
|
1280
|
-
return { unsubscribe };
|
|
1281
|
-
}
|
|
1282
|
-
async watch(accountNames, blockHash, printFn) {
|
|
1283
|
-
const client = await this.client;
|
|
1284
|
-
const api = blockHash ? await client.at(blockHash) : client;
|
|
1285
|
-
const unsubscribe = await api.query.miningSlot.bidsForNextSlotCohort(async (next) => {
|
|
1286
|
-
this.nextCohort = await Promise.all(next.map((x) => this.toBid(accountNames, x)));
|
|
1287
|
-
if (!this.shouldLog) return;
|
|
1288
|
-
console.clear();
|
|
1289
|
-
const block = await client.query.system.number();
|
|
1290
|
-
if (!printFn) {
|
|
1291
|
-
console.log("At block", block.toNumber());
|
|
1292
|
-
this.print();
|
|
1293
|
-
} else {
|
|
1294
|
-
printFn(block.toNumber());
|
|
1295
|
-
}
|
|
1296
|
-
});
|
|
1297
|
-
return { unsubscribe };
|
|
1298
|
-
}
|
|
1299
|
-
async loadAt(accountNames, blockHash) {
|
|
1300
|
-
const client = await this.client;
|
|
1301
|
-
const api = blockHash ? await client.at(blockHash) : client;
|
|
1302
|
-
const nextCohort = await api.query.miningSlot.bidsForNextSlotCohort();
|
|
1303
|
-
this.nextCohort = await Promise.all(nextCohort.map((x) => this.toBid(accountNames, x)));
|
|
1304
|
-
}
|
|
1305
|
-
async toBid(accountNames, bid) {
|
|
1306
|
-
return {
|
|
1307
|
-
accountId: bid.accountId.toString(),
|
|
1308
|
-
isOurs: accountNames.get(bid.accountId.toString()) ?? "n",
|
|
1309
|
-
bidAmount: bid.bid.toBigInt()
|
|
1310
|
-
};
|
|
1311
|
-
}
|
|
1312
|
-
print() {
|
|
1313
|
-
const bids = this.nextCohort.map((bid) => {
|
|
1314
|
-
return {
|
|
1315
|
-
account: bid.accountId,
|
|
1316
|
-
isOurs: bid.isOurs,
|
|
1317
|
-
bidAmount: formatArgons(bid.bidAmount)
|
|
1318
|
-
};
|
|
1319
|
-
});
|
|
1320
|
-
if (bids.length) {
|
|
1321
|
-
console.log("\n\nMining Bids:");
|
|
1322
|
-
printTable(bids);
|
|
1323
|
-
}
|
|
1324
|
-
}
|
|
1325
|
-
};
|
|
1326
|
-
|
|
1327
|
-
// src/Vault.ts
|
|
1328
|
-
import BigNumber2, * as BN2 from "bignumber.js";
|
|
1329
|
-
import bs58check from "bs58check";
|
|
1330
|
-
import { hexToU8a } from "@polkadot/util";
|
|
1331
|
-
var { ROUND_FLOOR: ROUND_FLOOR2 } = BN2;
|
|
1332
|
-
var Vault = class _Vault {
|
|
1333
|
-
constructor(id, vault, tickDuration) {
|
|
1334
|
-
this.tickDuration = tickDuration;
|
|
1335
|
-
this.vaultId = id;
|
|
1336
|
-
this.openedTick = vault.openedTick.toNumber();
|
|
1337
|
-
this.openedDate = new Date(this.openedTick * this.tickDuration);
|
|
1338
|
-
this.argonsScheduledForRelease = /* @__PURE__ */ new Map();
|
|
1339
|
-
this.load(vault);
|
|
1340
|
-
}
|
|
1341
|
-
securitization;
|
|
1342
|
-
argonsLocked;
|
|
1343
|
-
argonsPendingActivation;
|
|
1344
|
-
argonsScheduledForRelease;
|
|
1345
|
-
terms;
|
|
1346
|
-
operatorAccountId;
|
|
1347
|
-
isClosed;
|
|
1348
|
-
vaultId;
|
|
1349
|
-
pendingTerms;
|
|
1350
|
-
pendingTermsChangeTick;
|
|
1351
|
-
openedDate;
|
|
1352
|
-
openedTick;
|
|
1353
|
-
securitizationRatio;
|
|
1354
|
-
load(vault) {
|
|
1355
|
-
this.securitization = vault.securitization.toBigInt();
|
|
1356
|
-
this.securitizationRatio = convertFixedU128ToBigNumber(
|
|
1357
|
-
vault.securitizationRatio.toBigInt()
|
|
1358
|
-
).toNumber();
|
|
1359
|
-
this.argonsLocked = vault.argonsLocked.toBigInt();
|
|
1360
|
-
this.argonsPendingActivation = vault.argonsPendingActivation.toBigInt();
|
|
1361
|
-
if (vault.argonsScheduledForRelease.size > 0) {
|
|
1362
|
-
this.argonsScheduledForRelease.clear();
|
|
1363
|
-
for (const [tick, amount] of vault.argonsScheduledForRelease.entries()) {
|
|
1364
|
-
this.argonsScheduledForRelease.set(tick.toNumber(), amount.toBigInt());
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
this.terms = {
|
|
1368
|
-
bitcoinAnnualPercentRate: convertFixedU128ToBigNumber(
|
|
1369
|
-
vault.terms.bitcoinAnnualPercentRate.toBigInt()
|
|
1370
|
-
),
|
|
1371
|
-
bitcoinBaseFee: vault.terms.bitcoinBaseFee.toBigInt(),
|
|
1372
|
-
liquidityPoolProfitSharing: convertPermillToBigNumber(
|
|
1373
|
-
vault.terms.liquidityPoolProfitSharing.toBigInt()
|
|
1374
|
-
)
|
|
1375
|
-
};
|
|
1376
|
-
this.operatorAccountId = vault.operatorAccountId.toString();
|
|
1377
|
-
this.isClosed = vault.isClosed.valueOf();
|
|
1378
|
-
if (vault.pendingTerms.isSome) {
|
|
1379
|
-
const [tickApply, terms] = vault.pendingTerms.value;
|
|
1380
|
-
this.pendingTermsChangeTick = tickApply.toNumber();
|
|
1381
|
-
this.pendingTerms = {
|
|
1382
|
-
bitcoinAnnualPercentRate: convertFixedU128ToBigNumber(
|
|
1383
|
-
terms.bitcoinAnnualPercentRate.toBigInt()
|
|
1384
|
-
),
|
|
1385
|
-
bitcoinBaseFee: terms.bitcoinBaseFee.toBigInt(),
|
|
1386
|
-
liquidityPoolProfitSharing: convertPermillToBigNumber(
|
|
1387
|
-
vault.terms.liquidityPoolProfitSharing.toBigInt()
|
|
1388
|
-
)
|
|
1389
|
-
};
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
availableBitcoinSpace() {
|
|
1393
|
-
const recoverySecuritization = this.recoverySecuritization();
|
|
1394
|
-
const reLockable = this.getRelockCapacity();
|
|
1395
|
-
return this.securitization - recoverySecuritization - this.argonsLocked + reLockable;
|
|
1396
|
-
}
|
|
1397
|
-
getRelockCapacity() {
|
|
1398
|
-
return [...this.argonsScheduledForRelease.values()].reduce((acc, val) => acc + val, 0n);
|
|
1399
|
-
}
|
|
1400
|
-
securitizationRatioBN() {
|
|
1401
|
-
return new BigNumber2(this.securitizationRatio);
|
|
1402
|
-
}
|
|
1403
|
-
recoverySecuritization() {
|
|
1404
|
-
const reserved = new BigNumber2(1).div(this.securitizationRatioBN());
|
|
1405
|
-
return this.securitization - BigInt(reserved.multipliedBy(this.securitization.toString()).toFixed(0, ROUND_FLOOR2));
|
|
1406
|
-
}
|
|
1407
|
-
minimumSecuritization() {
|
|
1408
|
-
return BigInt(
|
|
1409
|
-
this.securitizationRatioBN().multipliedBy(this.argonsLocked.toString()).decimalPlaces(0, BigNumber2.ROUND_CEIL).toString()
|
|
1410
|
-
);
|
|
1411
|
-
}
|
|
1412
|
-
activatedSecuritization() {
|
|
1413
|
-
const activated = this.argonsLocked - this.argonsPendingActivation;
|
|
1414
|
-
const maxRatio = BigNumber2(Math.min(this.securitizationRatio, 2));
|
|
1415
|
-
return BigInt(maxRatio.multipliedBy(activated.toString()).toFixed(0, ROUND_FLOOR2));
|
|
1416
|
-
}
|
|
1417
|
-
/**
|
|
1418
|
-
* Returns the amount of Argons available to match per liquidity pool
|
|
1419
|
-
*/
|
|
1420
|
-
activatedSecuritizationPerSlot() {
|
|
1421
|
-
const activated = this.activatedSecuritization();
|
|
1422
|
-
return activated / 10n;
|
|
1423
|
-
}
|
|
1424
|
-
calculateBitcoinFee(amount) {
|
|
1425
|
-
const fee = this.terms.bitcoinAnnualPercentRate.multipliedBy(Number(amount)).integerValue(BigNumber2.ROUND_CEIL);
|
|
1426
|
-
return BigInt(fee.toString()) + this.terms.bitcoinBaseFee;
|
|
1427
|
-
}
|
|
1428
|
-
static async get(client, vaultId, tickDurationMillis) {
|
|
1429
|
-
const rawVault = await client.query.vaults.vaultsById(vaultId);
|
|
1430
|
-
if (rawVault.isNone) {
|
|
1431
|
-
throw new Error(`Vault with id ${vaultId} not found`);
|
|
1432
|
-
}
|
|
1433
|
-
const tickDuration = tickDurationMillis ?? await client.query.ticks.genesisTicker().then((x) => x.tickDurationMillis.toNumber());
|
|
1434
|
-
return new _Vault(vaultId, rawVault.unwrap(), tickDuration);
|
|
1435
|
-
}
|
|
1436
|
-
static async create(client, keypair, args, config2 = {}) {
|
|
1437
|
-
const {
|
|
1438
|
-
securitization,
|
|
1439
|
-
securitizationRatio,
|
|
1440
|
-
annualPercentRate,
|
|
1441
|
-
baseFee,
|
|
1442
|
-
bitcoinXpub,
|
|
1443
|
-
tip,
|
|
1444
|
-
doNotExceedBalance,
|
|
1445
|
-
txProgressCallback
|
|
1446
|
-
} = args;
|
|
1447
|
-
let xpubBytes = hexToU8a(bitcoinXpub);
|
|
1448
|
-
if (xpubBytes.length !== 78) {
|
|
1449
|
-
if (bitcoinXpub.startsWith("xpub") || bitcoinXpub.startsWith("tpub") || bitcoinXpub.startsWith("zpub")) {
|
|
1450
|
-
const bytes = bs58check.decode(bitcoinXpub);
|
|
1451
|
-
if (bytes.length !== 78) {
|
|
1452
|
-
throw new Error("Invalid Bitcoin xpub key length, must be 78 bytes");
|
|
1453
|
-
}
|
|
1454
|
-
xpubBytes = bytes;
|
|
1455
|
-
}
|
|
1456
|
-
}
|
|
1457
|
-
let vaultParams = {
|
|
1458
|
-
terms: {
|
|
1459
|
-
// convert to fixed u128
|
|
1460
|
-
bitcoinAnnualPercentRate: toFixedNumber(annualPercentRate, 18),
|
|
1461
|
-
bitcoinBaseFee: BigInt(baseFee),
|
|
1462
|
-
liquidityPoolProfitSharing: toFixedNumber(args.liquidityPoolProfitSharing, 6)
|
|
1463
|
-
},
|
|
1464
|
-
securitizationRatio: toFixedNumber(securitizationRatio, 18),
|
|
1465
|
-
securitization: BigInt(securitization),
|
|
1466
|
-
bitcoinXpubkey: xpubBytes
|
|
1467
|
-
};
|
|
1468
|
-
let tx = new TxSubmitter(client, client.tx.vaults.create(vaultParams), keypair);
|
|
1469
|
-
if (doNotExceedBalance) {
|
|
1470
|
-
const finalTip = tip ?? 0n;
|
|
1471
|
-
let txFee = await tx.feeEstimate(finalTip);
|
|
1472
|
-
while (txFee + finalTip + vaultParams.securitization > doNotExceedBalance) {
|
|
1473
|
-
vaultParams.securitization = doNotExceedBalance - txFee - finalTip;
|
|
1474
|
-
tx.tx = client.tx.vaults.create(vaultParams);
|
|
1475
|
-
txFee = await tx.feeEstimate(finalTip);
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
const canAfford = await tx.canAfford({ tip, unavailableBalance: BigInt(securitization) });
|
|
1479
|
-
if (!canAfford.canAfford) {
|
|
1480
|
-
throw new Error(
|
|
1481
|
-
`Insufficient balance to create vault. Required: ${formatArgons(securitization)}, Available: ${formatArgons(canAfford.availableBalance)}`
|
|
1482
|
-
);
|
|
1483
|
-
}
|
|
1484
|
-
const result = await tx.submit({
|
|
1485
|
-
tip,
|
|
1486
|
-
useLatestNonce: true,
|
|
1487
|
-
waitForBlock: true,
|
|
1488
|
-
txProgressCallback
|
|
1489
|
-
});
|
|
1490
|
-
await result.inBlockPromise;
|
|
1491
|
-
let vaultId;
|
|
1492
|
-
for (const event of result.events) {
|
|
1493
|
-
if (client.events.vaults.VaultCreated.is(event)) {
|
|
1494
|
-
vaultId = event.data.vaultId.toNumber();
|
|
1495
|
-
break;
|
|
1496
|
-
}
|
|
1497
|
-
}
|
|
1498
|
-
if (vaultId === void 0) {
|
|
1499
|
-
throw new Error("Vault creation failed, no VaultCreated event found");
|
|
1500
|
-
}
|
|
1501
|
-
const rawVault = await client.query.vaults.vaultsById(vaultId);
|
|
1502
|
-
if (rawVault.isNone) {
|
|
1503
|
-
throw new Error("Vault creation failed, vault not found");
|
|
1504
|
-
}
|
|
1505
|
-
const tickDuration = config2.tickDurationMillis ?? await client.query.ticks.genesisTicker().then((x) => x.tickDurationMillis.toNumber());
|
|
1506
|
-
const vault = new _Vault(vaultId, rawVault.unwrap(), tickDuration);
|
|
1507
|
-
return { vault, txResult: result };
|
|
1508
|
-
}
|
|
1509
|
-
};
|
|
1510
|
-
|
|
1511
|
-
// src/VaultMonitor.ts
|
|
1512
|
-
import { printTable as printTable2 } from "console-table-printer";
|
|
1513
|
-
var VaultMonitor = class {
|
|
1514
|
-
constructor(accountset, alerts = {}, options = {}) {
|
|
1515
|
-
this.accountset = accountset;
|
|
1516
|
-
this.alerts = alerts;
|
|
1517
|
-
this.options = options;
|
|
1518
|
-
this.mainchain = accountset.client;
|
|
1519
|
-
if (options.vaultOnlyWatchMode !== void 0) {
|
|
1520
|
-
this.vaultOnlyWatchMode = options.vaultOnlyWatchMode;
|
|
1521
|
-
}
|
|
1522
|
-
if (options.shouldLog !== void 0) {
|
|
1523
|
-
this.shouldLog = options.shouldLog;
|
|
1524
|
-
}
|
|
1525
|
-
this.miningBids = new MiningBids(this.mainchain, this.shouldLog);
|
|
1526
|
-
this.blockWatch = new BlockWatch(this.mainchain, {
|
|
1527
|
-
shouldLog: this.shouldLog
|
|
1528
|
-
});
|
|
1529
|
-
this.blockWatch.events.on(
|
|
1530
|
-
"vaults-updated",
|
|
1531
|
-
(header, vaultIds) => this.onVaultsUpdated(header.hash, vaultIds)
|
|
1532
|
-
);
|
|
1533
|
-
this.blockWatch.events.on("mining-bid", async (header, _bid) => {
|
|
1534
|
-
await this.miningBids.loadAt(this.accountset.namedAccounts, header.hash);
|
|
1535
|
-
this.printBids(header.hash);
|
|
1536
|
-
});
|
|
1537
|
-
this.blockWatch.events.on("mining-bid-ousted", async (header) => {
|
|
1538
|
-
await this.miningBids.loadAt(this.accountset.namedAccounts, header.hash);
|
|
1539
|
-
this.printBids(header.hash);
|
|
1540
|
-
});
|
|
1541
|
-
}
|
|
1542
|
-
events = createNanoEvents();
|
|
1543
|
-
vaultsById = {};
|
|
1544
|
-
blockWatch;
|
|
1545
|
-
mainchain;
|
|
1546
|
-
activatedCapitalByVault = {};
|
|
1547
|
-
lastPrintedBids;
|
|
1548
|
-
miningBids;
|
|
1549
|
-
tickDuration = 0;
|
|
1550
|
-
vaultOnlyWatchMode = false;
|
|
1551
|
-
shouldLog = true;
|
|
1552
|
-
stop() {
|
|
1553
|
-
this.blockWatch.stop();
|
|
1554
|
-
}
|
|
1555
|
-
async monitor(justPrint = false) {
|
|
1556
|
-
const client = await this.mainchain;
|
|
1557
|
-
this.tickDuration = (await client.query.ticks.genesisTicker()).tickDurationMillis.toNumber();
|
|
1558
|
-
const blockHeader = await client.rpc.chain.getHeader();
|
|
1559
|
-
const blockHash = new Uint8Array(blockHeader.hash);
|
|
1560
|
-
console.log(
|
|
1561
|
-
`${justPrint ? "Run" : "Started"} at block ${blockHeader.number} - ${blockHeader.hash.toHuman()}`
|
|
1562
|
-
);
|
|
1563
|
-
await this.miningBids.loadAt(this.accountset.namedAccounts, blockHash);
|
|
1564
|
-
const vaults = await client.query.vaults.vaultsById.entries();
|
|
1565
|
-
for (const [storageKey, rawVault] of vaults) {
|
|
1566
|
-
const vaultId = storageKey.args[0].toNumber();
|
|
1567
|
-
this.updateVault(vaultId, rawVault);
|
|
1568
|
-
}
|
|
1569
|
-
await client.query.liquidityPools.capitalRaising((x) => {
|
|
1570
|
-
this.activatedCapitalByVault = {};
|
|
1571
|
-
for (const entry of x) {
|
|
1572
|
-
const vaultId = entry.vaultId.toNumber();
|
|
1573
|
-
this.activatedCapitalByVault[vaultId] = entry.activatedCapital.toBigInt();
|
|
1574
|
-
}
|
|
1575
|
-
for (const [vaultId, vault] of Object.entries(this.vaultsById)) {
|
|
1576
|
-
const id = Number(vaultId);
|
|
1577
|
-
this.activatedCapitalByVault[id] ??= 0n;
|
|
1578
|
-
this.checkMiningBondAlerts(id, vault);
|
|
1579
|
-
}
|
|
1580
|
-
});
|
|
1581
|
-
this.printVaults();
|
|
1582
|
-
if (!this.vaultOnlyWatchMode && this.shouldLog) {
|
|
1583
|
-
this.miningBids.print();
|
|
1584
|
-
}
|
|
1585
|
-
if (!justPrint) await this.blockWatch.start();
|
|
1586
|
-
}
|
|
1587
|
-
printVaults() {
|
|
1588
|
-
if (!this.shouldLog) return;
|
|
1589
|
-
const vaults = [];
|
|
1590
|
-
for (const [vaultId, vault] of Object.entries(this.vaultsById)) {
|
|
1591
|
-
vaults.push({
|
|
1592
|
-
id: vaultId,
|
|
1593
|
-
btcSpace: `${formatArgons(vault.availableBitcoinSpace())} (${formatArgons(vault.argonsPendingActivation)} pending)`,
|
|
1594
|
-
btcDeal: `${formatArgons(vault.terms.bitcoinBaseFee)} + ${formatPercent(vault.terms.bitcoinAnnualPercentRate)}`,
|
|
1595
|
-
securitization: `${formatArgons(vault.securitization)} at ${vault.securitizationRatio}x`,
|
|
1596
|
-
securActivated: `${formatArgons(vault.activatedSecuritizationPerSlot())}/slot`,
|
|
1597
|
-
liquidPoolDeal: `${formatPercent(vault.terms.liquidityPoolProfitSharing)} sharing`,
|
|
1598
|
-
operator: `${this.accountset.namedAccounts.has(vault.operatorAccountId) ? ` (${this.accountset.namedAccounts.get(vault.operatorAccountId)})` : vault.operatorAccountId}`,
|
|
1599
|
-
state: vault.isClosed ? "closed" : vault.openedDate < /* @__PURE__ */ new Date() ? "open" : "pending"
|
|
1600
|
-
});
|
|
1601
|
-
}
|
|
1602
|
-
if (vaults.length) {
|
|
1603
|
-
if (this.vaultOnlyWatchMode) {
|
|
1604
|
-
console.clear();
|
|
1605
|
-
}
|
|
1606
|
-
console.log("\n\nVaults:");
|
|
1607
|
-
printTable2(vaults);
|
|
1608
|
-
}
|
|
1609
|
-
}
|
|
1610
|
-
async recheckAfterActive(vaultId) {
|
|
1611
|
-
const activationDate = this.vaultsById[vaultId].openedDate;
|
|
1612
|
-
if (this.shouldLog) {
|
|
1613
|
-
console.log(`Waiting for vault ${vaultId} to activate ${activationDate}`);
|
|
1614
|
-
}
|
|
1615
|
-
await new Promise((resolve) => setTimeout(resolve, activationDate.getTime() - Date.now()));
|
|
1616
|
-
const client = await this.mainchain;
|
|
1617
|
-
let isReady = false;
|
|
1618
|
-
while (!isReady) {
|
|
1619
|
-
const rawVault = await client.query.vaults.vaultsById(vaultId);
|
|
1620
|
-
if (!rawVault.isSome) return;
|
|
1621
|
-
const vault = new Vault(vaultId, rawVault.value, this.tickDuration);
|
|
1622
|
-
this.vaultsById[vaultId] = vault;
|
|
1623
|
-
if (vault.isClosed) return;
|
|
1624
|
-
if (vault.openedDate < /* @__PURE__ */ new Date()) {
|
|
1625
|
-
isReady = true;
|
|
1626
|
-
break;
|
|
1627
|
-
}
|
|
1628
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1629
|
-
}
|
|
1630
|
-
this.checkAlerts(vaultId, this.vaultsById[vaultId]);
|
|
1631
|
-
}
|
|
1632
|
-
async onVaultsUpdated(blockHash, vaultIds) {
|
|
1633
|
-
await this.reloadVaultsAt([...vaultIds], blockHash).catch((err) => {
|
|
1634
|
-
console.error(`Failed to reload vault ${[...vaultIds]} at block ${blockHash}:`, err);
|
|
1635
|
-
});
|
|
1636
|
-
this.printVaults();
|
|
1637
|
-
}
|
|
1638
|
-
async reloadVaultsAt(vaultIds, blockHash) {
|
|
1639
|
-
const client = await this.mainchain;
|
|
1640
|
-
const api = await client.at(blockHash);
|
|
1641
|
-
const vaults = await api.query.vaults.vaultsById.multi(vaultIds);
|
|
1642
|
-
for (let i = 0; i < vaultIds.length; i += 1) {
|
|
1643
|
-
this.updateVault(vaultIds[i], vaults[i]);
|
|
1644
|
-
}
|
|
1645
|
-
}
|
|
1646
|
-
updateVault(vaultId, rawVault) {
|
|
1647
|
-
if (rawVault.isNone) return;
|
|
1648
|
-
const vault = new Vault(vaultId, rawVault.value, this.tickDuration);
|
|
1649
|
-
this.vaultsById[vaultId] = vault;
|
|
1650
|
-
if (vault.openedDate > /* @__PURE__ */ new Date()) {
|
|
1651
|
-
void this.recheckAfterActive(vaultId);
|
|
1652
|
-
} else {
|
|
1653
|
-
this.checkAlerts(vaultId, vault);
|
|
1654
|
-
}
|
|
1655
|
-
}
|
|
1656
|
-
checkAlerts(vaultId, vault) {
|
|
1657
|
-
if (this.alerts.bitcoinSpaceAvailable !== void 0) {
|
|
1658
|
-
const availableBitcoinSpace = vault.availableBitcoinSpace();
|
|
1659
|
-
if (availableBitcoinSpace >= this.alerts.bitcoinSpaceAvailable) {
|
|
1660
|
-
console.warn(
|
|
1661
|
-
`Vault ${vaultId} has available bitcoins above ${formatArgons(this.alerts.bitcoinSpaceAvailable)}`
|
|
1662
|
-
);
|
|
1663
|
-
this.events.emit("bitcoin-space-above", vaultId, availableBitcoinSpace);
|
|
1664
|
-
}
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
checkMiningBondAlerts(vaultId, vault) {
|
|
1668
|
-
if (this.alerts.liquidityPoolSpaceAvailable === void 0) return;
|
|
1669
|
-
const activatedSecuritization = vault.activatedSecuritizationPerSlot();
|
|
1670
|
-
const capitalization = this.activatedCapitalByVault[vaultId] ?? 0n;
|
|
1671
|
-
const available = activatedSecuritization - capitalization;
|
|
1672
|
-
if (available >= this.alerts.liquidityPoolSpaceAvailable) {
|
|
1673
|
-
this.events.emit("liquidity-pool-space-above", vaultId, available);
|
|
1674
|
-
}
|
|
1675
|
-
}
|
|
1676
|
-
printBids(blockHash) {
|
|
1677
|
-
if (!this.shouldLog) return;
|
|
1678
|
-
if (this.lastPrintedBids === blockHash) return;
|
|
1679
|
-
this.miningBids.print();
|
|
1680
|
-
this.lastPrintedBids = blockHash;
|
|
1681
|
-
}
|
|
1682
|
-
};
|
|
1683
|
-
|
|
1684
|
-
// src/CohortBidder.ts
|
|
1685
|
-
var CohortBidder = class {
|
|
1686
|
-
constructor(accountset, cohortStartingFrameId, subaccounts, options, callbacks) {
|
|
1687
|
-
this.accountset = accountset;
|
|
1688
|
-
this.cohortStartingFrameId = cohortStartingFrameId;
|
|
1689
|
-
this.subaccounts = subaccounts;
|
|
1690
|
-
this.options = options;
|
|
1691
|
-
this.callbacks = callbacks;
|
|
1692
|
-
this.subaccounts.forEach((x) => {
|
|
1693
|
-
this.myAddresses.add(x.address);
|
|
1694
|
-
});
|
|
1695
|
-
}
|
|
1696
|
-
get clientPromise() {
|
|
1697
|
-
return this.accountset.client;
|
|
1698
|
-
}
|
|
1699
|
-
txFees = 0n;
|
|
1700
|
-
bidsAttempted = 0;
|
|
1701
|
-
winningBids = [];
|
|
1702
|
-
myAddresses = /* @__PURE__ */ new Set();
|
|
1703
|
-
currentBids = {
|
|
1704
|
-
bids: [],
|
|
1705
|
-
mostRecentBidTick: 0,
|
|
1706
|
-
atTick: 0,
|
|
1707
|
-
atBlockNumber: 0
|
|
1708
|
-
};
|
|
1709
|
-
unsubscribe;
|
|
1710
|
-
pendingRequest;
|
|
1711
|
-
isStopped = false;
|
|
1712
|
-
millisPerTick;
|
|
1713
|
-
minIncrement = 10000n;
|
|
1714
|
-
nextCohortSize;
|
|
1715
|
-
lastBidTick = 0;
|
|
1716
|
-
evaluateInterval;
|
|
1717
|
-
async start() {
|
|
1718
|
-
console.log(`Starting cohort ${this.cohortStartingFrameId} bidder`, {
|
|
1719
|
-
maxBid: formatArgons(this.options.maxBid),
|
|
1720
|
-
minBid: formatArgons(this.options.minBid),
|
|
1721
|
-
bidIncrement: formatArgons(this.options.bidIncrement),
|
|
1722
|
-
maxBudget: formatArgons(this.options.maxBudget),
|
|
1723
|
-
bidDelay: this.options.bidDelay,
|
|
1724
|
-
subaccounts: this.subaccounts
|
|
1725
|
-
});
|
|
1726
|
-
const client = await this.clientPromise;
|
|
1727
|
-
this.minIncrement = client.consts.miningSlot.bidIncrements.toBigInt();
|
|
1728
|
-
this.nextCohortSize = await client.query.miningSlot.nextCohortSize().then((x) => x.toNumber());
|
|
1729
|
-
if (this.subaccounts.length > this.nextCohortSize) {
|
|
1730
|
-
console.info(
|
|
1731
|
-
`Cohort size ${this.nextCohortSize} is less than provided subaccounts ${this.subaccounts.length}.`
|
|
1732
|
-
);
|
|
1733
|
-
this.subaccounts.length = this.nextCohortSize;
|
|
1734
|
-
}
|
|
1735
|
-
this.millisPerTick = await client.query.ticks.genesisTicker().then((x) => x.tickDurationMillis.toNumber());
|
|
1736
|
-
let didStart = false;
|
|
1737
|
-
this.unsubscribe = await client.queryMulti(
|
|
1738
|
-
[
|
|
1739
|
-
client.query.miningSlot.bidsForNextSlotCohort,
|
|
1740
|
-
client.query.miningSlot.nextFrameId,
|
|
1741
|
-
client.query.ticks.currentTick,
|
|
1742
|
-
client.query.system.number
|
|
1743
|
-
],
|
|
1744
|
-
async ([rawBids, nextFrameId, currentTick, blockNumber]) => {
|
|
1745
|
-
if (nextFrameId.toNumber() === this.cohortStartingFrameId) {
|
|
1746
|
-
this.updateBidList(rawBids, blockNumber.toNumber(), currentTick.toNumber());
|
|
1747
|
-
if (!didStart) {
|
|
1748
|
-
didStart = true;
|
|
1749
|
-
this.scheduleEvaluation();
|
|
1750
|
-
void this.checkWinningBids();
|
|
1751
|
-
}
|
|
1752
|
-
}
|
|
1753
|
-
}
|
|
1754
|
-
);
|
|
1755
|
-
}
|
|
1756
|
-
async stop() {
|
|
1757
|
-
if (this.isStopped) return this.winningBids;
|
|
1758
|
-
this.isStopped = true;
|
|
1759
|
-
clearInterval(this.evaluateInterval);
|
|
1760
|
-
console.log("Stopping bidder for cohort", this.cohortStartingFrameId);
|
|
1761
|
-
if (this.unsubscribe) {
|
|
1762
|
-
this.unsubscribe();
|
|
1763
|
-
}
|
|
1764
|
-
const client = await this.clientPromise;
|
|
1765
|
-
const [nextFrameId, isBiddingOpen] = await client.queryMulti([
|
|
1766
|
-
client.query.miningSlot.nextFrameId,
|
|
1767
|
-
client.query.miningSlot.isNextSlotBiddingOpen
|
|
1768
|
-
]);
|
|
1769
|
-
if (nextFrameId.toNumber() === this.cohortStartingFrameId && isBiddingOpen.isTrue) {
|
|
1770
|
-
console.log("Bidding is still open, waiting for it to close");
|
|
1771
|
-
await new Promise(async (resolve) => {
|
|
1772
|
-
const unsub = await client.query.miningSlot.isNextSlotBiddingOpen((isOpen) => {
|
|
1773
|
-
if (isOpen.isFalse) {
|
|
1774
|
-
unsub();
|
|
1775
|
-
resolve();
|
|
1776
|
-
}
|
|
1777
|
-
});
|
|
1778
|
-
});
|
|
1779
|
-
}
|
|
1780
|
-
void await this.pendingRequest;
|
|
1781
|
-
const currentFrameId = await client.query.miningSlot.nextFrameId();
|
|
1782
|
-
let blockNumber;
|
|
1783
|
-
if (currentFrameId.toNumber() > this.cohortStartingFrameId) {
|
|
1784
|
-
blockNumber = await client.query.miningSlot.frameStartBlockNumbers().then((x) => x[0]?.toNumber()) - 1;
|
|
1785
|
-
} else {
|
|
1786
|
-
blockNumber = await client.query.system.number().then((x) => x.toNumber());
|
|
1787
|
-
}
|
|
1788
|
-
const blockHash = await client.rpc.chain.getBlockHash(blockNumber);
|
|
1789
|
-
const api = await client.at(blockHash);
|
|
1790
|
-
const rawBids = await api.query.miningSlot.bidsForNextSlotCohort();
|
|
1791
|
-
const currentTick = await api.query.ticks.currentTick().then((x) => x.toNumber());
|
|
1792
|
-
this.updateBidList(rawBids, blockNumber, currentTick);
|
|
1793
|
-
console.log("Bidder stopped", {
|
|
1794
|
-
cohortStartingFrameId: this.cohortStartingFrameId,
|
|
1795
|
-
blockNumber,
|
|
1796
|
-
winningBids: this.winningBids
|
|
1797
|
-
});
|
|
1798
|
-
return this.winningBids;
|
|
1799
|
-
}
|
|
1800
|
-
async checkWinningBids() {
|
|
1801
|
-
if (this.isStopped) return;
|
|
1802
|
-
if (this.pendingRequest) {
|
|
1803
|
-
console.log("Current bid is still in progress, skipping this check");
|
|
1804
|
-
return;
|
|
1805
|
-
}
|
|
1806
|
-
if (this.currentBids.mostRecentBidTick < this.lastBidTick) {
|
|
1807
|
-
console.log(`Waiting for bids more recent than our last attempt.`, {
|
|
1808
|
-
ownAttemptedBidTick: this.lastBidTick,
|
|
1809
|
-
liveBidsTick: this.currentBids.mostRecentBidTick
|
|
1810
|
-
});
|
|
1811
|
-
return;
|
|
1812
|
-
}
|
|
1813
|
-
const bids = [...this.currentBids.bids];
|
|
1814
|
-
const bidsAtTick = this.currentBids.atTick;
|
|
1815
|
-
const blockNumber = this.currentBids.atBlockNumber;
|
|
1816
|
-
const winningBids = bids.filter((x) => this.myAddresses.has(x.address));
|
|
1817
|
-
if (winningBids.length >= this.subaccounts.length) {
|
|
1818
|
-
console.log(`No updates needed. Winning all remaining seats (${winningBids.length}).`);
|
|
1819
|
-
return;
|
|
1820
|
-
}
|
|
1821
|
-
console.log(
|
|
1822
|
-
`Checking bids for cohort ${this.cohortStartingFrameId}, Still trying for seats: ${this.subaccounts.length}`
|
|
1823
|
-
);
|
|
1824
|
-
const winningAddresses = new Set(winningBids.map((x) => x.address));
|
|
1825
|
-
let lowestBid;
|
|
1826
|
-
let myAllocatedBids = 0n;
|
|
1827
|
-
for (const bid of bids) {
|
|
1828
|
-
lowestBid ??= bid.bidMicrogons;
|
|
1829
|
-
if (this.myAddresses.has(bid.address)) {
|
|
1830
|
-
myAllocatedBids += bid.bidMicrogons;
|
|
1831
|
-
} else {
|
|
1832
|
-
if (bid.bidMicrogons < lowestBid) {
|
|
1833
|
-
lowestBid = bid.bidMicrogons;
|
|
1834
|
-
}
|
|
1835
|
-
}
|
|
1836
|
-
}
|
|
1837
|
-
lowestBid ??= -this.options.bidIncrement;
|
|
1838
|
-
let nextBid = lowestBid + this.options.bidIncrement;
|
|
1839
|
-
if (nextBid < this.options.minBid) {
|
|
1840
|
-
nextBid = this.options.minBid;
|
|
1841
|
-
}
|
|
1842
|
-
if (nextBid > this.options.maxBid) {
|
|
1843
|
-
nextBid = this.options.maxBid;
|
|
1844
|
-
}
|
|
1845
|
-
const fakeTx = await this.accountset.createMiningBidTx({
|
|
1846
|
-
subaccounts: this.subaccounts,
|
|
1847
|
-
bidAmount: nextBid
|
|
1848
|
-
});
|
|
1849
|
-
let availableBalanceForBids = await this.accountset.submitterBalance();
|
|
1850
|
-
availableBalanceForBids += myAllocatedBids;
|
|
1851
|
-
const tip = this.options.tipPerTransaction ?? 0n;
|
|
1852
|
-
const feeEstimate = await fakeTx.feeEstimate(tip);
|
|
1853
|
-
const estimatedFeePlusTip = feeEstimate + tip;
|
|
1854
|
-
let budgetForSeats = this.options.maxBudget - estimatedFeePlusTip;
|
|
1855
|
-
if (budgetForSeats > availableBalanceForBids) {
|
|
1856
|
-
budgetForSeats = availableBalanceForBids - estimatedFeePlusTip;
|
|
1857
|
-
}
|
|
1858
|
-
if (nextBid < lowestBid) {
|
|
1859
|
-
console.log(
|
|
1860
|
-
`Next bid within parameters is ${formatArgons(nextBid)}, but it's not enough. Current lowest bid is ${formatArgons(lowestBid)}.`
|
|
1861
|
-
);
|
|
1862
|
-
this.safeRecordParamsAdjusted({
|
|
1863
|
-
tick: bidsAtTick,
|
|
1864
|
-
blockNumber,
|
|
1865
|
-
maxSeats: 0,
|
|
1866
|
-
winningBidCount: winningBids.length,
|
|
1867
|
-
reason: "max-bid-too-low",
|
|
1868
|
-
availableBalanceForBids
|
|
1869
|
-
});
|
|
1870
|
-
return;
|
|
1871
|
-
}
|
|
1872
|
-
if (nextBid - lowestBid < Number(this.minIncrement)) {
|
|
1873
|
-
console.log(
|
|
1874
|
-
`Can't make any more bids for ${this.cohortStartingFrameId} with given constraints (next bid below min increment).`,
|
|
1875
|
-
{
|
|
1876
|
-
lowestCurrentBid: formatArgons(lowestBid),
|
|
1877
|
-
nextAttemptedBid: formatArgons(nextBid),
|
|
1878
|
-
maxBid: formatArgons(this.options.maxBid)
|
|
1879
|
-
}
|
|
1880
|
-
);
|
|
1881
|
-
this.safeRecordParamsAdjusted({
|
|
1882
|
-
tick: bidsAtTick,
|
|
1883
|
-
blockNumber,
|
|
1884
|
-
maxSeats: 0,
|
|
1885
|
-
winningBidCount: winningBids.length,
|
|
1886
|
-
reason: "max-bid-too-low",
|
|
1887
|
-
availableBalanceForBids
|
|
1888
|
-
});
|
|
1889
|
-
return;
|
|
1890
|
-
}
|
|
1891
|
-
const seatsInBudget = nextBid === 0n ? this.subaccounts.length : Number(budgetForSeats / nextBid);
|
|
1892
|
-
let accountsToUse = [...this.subaccounts];
|
|
1893
|
-
if (accountsToUse.length > seatsInBudget) {
|
|
1894
|
-
this.safeRecordParamsAdjusted({
|
|
1895
|
-
tick: bidsAtTick,
|
|
1896
|
-
blockNumber,
|
|
1897
|
-
maxSeats: this.subaccounts.length,
|
|
1898
|
-
winningBidCount: winningBids.length,
|
|
1899
|
-
reason: availableBalanceForBids - estimatedFeePlusTip < nextBid * BigInt(seatsInBudget) ? "insufficient-balance" : "max-budget-too-low",
|
|
1900
|
-
availableBalanceForBids
|
|
1901
|
-
});
|
|
1902
|
-
accountsToUse.sort((a, b) => {
|
|
1903
|
-
const isWinningA = winningAddresses.has(a.address);
|
|
1904
|
-
const isWinningB = winningAddresses.has(b.address);
|
|
1905
|
-
if (isWinningA && !isWinningB) return -1;
|
|
1906
|
-
if (!isWinningA && isWinningB) return 1;
|
|
1907
|
-
if (a.isRebid && !b.isRebid) return -1;
|
|
1908
|
-
if (!a.isRebid && b.isRebid) return 1;
|
|
1909
|
-
return a.index - b.index;
|
|
1910
|
-
});
|
|
1911
|
-
accountsToUse.length = seatsInBudget;
|
|
1912
|
-
}
|
|
1913
|
-
if (accountsToUse.length > winningBids.length) {
|
|
1914
|
-
this.pendingRequest = this.submitBids(nextBid, accountsToUse);
|
|
1915
|
-
}
|
|
1916
|
-
}
|
|
1917
|
-
async submitBids(microgonsPerSeat, subaccounts) {
|
|
1918
|
-
try {
|
|
1919
|
-
this.bidsAttempted += subaccounts.length;
|
|
1920
|
-
const submitter = await this.accountset.createMiningBidTx({
|
|
1921
|
-
subaccounts,
|
|
1922
|
-
bidAmount: microgonsPerSeat
|
|
1923
|
-
});
|
|
1924
|
-
const tip = this.options.tipPerTransaction ?? 0n;
|
|
1925
|
-
const txResult = await submitter.submit({
|
|
1926
|
-
tip,
|
|
1927
|
-
useLatestNonce: true
|
|
1928
|
-
});
|
|
1929
|
-
const bidError = await txResult.inBlockPromise.then(() => void 0).catch((x) => x);
|
|
1930
|
-
const client = await this.clientPromise;
|
|
1931
|
-
let api = txResult.includedInBlock ? await client.at(txResult.includedInBlock) : client;
|
|
1932
|
-
this.lastBidTick = await api.query.ticks.currentTick().then((x) => x.toNumber());
|
|
1933
|
-
const blockNumber = await api.query.system.number().then((x) => x.toNumber());
|
|
1934
|
-
const bidAtTick = this.lastBidTick;
|
|
1935
|
-
try {
|
|
1936
|
-
this.callbacks?.onBidsSubmitted?.({
|
|
1937
|
-
tick: bidAtTick,
|
|
1938
|
-
blockNumber,
|
|
1939
|
-
microgonsPerSeat,
|
|
1940
|
-
txFeePlusTip: txResult.finalFee ?? 0n,
|
|
1941
|
-
submittedCount: subaccounts.length
|
|
1942
|
-
});
|
|
1943
|
-
} catch (error) {
|
|
1944
|
-
console.error("Error in onBidsSubmitted callback:", error);
|
|
1945
|
-
}
|
|
1946
|
-
const successfulBids = txResult.batchInterruptedIndex ?? subaccounts.length;
|
|
1947
|
-
this.txFees += txResult.finalFee ?? 0n;
|
|
1948
|
-
console.log("Result of bids for cohort", {
|
|
1949
|
-
successfulBids,
|
|
1950
|
-
bidsPlaced: subaccounts.length,
|
|
1951
|
-
bidPerSeat: formatArgons(microgonsPerSeat),
|
|
1952
|
-
bidAtTick
|
|
1953
|
-
});
|
|
1954
|
-
if (bidError) {
|
|
1955
|
-
try {
|
|
1956
|
-
this.callbacks?.onBidsRejected?.({
|
|
1957
|
-
tick: bidAtTick,
|
|
1958
|
-
blockNumber,
|
|
1959
|
-
microgonsPerSeat,
|
|
1960
|
-
submittedCount: subaccounts.length,
|
|
1961
|
-
rejectedCount: subaccounts.length - successfulBids,
|
|
1962
|
-
bidError
|
|
1963
|
-
});
|
|
1964
|
-
} catch (error) {
|
|
1965
|
-
console.error("Error in onBidsRejected callback:", error);
|
|
1966
|
-
}
|
|
1967
|
-
throw bidError;
|
|
1968
|
-
}
|
|
1969
|
-
} catch (err) {
|
|
1970
|
-
console.error(`Error bidding for cohort ${this.cohortStartingFrameId}:`, err);
|
|
1971
|
-
} finally {
|
|
1972
|
-
this.pendingRequest = void 0;
|
|
1973
|
-
this.scheduleEvaluation();
|
|
1974
|
-
}
|
|
1975
|
-
}
|
|
1976
|
-
scheduleEvaluation() {
|
|
1977
|
-
if (this.isStopped) return;
|
|
1978
|
-
const millisPerTick = this.millisPerTick;
|
|
1979
|
-
const delayTicks = Math.max(this.options.bidDelay, 1);
|
|
1980
|
-
const delay = delayTicks * millisPerTick;
|
|
1981
|
-
if (this.evaluateInterval) clearInterval(this.evaluateInterval);
|
|
1982
|
-
console.log(`Scheduling next evaluation in ${delay}ms`);
|
|
1983
|
-
this.evaluateInterval = setInterval(() => this.checkWinningBids().catch(console.error), delay);
|
|
1984
|
-
}
|
|
1985
|
-
updateBidList(rawBids, blockNumber, tick) {
|
|
1986
|
-
try {
|
|
1987
|
-
let mostRecentBidTick = 0;
|
|
1988
|
-
let hasDiffs = this.currentBids.bids.length !== rawBids.length;
|
|
1989
|
-
const bids = [];
|
|
1990
|
-
for (let i = 0; i < rawBids.length; i += 1) {
|
|
1991
|
-
const rawBid = rawBids[i];
|
|
1992
|
-
const bidAtTick = rawBid.bidAtTick.toNumber();
|
|
1993
|
-
if (bidAtTick > mostRecentBidTick) {
|
|
1994
|
-
mostRecentBidTick = bidAtTick;
|
|
1995
|
-
}
|
|
1996
|
-
const address = rawBid.accountId.toHuman();
|
|
1997
|
-
const bidMicrogons = rawBid.bid.toBigInt();
|
|
1998
|
-
if (!hasDiffs) {
|
|
1999
|
-
const existing = this.currentBids.bids[i];
|
|
2000
|
-
hasDiffs = existing?.address !== address || existing?.bidMicrogons !== bidMicrogons;
|
|
2001
|
-
}
|
|
2002
|
-
bids.push({
|
|
2003
|
-
address,
|
|
2004
|
-
bidMicrogons,
|
|
2005
|
-
bidAtTick
|
|
2006
|
-
});
|
|
2007
|
-
}
|
|
2008
|
-
if (blockNumber > this.currentBids.atBlockNumber && hasDiffs) {
|
|
2009
|
-
this.currentBids.bids = bids;
|
|
2010
|
-
this.currentBids.mostRecentBidTick = mostRecentBidTick;
|
|
2011
|
-
this.currentBids.atTick = tick;
|
|
2012
|
-
this.currentBids.atBlockNumber = blockNumber;
|
|
2013
|
-
this.winningBids = bids.filter((x) => this.myAddresses.has(x.address));
|
|
2014
|
-
console.log("Now winning bids:", this.winningBids.length);
|
|
2015
|
-
if (this.callbacks?.onBidsUpdated) {
|
|
2016
|
-
this.callbacks.onBidsUpdated({
|
|
2017
|
-
bids: this.winningBids,
|
|
2018
|
-
atBlockNumber: blockNumber,
|
|
2019
|
-
tick: mostRecentBidTick
|
|
2020
|
-
});
|
|
2021
|
-
}
|
|
2022
|
-
}
|
|
2023
|
-
} catch (err) {
|
|
2024
|
-
console.error("Error processing updated bids list:", err);
|
|
2025
|
-
}
|
|
2026
|
-
}
|
|
2027
|
-
safeRecordParamsAdjusted(args) {
|
|
2028
|
-
try {
|
|
2029
|
-
this.callbacks?.onBidParamsAdjusted?.(args);
|
|
2030
|
-
} catch (err) {
|
|
2031
|
-
console.error("Error in onBidParamsAdjusted callback:", err);
|
|
2032
|
-
}
|
|
2033
|
-
}
|
|
2034
|
-
};
|
|
2035
|
-
|
|
2036
|
-
// src/BidPool.ts
|
|
2037
|
-
import { Table } from "console-table-printer";
|
|
2038
|
-
var EMPTY_TABLE = {
|
|
2039
|
-
headerBottom: { left: " ", mid: " ", other: "\u2500", right: " " },
|
|
2040
|
-
headerTop: { left: " ", mid: " ", other: " ", right: " " },
|
|
2041
|
-
rowSeparator: { left: " ", mid: " ", other: " ", right: " " },
|
|
2042
|
-
tableBottom: { left: " ", mid: " ", other: " ", right: " " },
|
|
2043
|
-
vertical: " "
|
|
2044
|
-
};
|
|
2045
|
-
var BidPool = class {
|
|
2046
|
-
constructor(client, keypair, accountRegistry = AccountRegistry.factory()) {
|
|
2047
|
-
this.client = client;
|
|
2048
|
-
this.keypair = keypair;
|
|
2049
|
-
this.accountRegistry = accountRegistry;
|
|
2050
|
-
this.blockWatch = new BlockWatch(client, { shouldLog: false });
|
|
2051
|
-
}
|
|
2052
|
-
bidPoolAmount = 0n;
|
|
2053
|
-
nextFrameId = 1;
|
|
2054
|
-
poolVaultCapitalByFrame = {};
|
|
2055
|
-
vaultSecuritization = [];
|
|
2056
|
-
printTimeout;
|
|
2057
|
-
blockWatch;
|
|
2058
|
-
vaultsById = {};
|
|
2059
|
-
tickDuration;
|
|
2060
|
-
lastDistributedFrameId;
|
|
2061
|
-
FrameSubscriptions = {};
|
|
2062
|
-
async onVaultsUpdated(blockHash, vaultIdSet) {
|
|
2063
|
-
const client = await this.client;
|
|
2064
|
-
this.tickDuration ??= (await client.query.ticks.genesisTicker()).tickDurationMillis.toNumber();
|
|
2065
|
-
const api = await client.at(blockHash);
|
|
2066
|
-
const vaultIds = [...vaultIdSet];
|
|
2067
|
-
const rawVaults = await api.query.vaults.vaultsById.multi(vaultIds);
|
|
2068
|
-
for (let i = 0; i < vaultIds.length; i += 1) {
|
|
2069
|
-
const rawVault = rawVaults[i];
|
|
2070
|
-
if (rawVault.isNone) continue;
|
|
2071
|
-
const vaultId = vaultIds[i];
|
|
2072
|
-
this.vaultsById[vaultId] = new Vault(vaultId, rawVault.unwrap(), this.tickDuration);
|
|
2073
|
-
}
|
|
2074
|
-
const vaults = Object.entries(this.vaultsById);
|
|
2075
|
-
const newSecuritization = [];
|
|
2076
|
-
for (const [vaultId, vault] of vaults) {
|
|
2077
|
-
const amount = vault.activatedSecuritizationPerSlot();
|
|
2078
|
-
newSecuritization.push({
|
|
2079
|
-
vaultId: Number(vaultId),
|
|
2080
|
-
bitcoinSpace: vault.availableBitcoinSpace(),
|
|
2081
|
-
activatedSecuritization: amount,
|
|
2082
|
-
vaultSharingPercent: vault.terms.liquidityPoolProfitSharing
|
|
2083
|
-
});
|
|
2084
|
-
}
|
|
2085
|
-
newSecuritization.sort((a, b) => {
|
|
2086
|
-
const diff2 = b.activatedSecuritization - a.activatedSecuritization;
|
|
2087
|
-
if (diff2 !== 0n) return Number(diff2);
|
|
2088
|
-
return a.vaultId - b.vaultId;
|
|
2089
|
-
});
|
|
2090
|
-
this.vaultSecuritization = newSecuritization;
|
|
2091
|
-
this.printDebounce();
|
|
2092
|
-
}
|
|
2093
|
-
async getBidPool() {
|
|
2094
|
-
const client = await this.client;
|
|
2095
|
-
const balanceBytes = await client.rpc.state.call("MiningSlotApi_bid_pool", "");
|
|
2096
|
-
const balance = client.createType("U128", balanceBytes);
|
|
2097
|
-
return balance.toBigInt();
|
|
2098
|
-
}
|
|
2099
|
-
async loadAt(blockHash) {
|
|
2100
|
-
const client = await this.client;
|
|
2101
|
-
blockHash ??= new Uint8Array((await client.rpc.chain.getHeader()).hash);
|
|
2102
|
-
const api = await client.at(blockHash);
|
|
2103
|
-
const rawVaultIds = await api.query.vaults.vaultsById.keys();
|
|
2104
|
-
const vaultIds = rawVaultIds.map((x) => x.args[0].toNumber());
|
|
2105
|
-
this.bidPoolAmount = await this.getBidPool();
|
|
2106
|
-
this.nextFrameId = (await api.query.miningSlot.nextFrameId()).toNumber();
|
|
2107
|
-
const contributors = await api.query.liquidityPools.vaultPoolsByFrame.entries();
|
|
2108
|
-
for (const [frameId, funds] of contributors) {
|
|
2109
|
-
const FrameIdNumber = frameId.args[0].toNumber();
|
|
2110
|
-
this.loadFrameData(FrameIdNumber, funds);
|
|
2111
|
-
}
|
|
2112
|
-
for (const entrant of await api.query.liquidityPools.capitalActive()) {
|
|
2113
|
-
this.setVaultFrameData(entrant.frameId.toNumber(), entrant.vaultId.toNumber(), {
|
|
2114
|
-
activatedCapital: entrant.activatedCapital.toBigInt()
|
|
2115
|
-
});
|
|
2116
|
-
}
|
|
2117
|
-
for (const entrant of await api.query.liquidityPools.capitalRaising()) {
|
|
2118
|
-
this.setVaultFrameData(entrant.frameId.toNumber(), entrant.vaultId.toNumber(), {
|
|
2119
|
-
activatedCapital: entrant.activatedCapital.toBigInt()
|
|
2120
|
-
});
|
|
2121
|
-
}
|
|
2122
|
-
await this.onVaultsUpdated(blockHash, new Set(vaultIds));
|
|
2123
|
-
this.print();
|
|
2124
|
-
}
|
|
2125
|
-
async watch() {
|
|
2126
|
-
await this.loadAt();
|
|
2127
|
-
await this.blockWatch.start();
|
|
2128
|
-
this.blockWatch.events.on("vaults-updated", (b, v) => this.onVaultsUpdated(b.hash, v));
|
|
2129
|
-
const api = await this.client;
|
|
2130
|
-
this.blockWatch.events.on("event", async (_, event) => {
|
|
2131
|
-
if (api.events.liquidityPools.BidPoolDistributed.is(event)) {
|
|
2132
|
-
const { frameId: rawFrameId } = event.data;
|
|
2133
|
-
this.lastDistributedFrameId = rawFrameId.toNumber();
|
|
2134
|
-
this.bidPoolAmount = await this.getBidPool();
|
|
2135
|
-
this.FrameSubscriptions[rawFrameId.toNumber()]?.();
|
|
2136
|
-
const entrant = await api.query.liquidityPools.vaultPoolsByFrame(rawFrameId);
|
|
2137
|
-
this.loadFrameData(rawFrameId.toNumber(), entrant);
|
|
2138
|
-
this.printDebounce();
|
|
2139
|
-
}
|
|
2140
|
-
if (api.events.liquidityPools.NextBidPoolCapitalLocked.is(event)) {
|
|
2141
|
-
const { frameId } = event.data;
|
|
2142
|
-
for (let inc = 0; inc < 2; inc++) {
|
|
2143
|
-
const id = frameId.toNumber() + inc;
|
|
2144
|
-
if (!this.FrameSubscriptions[id]) {
|
|
2145
|
-
this.FrameSubscriptions[id] = await api.query.liquidityPools.vaultPoolsByFrame(
|
|
2146
|
-
id,
|
|
2147
|
-
async (entrant) => {
|
|
2148
|
-
this.loadFrameData(id, entrant);
|
|
2149
|
-
this.printDebounce();
|
|
2150
|
-
}
|
|
2151
|
-
);
|
|
2152
|
-
}
|
|
2153
|
-
}
|
|
2154
|
-
}
|
|
2155
|
-
});
|
|
2156
|
-
const unsubscribe = await api.queryMulti(
|
|
2157
|
-
[
|
|
2158
|
-
api.query.miningSlot.bidsForNextSlotCohort,
|
|
2159
|
-
api.query.miningSlot.nextFrameId,
|
|
2160
|
-
api.query.liquidityPools.capitalActive,
|
|
2161
|
-
api.query.liquidityPools.capitalRaising
|
|
2162
|
-
],
|
|
2163
|
-
async ([_bids, nextFrameId, openVaultBidPoolCapital, nextPoolCapital]) => {
|
|
2164
|
-
this.bidPoolAmount = await this.getBidPool();
|
|
2165
|
-
this.nextFrameId = nextFrameId.toNumber();
|
|
2166
|
-
for (const entrant of [...openVaultBidPoolCapital, ...nextPoolCapital]) {
|
|
2167
|
-
this.setVaultFrameData(entrant.frameId.toNumber(), entrant.vaultId.toNumber(), {
|
|
2168
|
-
activatedCapital: entrant.activatedCapital.toBigInt()
|
|
2169
|
-
});
|
|
2170
|
-
}
|
|
2171
|
-
this.printDebounce();
|
|
2172
|
-
}
|
|
2173
|
-
);
|
|
2174
|
-
return { unsubscribe };
|
|
2175
|
-
}
|
|
2176
|
-
async bondArgons(vaultId, amount, options) {
|
|
2177
|
-
const client = await this.client;
|
|
2178
|
-
const tx = client.tx.liquidityPools.bondArgons(vaultId, amount);
|
|
2179
|
-
const txSubmitter = new TxSubmitter(client, tx, this.keypair);
|
|
2180
|
-
const affordability = await txSubmitter.canAfford({
|
|
2181
|
-
tip: options?.tip,
|
|
2182
|
-
unavailableBalance: amount
|
|
2183
|
-
});
|
|
2184
|
-
if (!affordability.canAfford) {
|
|
2185
|
-
console.warn("Insufficient balance to bond argons to liquidity pool", {
|
|
2186
|
-
...affordability,
|
|
2187
|
-
argonsNeeded: amount
|
|
2188
|
-
});
|
|
2189
|
-
throw new Error("Insufficient balance to bond argons to liquidity pool");
|
|
2190
|
-
}
|
|
2191
|
-
const result = await txSubmitter.submit({
|
|
2192
|
-
tip: options?.tip,
|
|
2193
|
-
useLatestNonce: true
|
|
2194
|
-
});
|
|
2195
|
-
await result.inBlockPromise;
|
|
2196
|
-
return result;
|
|
2197
|
-
}
|
|
2198
|
-
printDebounce() {
|
|
2199
|
-
if (this.printTimeout) {
|
|
2200
|
-
clearTimeout(this.printTimeout);
|
|
2201
|
-
}
|
|
2202
|
-
this.printTimeout = setTimeout(() => {
|
|
2203
|
-
this.print();
|
|
2204
|
-
}, 100);
|
|
2205
|
-
}
|
|
2206
|
-
getOperatorName(vaultId) {
|
|
2207
|
-
const vault = this.vaultsById[vaultId];
|
|
2208
|
-
return this.accountRegistry.getName(vault.operatorAccountId) ?? vault.operatorAccountId;
|
|
2209
|
-
}
|
|
2210
|
-
print() {
|
|
2211
|
-
console.clear();
|
|
2212
|
-
const lastDistributedFrameId = this.lastDistributedFrameId;
|
|
2213
|
-
const distributedFrame = this.poolVaultCapitalByFrame[this.lastDistributedFrameId ?? -1] ?? {};
|
|
2214
|
-
if (Object.keys(distributedFrame).length > 0) {
|
|
2215
|
-
console.log(`
|
|
2216
|
-
|
|
2217
|
-
Distributed (Frame ${lastDistributedFrameId})`);
|
|
2218
|
-
const rows = [];
|
|
2219
|
-
let maxWidth2 = 0;
|
|
2220
|
-
for (const [key, entry] of Object.entries(distributedFrame)) {
|
|
2221
|
-
const { table, width } = this.createBondCapitalTable(
|
|
2222
|
-
entry.earnings ?? 0n,
|
|
2223
|
-
entry.contributors ?? [],
|
|
2224
|
-
`Earnings (shared = ${formatPercent(entry.vaultSharingPercent)})`
|
|
2225
|
-
);
|
|
2226
|
-
if (width > maxWidth2) {
|
|
2227
|
-
maxWidth2 = width;
|
|
2228
|
-
}
|
|
2229
|
-
rows.push({
|
|
2230
|
-
Vault: key,
|
|
2231
|
-
Who: this.getOperatorName(Number(key)),
|
|
2232
|
-
Balances: table
|
|
2233
|
-
});
|
|
2234
|
-
}
|
|
2235
|
-
new Table({
|
|
2236
|
-
columns: [
|
|
2237
|
-
{ name: "Vault", alignment: "left" },
|
|
2238
|
-
{ name: "Who", alignment: "left" },
|
|
2239
|
-
{
|
|
2240
|
-
name: "Balances",
|
|
2241
|
-
title: "Contributor Balances",
|
|
2242
|
-
alignment: "center",
|
|
2243
|
-
minLen: maxWidth2
|
|
2244
|
-
}
|
|
2245
|
-
],
|
|
2246
|
-
rows
|
|
2247
|
-
}).printTable();
|
|
2248
|
-
}
|
|
2249
|
-
console.log(
|
|
2250
|
-
`
|
|
2251
|
-
|
|
2252
|
-
Active Bid Pool: ${formatArgons(this.bidPoolAmount)} (Frame ${this.nextFrameId})`
|
|
2253
|
-
);
|
|
2254
|
-
const Frame = this.poolVaultCapitalByFrame[this.nextFrameId];
|
|
2255
|
-
if (Object.keys(Frame ?? {}).length > 0) {
|
|
2256
|
-
const rows = [];
|
|
2257
|
-
let maxWidth2 = 0;
|
|
2258
|
-
for (const [key, entry] of Object.entries(Frame)) {
|
|
2259
|
-
const { table, width } = this.createBondCapitalTable(
|
|
2260
|
-
entry.activatedCapital,
|
|
2261
|
-
entry.contributors ?? []
|
|
2262
|
-
);
|
|
2263
|
-
if (width > maxWidth2) {
|
|
2264
|
-
maxWidth2 = width;
|
|
2265
|
-
}
|
|
2266
|
-
rows.push({
|
|
2267
|
-
Vault: key,
|
|
2268
|
-
Who: this.getOperatorName(Number(key)),
|
|
2269
|
-
"Pool Capital": table
|
|
2270
|
-
});
|
|
2271
|
-
}
|
|
2272
|
-
new Table({
|
|
2273
|
-
columns: [
|
|
2274
|
-
{ name: "Vault", alignment: "left" },
|
|
2275
|
-
{ name: "Who", alignment: "left" },
|
|
2276
|
-
{ name: "Pool Capital", alignment: "left", minLen: maxWidth2 }
|
|
2277
|
-
],
|
|
2278
|
-
rows
|
|
2279
|
-
}).printTable();
|
|
2280
|
-
}
|
|
2281
|
-
const raisingFunds = this.poolVaultCapitalByFrame[this.nextFrameId + 1] ?? [];
|
|
2282
|
-
let maxWidth = 0;
|
|
2283
|
-
const nextCapital = [];
|
|
2284
|
-
for (const x of this.vaultSecuritization) {
|
|
2285
|
-
const entry = raisingFunds[x.vaultId] ?? {};
|
|
2286
|
-
const { table, width } = this.createBondCapitalTable(
|
|
2287
|
-
x.activatedSecuritization,
|
|
2288
|
-
entry.contributors ?? []
|
|
2289
|
-
);
|
|
2290
|
-
if (width > maxWidth) {
|
|
2291
|
-
maxWidth = width;
|
|
2292
|
-
}
|
|
2293
|
-
nextCapital.push({
|
|
2294
|
-
Vault: x.vaultId,
|
|
2295
|
-
Owner: this.getOperatorName(x.vaultId),
|
|
2296
|
-
"Bitcoin Space": formatArgons(x.bitcoinSpace),
|
|
2297
|
-
"Activated Securitization": `${formatArgons(x.activatedSecuritization)} / slot`,
|
|
2298
|
-
"Liquidity Pool": `${formatPercent(x.vaultSharingPercent)} profit sharing${table}`
|
|
2299
|
-
});
|
|
2300
|
-
}
|
|
2301
|
-
if (nextCapital.length) {
|
|
2302
|
-
console.log(`
|
|
2303
|
-
|
|
2304
|
-
Raising Funds (Frame ${this.nextFrameId + 1}):`);
|
|
2305
|
-
new Table({
|
|
2306
|
-
columns: [
|
|
2307
|
-
{ name: "Vault", alignment: "left" },
|
|
2308
|
-
{ name: "Owner", alignment: "left" },
|
|
2309
|
-
{ name: "Bitcoin Space", alignment: "right" },
|
|
2310
|
-
{ name: "Activated Securitization", alignment: "right" },
|
|
2311
|
-
{ name: "Liquidity Pool", alignment: "left", minLen: maxWidth }
|
|
2312
|
-
],
|
|
2313
|
-
rows: nextCapital
|
|
2314
|
-
}).printTable();
|
|
2315
|
-
}
|
|
2316
|
-
}
|
|
2317
|
-
setVaultFrameData(frameId, vaultId, data) {
|
|
2318
|
-
this.poolVaultCapitalByFrame ??= {};
|
|
2319
|
-
this.poolVaultCapitalByFrame[frameId] ??= {};
|
|
2320
|
-
this.poolVaultCapitalByFrame[frameId][vaultId] ??= {
|
|
2321
|
-
activatedCapital: data.activatedCapital ?? data.contributors?.reduce((a, b) => a + b.amount, 0n) ?? 0n
|
|
2322
|
-
};
|
|
2323
|
-
Object.assign(this.poolVaultCapitalByFrame[frameId][vaultId], filterUndefined(data));
|
|
2324
|
-
}
|
|
2325
|
-
createBondCapitalTable(total, contributors, title = "Total") {
|
|
2326
|
-
const table = new Table({
|
|
2327
|
-
style: EMPTY_TABLE,
|
|
2328
|
-
columns: [
|
|
2329
|
-
{ name: "who", title, minLen: 10, alignment: "right" },
|
|
2330
|
-
{
|
|
2331
|
-
name: "amount",
|
|
2332
|
-
title: formatArgons(total),
|
|
2333
|
-
minLen: 7,
|
|
2334
|
-
alignment: "left"
|
|
2335
|
-
}
|
|
2336
|
-
]
|
|
2337
|
-
});
|
|
2338
|
-
for (const x of contributors) {
|
|
2339
|
-
table.addRow({
|
|
2340
|
-
who: this.accountRegistry.getName(x.address) ?? x.address,
|
|
2341
|
-
amount: formatArgons(x.amount)
|
|
2342
|
-
});
|
|
2343
|
-
}
|
|
2344
|
-
const str = table.render();
|
|
2345
|
-
const width = str.indexOf("\n");
|
|
2346
|
-
return { table: str, width };
|
|
2347
|
-
}
|
|
2348
|
-
loadFrameData(frameId, vaultFunds) {
|
|
2349
|
-
for (const [vaultId, fund] of vaultFunds) {
|
|
2350
|
-
const vaultIdNumber = vaultId.toNumber();
|
|
2351
|
-
const contributors = fund.contributorBalances.map(([a, b]) => ({
|
|
2352
|
-
address: a.toHuman(),
|
|
2353
|
-
amount: b.toBigInt()
|
|
2354
|
-
}));
|
|
2355
|
-
if (fund.distributedProfits.isSome) {
|
|
2356
|
-
if (frameId > (this.lastDistributedFrameId ?? 0)) {
|
|
2357
|
-
this.lastDistributedFrameId = frameId;
|
|
2358
|
-
}
|
|
2359
|
-
}
|
|
2360
|
-
this.setVaultFrameData(frameId, vaultIdNumber, {
|
|
2361
|
-
earnings: fund.distributedProfits.isSome ? fund.distributedProfits.unwrap().toBigInt() : void 0,
|
|
2362
|
-
vaultSharingPercent: convertPermillToBigNumber(fund.vaultSharingPercent.toBigInt()),
|
|
2363
|
-
contributors
|
|
2364
|
-
});
|
|
2365
|
-
}
|
|
2366
|
-
}
|
|
2367
|
-
};
|
|
2368
|
-
|
|
2369
|
-
// src/BitcoinLocks.ts
|
|
2370
|
-
import { hexToU8a as hexToU8a2, u8aToHex as u8aToHex3 } from "@polkadot/util";
|
|
2371
|
-
var SATS_PER_BTC = 100000000n;
|
|
2372
|
-
var BitcoinLocks = class _BitcoinLocks {
|
|
2373
|
-
constructor(client) {
|
|
2374
|
-
this.client = client;
|
|
2375
|
-
}
|
|
2376
|
-
async getUtxoIdFromEvents(events) {
|
|
2377
|
-
const client = await this.client;
|
|
2378
|
-
for (const event of events) {
|
|
2379
|
-
if (client.events.bitcoinLocks.BitcoinLockCreated.is(event)) {
|
|
2380
|
-
return event.data.utxoId.toNumber();
|
|
2381
|
-
}
|
|
2382
|
-
}
|
|
2383
|
-
return void 0;
|
|
2384
|
-
}
|
|
2385
|
-
async getMarketRate(satoshis) {
|
|
2386
|
-
const client = await this.client;
|
|
2387
|
-
const sats = client.createType("U64", satoshis.toString());
|
|
2388
|
-
const marketRate = await client.rpc.state.call("BitcoinApis_market_rate", sats.toHex(true));
|
|
2389
|
-
const rate = client.createType("Option<U128>", marketRate);
|
|
2390
|
-
if (!rate.isSome) {
|
|
2391
|
-
throw new Error("Market rate not available");
|
|
2392
|
-
}
|
|
2393
|
-
return rate.value.toBigInt();
|
|
2394
|
-
}
|
|
2395
|
-
async getRedemptionRate(satoshis) {
|
|
2396
|
-
const client = await this.client;
|
|
2397
|
-
const sats = client.createType("U64", satoshis.toString());
|
|
2398
|
-
const marketRate = await client.rpc.state.call("BitcoinApis_redemption_rate", sats.toHex(true));
|
|
2399
|
-
const rate = client.createType("Option<U128>", marketRate);
|
|
2400
|
-
if (!rate.isSome) {
|
|
2401
|
-
throw new Error("Redemption rate not available");
|
|
2402
|
-
}
|
|
2403
|
-
return rate.value.toBigInt();
|
|
2404
|
-
}
|
|
2405
|
-
async getConfig() {
|
|
2406
|
-
const client = await this.client;
|
|
2407
|
-
const bitcoinNetwork = await client.query.bitcoinUtxos.bitcoinNetwork();
|
|
2408
|
-
return {
|
|
2409
|
-
lockReleaseCosignDeadlineFrames: client.consts.bitcoinLocks.lockReleaseCosignDeadlineFrames.toNumber(),
|
|
2410
|
-
pendingConfirmationExpirationBlocks: client.consts.bitcoinUtxos.maxPendingConfirmationBlocks.toNumber(),
|
|
2411
|
-
tickDurationMillis: await client.query.ticks.genesisTicker().then((x) => x.tickDurationMillis.toNumber()),
|
|
2412
|
-
bitcoinNetwork
|
|
2413
|
-
};
|
|
2414
|
-
}
|
|
2415
|
-
async getBitcoinConfirmedBlockHeight() {
|
|
2416
|
-
const client = await this.client;
|
|
2417
|
-
return await client.query.bitcoinUtxos.confirmedBitcoinBlockTip().then((x) => x.value?.blockHeight.toNumber() ?? 0);
|
|
2418
|
-
}
|
|
2419
|
-
/**
|
|
2420
|
-
* Gets the UTXO reference by ID.
|
|
2421
|
-
* @param utxoId - The UTXO ID to look up.
|
|
2422
|
-
* @param atHeight - Optional block height to query the UTXO reference at a specific point in time.
|
|
2423
|
-
* @return An object containing the transaction ID and output index, or undefined if not found.
|
|
2424
|
-
* @return.txid - The Bitcoin transaction ID of the UTXO.
|
|
2425
|
-
* @return.vout - The output index of the UTXO in the transaction.
|
|
2426
|
-
* @return.bitcoinTxid - The Bitcoin transaction ID of the UTXO formatted in little endian
|
|
2427
|
-
*/
|
|
2428
|
-
async getUtxoRef(utxoId, atHeight) {
|
|
2429
|
-
let client = await this.client;
|
|
2430
|
-
if (atHeight !== void 0) {
|
|
2431
|
-
const blockHash = await client.rpc.chain.getBlockHash(atHeight);
|
|
2432
|
-
client = await client.at(blockHash);
|
|
2433
|
-
}
|
|
2434
|
-
const refRaw = await client.query.bitcoinUtxos.utxoIdToRef(utxoId);
|
|
2435
|
-
if (!refRaw) {
|
|
2436
|
-
return;
|
|
2437
|
-
}
|
|
2438
|
-
const ref = refRaw.unwrap();
|
|
2439
|
-
const txid = u8aToHex3(ref.txid);
|
|
2440
|
-
const bitcoinTxid = u8aToHex3(ref.txid.reverse());
|
|
2441
|
-
const vout = ref.outputIndex.toNumber();
|
|
2442
|
-
return { txid, vout, bitcoinTxid };
|
|
2443
|
-
}
|
|
2444
|
-
async getReleaseRequest(utxoId, atHeight) {
|
|
2445
|
-
let client = await this.client;
|
|
2446
|
-
if (atHeight !== void 0) {
|
|
2447
|
-
const blockHash = await client.rpc.chain.getBlockHash(atHeight);
|
|
2448
|
-
client = await client.at(blockHash);
|
|
2449
|
-
}
|
|
2450
|
-
const requestMaybe = await client.query.bitcoinLocks.lockReleaseRequestsByUtxoId(utxoId);
|
|
2451
|
-
if (!requestMaybe.isSome) {
|
|
2452
|
-
return void 0;
|
|
2453
|
-
}
|
|
2454
|
-
const request = requestMaybe.unwrap();
|
|
2455
|
-
return {
|
|
2456
|
-
toScriptPubkey: request.toScriptPubkey.toHex(),
|
|
2457
|
-
bitcoinNetworkFee: request.bitcoinNetworkFee.toBigInt(),
|
|
2458
|
-
dueFrame: request.cosignDueFrame.toNumber(),
|
|
2459
|
-
vaultId: request.vaultId.toNumber(),
|
|
2460
|
-
redemptionPrice: request.redemptionPrice.toBigInt()
|
|
2461
|
-
};
|
|
2462
|
-
}
|
|
2463
|
-
async submitVaultSignature(args) {
|
|
2464
|
-
const { utxoId, vaultSignature, argonKeyring, txProgressCallback } = args;
|
|
2465
|
-
const client = await this.client;
|
|
2466
|
-
if (!vaultSignature || vaultSignature.byteLength < 70 || vaultSignature.byteLength > 73) {
|
|
2467
|
-
throw new Error(
|
|
2468
|
-
`Invalid vault signature length: ${vaultSignature.byteLength}. Must be 70-73 bytes.`
|
|
2469
|
-
);
|
|
2470
|
-
}
|
|
2471
|
-
const signature = u8aToHex3(vaultSignature);
|
|
2472
|
-
const tx = client.tx.bitcoinLocks.cosignRelease(utxoId, signature);
|
|
2473
|
-
const submitter = new TxSubmitter(client, tx, argonKeyring);
|
|
2474
|
-
return await submitter.submit({ txProgressCallback });
|
|
2475
|
-
}
|
|
2476
|
-
async getBitcoinLock(utxoId) {
|
|
2477
|
-
const client = await this.client;
|
|
2478
|
-
const utxoRaw = await client.query.bitcoinLocks.locksByUtxoId(utxoId);
|
|
2479
|
-
if (!utxoRaw.isSome) {
|
|
2480
|
-
return;
|
|
2481
|
-
}
|
|
2482
|
-
const utxo = utxoRaw.unwrap();
|
|
2483
|
-
const p2shBytesPrefix = "0020";
|
|
2484
|
-
const wscriptHash = utxo.utxoScriptPubkey.asP2wsh.wscriptHash.toHex().replace("0x", "");
|
|
2485
|
-
const p2wshScriptHashHex = `0x${p2shBytesPrefix}${wscriptHash}`;
|
|
2486
|
-
const vaultId = utxo.vaultId.toNumber();
|
|
2487
|
-
const lockPrice = utxo.lockPrice.toBigInt();
|
|
2488
|
-
const ownerAccount = utxo.ownerAccount.toHuman();
|
|
2489
|
-
const satoshis = utxo.satoshis.toBigInt();
|
|
2490
|
-
const vaultPubkey = utxo.vaultPubkey.toHex();
|
|
2491
|
-
const vaultClaimPubkey = utxo.vaultClaimPubkey.toHex();
|
|
2492
|
-
const ownerPubkey = utxo.ownerPubkey.toHex();
|
|
2493
|
-
const [fingerprint, cosign_hd_index, claim_hd_index] = utxo.vaultXpubSources;
|
|
2494
|
-
const vaultXpubSources = {
|
|
2495
|
-
parentFingerprint: new Uint8Array(fingerprint),
|
|
2496
|
-
cosignHdIndex: cosign_hd_index.toNumber(),
|
|
2497
|
-
claimHdIndex: claim_hd_index.toNumber()
|
|
2498
|
-
};
|
|
2499
|
-
const vaultClaimHeight = utxo.vaultClaimHeight.toNumber();
|
|
2500
|
-
const openClaimHeight = utxo.openClaimHeight.toNumber();
|
|
2501
|
-
const createdAtHeight = utxo.createdAtHeight.toNumber();
|
|
2502
|
-
const isVerified = utxo.isVerified.toJSON();
|
|
2503
|
-
const isRejectedNeedsRelease = utxo.isRejectedNeedsRelease.toJSON();
|
|
2504
|
-
const fundHoldExtensionsByBitcoinExpirationHeight = Object.fromEntries(
|
|
2505
|
-
[...utxo.fundHoldExtensions.entries()].map(([x, y]) => [x.toNumber(), y.toBigInt()])
|
|
2506
|
-
);
|
|
2507
|
-
return {
|
|
2508
|
-
utxoId,
|
|
2509
|
-
p2wshScriptHashHex,
|
|
2510
|
-
vaultId,
|
|
2511
|
-
lockPrice,
|
|
2512
|
-
ownerAccount,
|
|
2513
|
-
satoshis,
|
|
2514
|
-
vaultPubkey,
|
|
2515
|
-
vaultClaimPubkey,
|
|
2516
|
-
ownerPubkey,
|
|
2517
|
-
vaultXpubSources,
|
|
2518
|
-
vaultClaimHeight,
|
|
2519
|
-
openClaimHeight,
|
|
2520
|
-
createdAtHeight,
|
|
2521
|
-
isVerified,
|
|
2522
|
-
isRejectedNeedsRelease,
|
|
2523
|
-
fundHoldExtensionsByBitcoinExpirationHeight
|
|
2524
|
-
};
|
|
2525
|
-
}
|
|
2526
|
-
/**
|
|
2527
|
-
* Finds the cosign signature for a vault lock by UTXO ID. Optionally waits for the signature
|
|
2528
|
-
* @param utxoId - The UTXO ID of the bitcoin lock
|
|
2529
|
-
* @param waitForSignatureMillis - Optional timeout in milliseconds to wait for the signature. If -1, waits indefinitely.
|
|
2530
|
-
*/
|
|
2531
|
-
async findVaultCosignSignature(utxoId, waitForSignatureMillis) {
|
|
2532
|
-
const client = await this.client;
|
|
2533
|
-
const releaseHeight = await client.query.bitcoinLocks.lockReleaseCosignHeightById(utxoId);
|
|
2534
|
-
if (releaseHeight.isSome) {
|
|
2535
|
-
const releaseHeightValue = releaseHeight.unwrap().toNumber();
|
|
2536
|
-
const signature = await this.getVaultCosignSignature(utxoId, releaseHeightValue);
|
|
2537
|
-
if (signature) {
|
|
2538
|
-
return { blockHeight: releaseHeightValue, signature };
|
|
2539
|
-
}
|
|
2540
|
-
}
|
|
2541
|
-
if (!waitForSignatureMillis) {
|
|
2542
|
-
return void 0;
|
|
2543
|
-
}
|
|
2544
|
-
return await new Promise(async (resolve, reject) => {
|
|
2545
|
-
let timeout;
|
|
2546
|
-
const unsub = await client.rpc.chain.subscribeNewHeads((header) => {
|
|
2547
|
-
const atHeight = header.number.toNumber();
|
|
2548
|
-
this.getVaultCosignSignature(utxoId, atHeight).then((signature) => {
|
|
2549
|
-
if (signature) {
|
|
2550
|
-
unsub?.();
|
|
2551
|
-
clearTimeout(timeout);
|
|
2552
|
-
resolve({ signature, blockHeight: atHeight });
|
|
2553
|
-
}
|
|
2554
|
-
}).catch((err) => {
|
|
2555
|
-
console.error(`Error checking for cosign signature at height ${atHeight}:`, err);
|
|
2556
|
-
});
|
|
2557
|
-
});
|
|
2558
|
-
if (waitForSignatureMillis !== -1) {
|
|
2559
|
-
timeout = setTimeout(() => {
|
|
2560
|
-
unsub?.();
|
|
2561
|
-
reject(new Error(`Timeout waiting for cosign signature for UTXO ID ${utxoId}`));
|
|
2562
|
-
}, waitForSignatureMillis);
|
|
2563
|
-
}
|
|
2564
|
-
});
|
|
2565
|
-
}
|
|
2566
|
-
async blockHashAtHeight(atHeight) {
|
|
2567
|
-
const client = await this.client;
|
|
2568
|
-
for (let i = 0; i < 10; i++) {
|
|
2569
|
-
const currentHeight = await client.query.system.number().then((x) => x.toNumber());
|
|
2570
|
-
if (atHeight > currentHeight) {
|
|
2571
|
-
console.warn(
|
|
2572
|
-
`Requested block height ${atHeight} is greater than current height ${currentHeight}. Retrying...`
|
|
2573
|
-
);
|
|
2574
|
-
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
2575
|
-
continue;
|
|
2576
|
-
}
|
|
2577
|
-
const hash = await client.rpc.chain.getBlockHash(atHeight).then((x) => x.toHex());
|
|
2578
|
-
if (hash === "0x0000000000000000000000000000000000000000000000000000000000000000") {
|
|
2579
|
-
console.warn(`Block hash not found for height ${atHeight}. Retrying...`);
|
|
2580
|
-
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
2581
|
-
continue;
|
|
2582
|
-
}
|
|
2583
|
-
return hash;
|
|
2584
|
-
}
|
|
2585
|
-
return void 0;
|
|
2586
|
-
}
|
|
2587
|
-
async getVaultCosignSignature(utxoId, atHeight) {
|
|
2588
|
-
const client = await this.client;
|
|
2589
|
-
const blockHash = await this.blockHashAtHeight(atHeight);
|
|
2590
|
-
if (!blockHash) {
|
|
2591
|
-
console.warn(`Block hash not found for height ${atHeight}`);
|
|
2592
|
-
return void 0;
|
|
2593
|
-
}
|
|
2594
|
-
const blockEvents = await client.at(blockHash).then((api) => api.query.system.events());
|
|
2595
|
-
for (const event of blockEvents) {
|
|
2596
|
-
if (client.events.bitcoinLocks.BitcoinUtxoCosigned.is(event.event)) {
|
|
2597
|
-
const { utxoId: id, signature } = event.event.data;
|
|
2598
|
-
if (id.toNumber() === utxoId) {
|
|
2599
|
-
return new Uint8Array(signature);
|
|
2600
|
-
}
|
|
2601
|
-
}
|
|
2602
|
-
}
|
|
2603
|
-
return void 0;
|
|
2604
|
-
}
|
|
2605
|
-
async findPendingMints(utxoId) {
|
|
2606
|
-
const client = await this.client;
|
|
2607
|
-
const pendingMint = await client.query.mint.pendingMintUtxos();
|
|
2608
|
-
const mintsPending = [];
|
|
2609
|
-
for (const [utxoIdRaw, _accountId, mintAmountRaw] of pendingMint) {
|
|
2610
|
-
if (utxoIdRaw.toNumber() === utxoId) {
|
|
2611
|
-
mintsPending.push(mintAmountRaw.toBigInt());
|
|
2612
|
-
}
|
|
2613
|
-
}
|
|
2614
|
-
return mintsPending;
|
|
2615
|
-
}
|
|
2616
|
-
async createInitializeLockTx(args) {
|
|
2617
|
-
const { vault, argonKeyring, satoshis, tip = 0n, ownerBitcoinPubkey } = args;
|
|
2618
|
-
const client = await this.client;
|
|
2619
|
-
if (ownerBitcoinPubkey.length !== 33) {
|
|
2620
|
-
throw new Error(
|
|
2621
|
-
`Invalid Bitcoin key length: ${ownerBitcoinPubkey.length}. Must be a compressed pukey (33 bytes).`
|
|
2622
|
-
);
|
|
2623
|
-
}
|
|
2624
|
-
const tx = client.tx.bitcoinLocks.initialize(vault.vaultId, satoshis, ownerBitcoinPubkey);
|
|
2625
|
-
const submitter = new TxSubmitter(
|
|
2626
|
-
client,
|
|
2627
|
-
client.tx.bitcoinLocks.initialize(vault.vaultId, satoshis, ownerBitcoinPubkey),
|
|
2628
|
-
argonKeyring
|
|
2629
|
-
);
|
|
2630
|
-
const marketPrice = await this.getMarketRate(BigInt(satoshis));
|
|
2631
|
-
const isVaultOwner = argonKeyring.address === vault.operatorAccountId;
|
|
2632
|
-
const securityFee = isVaultOwner ? 0n : vault.calculateBitcoinFee(marketPrice);
|
|
2633
|
-
const { canAfford, availableBalance, txFee } = await submitter.canAfford({
|
|
2634
|
-
tip,
|
|
2635
|
-
unavailableBalance: securityFee + (args.reducedBalanceBy ?? 0n),
|
|
2636
|
-
includeExistentialDeposit: true
|
|
2637
|
-
});
|
|
2638
|
-
if (!canAfford) {
|
|
2639
|
-
throw new Error(
|
|
2640
|
-
`Insufficient funds to initialize lock. Available: ${formatArgons(availableBalance)}, Required: ${satoshis}`
|
|
2641
|
-
);
|
|
2642
|
-
}
|
|
2643
|
-
return { tx, securityFee, txFee };
|
|
2644
|
-
}
|
|
2645
|
-
async getBitcoinLockFromTxResult(txResult) {
|
|
2646
|
-
const client = await this.client;
|
|
2647
|
-
const blockHash = await txResult.inBlockPromise;
|
|
2648
|
-
const blockHeight = await client.at(blockHash).then((x) => x.query.system.number()).then((x) => x.toNumber());
|
|
2649
|
-
const utxoId = await this.getUtxoIdFromEvents(txResult.events) ?? 0;
|
|
2650
|
-
if (utxoId === 0) {
|
|
2651
|
-
throw new Error("Bitcoin lock creation failed, no UTXO ID found in transaction events");
|
|
2652
|
-
}
|
|
2653
|
-
const lock = await this.getBitcoinLock(utxoId);
|
|
2654
|
-
if (!lock) {
|
|
2655
|
-
throw new Error(`Lock with ID ${utxoId} not found after initialization`);
|
|
2656
|
-
}
|
|
2657
|
-
return { lock, createdAtHeight: blockHeight, txResult };
|
|
2658
|
-
}
|
|
2659
|
-
async initializeLock(args) {
|
|
2660
|
-
const { argonKeyring, tip = 0n, txProgressCallback } = args;
|
|
2661
|
-
const client = await this.client;
|
|
2662
|
-
const { tx, securityFee } = await this.createInitializeLockTx(args);
|
|
2663
|
-
const submitter = new TxSubmitter(client, tx, argonKeyring);
|
|
2664
|
-
const txResult = await submitter.submit({
|
|
2665
|
-
waitForBlock: true,
|
|
2666
|
-
logResults: true,
|
|
2667
|
-
tip,
|
|
2668
|
-
txProgressCallback
|
|
2669
|
-
});
|
|
2670
|
-
const { lock, createdAtHeight } = await this.getBitcoinLockFromTxResult(txResult);
|
|
2671
|
-
return {
|
|
2672
|
-
lock,
|
|
2673
|
-
createdAtHeight,
|
|
2674
|
-
txResult,
|
|
2675
|
-
securityFee
|
|
2676
|
-
};
|
|
2677
|
-
}
|
|
2678
|
-
async requiredSatoshisForArgonLiquidity(argonAmount) {
|
|
2679
|
-
const marketRatePerBitcoin = await this.getMarketRate(SATS_PER_BTC);
|
|
2680
|
-
return argonAmount * SATS_PER_BTC / marketRatePerBitcoin;
|
|
2681
|
-
}
|
|
2682
|
-
async requestRelease(args) {
|
|
2683
|
-
const client = await this.client;
|
|
2684
|
-
const {
|
|
2685
|
-
lock,
|
|
2686
|
-
releaseRequest: { bitcoinNetworkFee, toScriptPubkey },
|
|
2687
|
-
argonKeyring,
|
|
2688
|
-
tip,
|
|
2689
|
-
txProgressCallback
|
|
2690
|
-
} = args;
|
|
2691
|
-
if (!toScriptPubkey.startsWith("0x")) {
|
|
2692
|
-
throw new Error("toScriptPubkey must be a hex string starting with 0x");
|
|
2693
|
-
}
|
|
2694
|
-
const submitter = new TxSubmitter(
|
|
2695
|
-
client,
|
|
2696
|
-
client.tx.bitcoinLocks.requestRelease(lock.utxoId, toScriptPubkey, bitcoinNetworkFee),
|
|
2697
|
-
argonKeyring
|
|
2698
|
-
);
|
|
2699
|
-
let redemptionPrice = await this.getRedemptionRate(lock.satoshis);
|
|
2700
|
-
if (redemptionPrice > lock.lockPrice) {
|
|
2701
|
-
redemptionPrice = lock.lockPrice;
|
|
2702
|
-
}
|
|
2703
|
-
const canAfford = await submitter.canAfford({
|
|
2704
|
-
tip,
|
|
2705
|
-
unavailableBalance: BigInt(redemptionPrice)
|
|
2706
|
-
});
|
|
2707
|
-
if (!canAfford.canAfford) {
|
|
2708
|
-
throw new Error(
|
|
2709
|
-
`Insufficient funds to release lock. Available: ${formatArgons(canAfford.availableBalance)}, Required: ${formatArgons(redemptionPrice)}`
|
|
2710
|
-
);
|
|
2711
|
-
}
|
|
2712
|
-
const txResult = await submitter.submit({
|
|
2713
|
-
waitForBlock: true,
|
|
2714
|
-
logResults: true,
|
|
2715
|
-
tip,
|
|
2716
|
-
txProgressCallback
|
|
2717
|
-
});
|
|
2718
|
-
const blockHash = await txResult.inBlockPromise;
|
|
2719
|
-
const blockHeight = await client.at(blockHash).then((x) => x.query.system.number()).then((x) => x.toNumber());
|
|
2720
|
-
return {
|
|
2721
|
-
blockHash,
|
|
2722
|
-
blockHeight
|
|
2723
|
-
};
|
|
2724
|
-
}
|
|
2725
|
-
async releasePrice(satoshis, lockPrice) {
|
|
2726
|
-
const client = await this.client;
|
|
2727
|
-
const redemptionRate = await this.getRedemptionRate(satoshis);
|
|
2728
|
-
if (redemptionRate > lockPrice) {
|
|
2729
|
-
return redemptionRate;
|
|
2730
|
-
}
|
|
2731
|
-
return lockPrice;
|
|
2732
|
-
}
|
|
2733
|
-
async getRatchetPrice(lock, vault) {
|
|
2734
|
-
const { createdAtHeight, vaultClaimHeight, lockPrice, satoshis } = lock;
|
|
2735
|
-
const client = await this.client;
|
|
2736
|
-
const marketRate = await this.getMarketRate(BigInt(satoshis));
|
|
2737
|
-
let ratchetingFee = vault.terms.bitcoinBaseFee;
|
|
2738
|
-
let burnAmount = 0n;
|
|
2739
|
-
if (marketRate > lockPrice) {
|
|
2740
|
-
const lockFee = vault.calculateBitcoinFee(marketRate);
|
|
2741
|
-
const currentBitcoinHeight = await client.query.bitcoinUtxos.confirmedBitcoinBlockTip().then((x) => x.unwrap().blockHeight.toNumber());
|
|
2742
|
-
const blockLength = vaultClaimHeight - createdAtHeight;
|
|
2743
|
-
const elapsed = (currentBitcoinHeight - createdAtHeight) / blockLength;
|
|
2744
|
-
const remainingDuration = 1 - elapsed;
|
|
2745
|
-
ratchetingFee = BigInt(remainingDuration * Number(lockFee));
|
|
2746
|
-
} else {
|
|
2747
|
-
burnAmount = await this.releasePrice(lock.satoshis, lockPrice);
|
|
2748
|
-
}
|
|
2749
|
-
return {
|
|
2750
|
-
ratchetingFee,
|
|
2751
|
-
burnAmount,
|
|
2752
|
-
marketRate
|
|
2753
|
-
};
|
|
2754
|
-
}
|
|
2755
|
-
async ratchet(args) {
|
|
2756
|
-
const { lock, argonKeyring, tip = 0n, vault, txProgressCallback } = args;
|
|
2757
|
-
const client = await this.client;
|
|
2758
|
-
const ratchetPrice = await this.getRatchetPrice(lock, vault);
|
|
2759
|
-
const txSubmitter = new TxSubmitter(
|
|
2760
|
-
client,
|
|
2761
|
-
client.tx.bitcoinLocks.ratchet(lock.utxoId),
|
|
2762
|
-
argonKeyring
|
|
2763
|
-
);
|
|
2764
|
-
const canAfford = await txSubmitter.canAfford({
|
|
2765
|
-
tip,
|
|
2766
|
-
unavailableBalance: BigInt(ratchetPrice.burnAmount + ratchetPrice.ratchetingFee)
|
|
2767
|
-
});
|
|
2768
|
-
if (!canAfford.canAfford) {
|
|
2769
|
-
throw new Error(
|
|
2770
|
-
`Insufficient funds to ratchet lock. Available: ${formatArgons(canAfford.availableBalance)}, Required: ${formatArgons(
|
|
2771
|
-
ratchetPrice.burnAmount + ratchetPrice.ratchetingFee
|
|
2772
|
-
)}`
|
|
2773
|
-
);
|
|
2774
|
-
}
|
|
2775
|
-
const submission = await txSubmitter.submit({
|
|
2776
|
-
waitForBlock: true,
|
|
2777
|
-
tip,
|
|
2778
|
-
txProgressCallback
|
|
2779
|
-
});
|
|
2780
|
-
const ratchetEvent = submission.events.find(
|
|
2781
|
-
(x) => client.events.bitcoinLocks.BitcoinLockRatcheted.is(x)
|
|
2782
|
-
);
|
|
2783
|
-
if (!ratchetEvent) {
|
|
2784
|
-
throw new Error(`Ratchet event not found in transaction events`);
|
|
2785
|
-
}
|
|
2786
|
-
const blockHash = await submission.inBlockPromise;
|
|
2787
|
-
const api = await client.at(blockHash);
|
|
2788
|
-
const blockHeight = await api.query.system.number().then((x) => x.toNumber());
|
|
2789
|
-
const bitcoinBlockHeight = await api.query.bitcoinUtxos.confirmedBitcoinBlockTip().then((x) => x.unwrap().blockHeight.toNumber());
|
|
2790
|
-
const { amountBurned, newLockPrice, originalLockPrice } = ratchetEvent.data;
|
|
2791
|
-
let mintAmount = newLockPrice.toBigInt();
|
|
2792
|
-
if (newLockPrice > originalLockPrice) {
|
|
2793
|
-
mintAmount -= originalLockPrice.toBigInt();
|
|
2794
|
-
}
|
|
2795
|
-
return {
|
|
2796
|
-
txFee: submission.finalFee ?? 0n,
|
|
2797
|
-
securityFee: ratchetPrice.ratchetingFee,
|
|
2798
|
-
pendingMint: mintAmount,
|
|
2799
|
-
newLockPrice: newLockPrice.toBigInt(),
|
|
2800
|
-
burned: amountBurned.toBigInt(),
|
|
2801
|
-
blockHeight,
|
|
2802
|
-
bitcoinBlockHeight
|
|
2803
|
-
};
|
|
2804
|
-
}
|
|
2805
|
-
static async waitForSpace(accountset, options) {
|
|
2806
|
-
const { argonAmount, bitcoinXpub, maxLockFee, tip = 0n } = options;
|
|
2807
|
-
const vaults = new VaultMonitor(accountset, {
|
|
2808
|
-
bitcoinSpaceAvailable: argonAmount
|
|
2809
|
-
});
|
|
2810
|
-
const bitcoinXpubBuffer = hexToU8a2(bitcoinXpub);
|
|
2811
|
-
return new Promise(async (resolve, reject) => {
|
|
2812
|
-
vaults.events.on("bitcoin-space-above", async (vaultId, amount) => {
|
|
2813
|
-
const vault = vaults.vaultsById[vaultId];
|
|
2814
|
-
const fee = vault.calculateBitcoinFee(amount);
|
|
2815
|
-
console.log(
|
|
2816
|
-
`Vault ${vaultId} has ${formatArgons(amount)} argons available for bitcoin. Lock fee is ${formatArgons(fee)}`
|
|
2817
|
-
);
|
|
2818
|
-
if (maxLockFee !== void 0 && fee > maxLockFee) {
|
|
2819
|
-
console.log(
|
|
2820
|
-
`Skipping vault ${vaultId} due to high lock fee: ${formatArgons(maxLockFee)}`
|
|
2821
|
-
);
|
|
2822
|
-
return;
|
|
2823
|
-
}
|
|
2824
|
-
try {
|
|
2825
|
-
const bitcoinLock = new _BitcoinLocks(accountset.client);
|
|
2826
|
-
let satoshis = await bitcoinLock.requiredSatoshisForArgonLiquidity(amount);
|
|
2827
|
-
satoshis -= options.satoshiWiggleRoomForDynamicPrice ?? 500n;
|
|
2828
|
-
const { txResult, lock, securityFee } = await bitcoinLock.initializeLock({
|
|
2829
|
-
vault,
|
|
2830
|
-
satoshis,
|
|
2831
|
-
argonKeyring: accountset.txSubmitterPair,
|
|
2832
|
-
ownerBitcoinPubkey: bitcoinXpubBuffer,
|
|
2833
|
-
tip
|
|
2834
|
-
});
|
|
2835
|
-
resolve({
|
|
2836
|
-
satoshis,
|
|
2837
|
-
argons: argonAmount,
|
|
2838
|
-
vaultId,
|
|
2839
|
-
securityFee,
|
|
2840
|
-
txFee: txResult.finalFee,
|
|
2841
|
-
finalizedPromise: txResult.finalizedPromise,
|
|
2842
|
-
utxoId: lock.utxoId
|
|
2843
|
-
});
|
|
2844
|
-
} catch (err) {
|
|
2845
|
-
console.error("Error submitting bitcoin lock tx:", err);
|
|
2846
|
-
reject(err);
|
|
2847
|
-
} finally {
|
|
2848
|
-
vaults.stop();
|
|
2849
|
-
}
|
|
2850
|
-
});
|
|
2851
|
-
await vaults.monitor();
|
|
2852
|
-
});
|
|
2853
|
-
}
|
|
2854
|
-
};
|
|
2855
|
-
|
|
2856
|
-
// src/keyringUtils.ts
|
|
2857
|
-
function keyringFromSuri(suri, cryptoType = "sr25519") {
|
|
2858
|
-
return new Keyring({ type: cryptoType }).createFromUri(suri);
|
|
2859
|
-
}
|
|
2860
|
-
function createKeyringPair(opts) {
|
|
2861
|
-
const { cryptoType } = opts;
|
|
2862
|
-
const seed = mnemonicGenerate();
|
|
2863
|
-
return keyringFromSuri(seed, cryptoType);
|
|
2864
|
-
}
|
|
2865
|
-
|
|
2866
|
-
// src/index.ts
|
|
2867
|
-
import { u8aToHex as u8aToHex4, hexToU8a as hexToU8a3, u8aEq } from "@polkadot/util";
|
|
2868
|
-
import { GenericEvent as GenericEvent2, GenericBlock, GenericAddress } from "@polkadot/types/generic";
|
|
2869
|
-
import {
|
|
2870
|
-
BTreeMap,
|
|
2871
|
-
Bytes,
|
|
2872
|
-
Compact,
|
|
2873
|
-
Enum,
|
|
2874
|
-
Null,
|
|
2875
|
-
Option as Option2,
|
|
2876
|
-
Result,
|
|
2877
|
-
Bool,
|
|
2878
|
-
Tuple,
|
|
2879
|
-
Range,
|
|
2880
|
-
Struct,
|
|
2881
|
-
Text,
|
|
2882
|
-
U256,
|
|
2883
|
-
U8aFixed,
|
|
2884
|
-
Vec,
|
|
2885
|
-
bool,
|
|
2886
|
-
i128,
|
|
2887
|
-
u128,
|
|
2888
|
-
u16,
|
|
2889
|
-
u32,
|
|
2890
|
-
u64,
|
|
2891
|
-
u8
|
|
2892
|
-
} from "@polkadot/types-codec";
|
|
2893
|
-
async function waitForLoad() {
|
|
2894
|
-
await cryptoWaitReady();
|
|
2895
|
-
}
|
|
2896
|
-
async function getClient(host) {
|
|
2897
|
-
let provider;
|
|
2898
|
-
if (host.startsWith("http")) {
|
|
2899
|
-
provider = new HttpProvider(host);
|
|
2900
|
-
} else {
|
|
2901
|
-
provider = new WsProvider(host);
|
|
2902
|
-
}
|
|
2903
|
-
return await ApiPromise.create({ provider, noInitWarn: true });
|
|
2904
|
-
}
|
|
2905
|
-
|
|
2906
|
-
export {
|
|
2907
|
-
WageProtector,
|
|
2908
|
-
setConfig,
|
|
2909
|
-
getConfig,
|
|
2910
|
-
TxSubmitter,
|
|
2911
|
-
TxResult,
|
|
2912
|
-
MICROGONS_PER_ARGON,
|
|
2913
|
-
miniSecretFromUri,
|
|
2914
|
-
formatArgons,
|
|
2915
|
-
formatPercent,
|
|
2916
|
-
filterUndefined,
|
|
2917
|
-
gettersToObject,
|
|
2918
|
-
toFixedNumber,
|
|
2919
|
-
convertNumberToFixedU128,
|
|
2920
|
-
convertFixedU128ToBigNumber,
|
|
2921
|
-
convertPermillToBigNumber,
|
|
2922
|
-
convertNumberToPermill,
|
|
2923
|
-
eventDataToJson,
|
|
2924
|
-
dispatchErrorToString,
|
|
2925
|
-
ExtrinsicError2 as ExtrinsicError,
|
|
2926
|
-
dispatchErrorToExtrinsicError,
|
|
2927
|
-
checkForExtrinsicSuccess,
|
|
2928
|
-
JsonExt,
|
|
2929
|
-
createNanoEvents,
|
|
2930
|
-
TypedEmitter,
|
|
2931
|
-
AccountRegistry,
|
|
2932
|
-
getTickFromHeader,
|
|
2933
|
-
getAuthorFromHeader,
|
|
2934
|
-
BlockWatch,
|
|
2935
|
-
FrameCalculator,
|
|
2936
|
-
AccountMiners,
|
|
2937
|
-
Accountset,
|
|
2938
|
-
parseSubaccountRange,
|
|
2939
|
-
MiningBids,
|
|
2940
|
-
Vault,
|
|
2941
|
-
VaultMonitor,
|
|
2942
|
-
CohortBidder,
|
|
2943
|
-
BidPool,
|
|
2944
|
-
SATS_PER_BTC,
|
|
2945
|
-
BitcoinLocks,
|
|
2946
|
-
keyringFromSuri,
|
|
2947
|
-
createKeyringPair,
|
|
2948
|
-
Keyring,
|
|
2949
|
-
decodeAddress,
|
|
2950
|
-
mnemonicGenerate,
|
|
2951
|
-
waitForLoad,
|
|
2952
|
-
getClient,
|
|
2953
|
-
u8aToHex4 as u8aToHex,
|
|
2954
|
-
hexToU8a3 as hexToU8a,
|
|
2955
|
-
u8aEq,
|
|
2956
|
-
GenericEvent2 as GenericEvent,
|
|
2957
|
-
GenericBlock,
|
|
2958
|
-
GenericAddress,
|
|
2959
|
-
BTreeMap,
|
|
2960
|
-
Bytes,
|
|
2961
|
-
Compact,
|
|
2962
|
-
Enum,
|
|
2963
|
-
Null,
|
|
2964
|
-
Option2 as Option,
|
|
2965
|
-
Result,
|
|
2966
|
-
Bool,
|
|
2967
|
-
Tuple,
|
|
2968
|
-
Range,
|
|
2969
|
-
Struct,
|
|
2970
|
-
Text,
|
|
2971
|
-
U256,
|
|
2972
|
-
U8aFixed,
|
|
2973
|
-
Vec,
|
|
2974
|
-
bool,
|
|
2975
|
-
i128,
|
|
2976
|
-
u128,
|
|
2977
|
-
u16,
|
|
2978
|
-
u32,
|
|
2979
|
-
u64,
|
|
2980
|
-
u8
|
|
2981
|
-
};
|
|
2982
|
-
//# sourceMappingURL=chunk-PXZPYJ4P.js.map
|