@argonprotocol/mainchain 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,854 @@
1
+ import {
2
+ Accountset,
3
+ BidPool,
4
+ BitcoinLocks,
5
+ CohortBidder,
6
+ Keyring,
7
+ MICROGONS_PER_ARGON,
8
+ MiningBids,
9
+ TxSubmitter,
10
+ Vault,
11
+ VaultMonitor,
12
+ createKeyringPair,
13
+ formatArgons,
14
+ getClient,
15
+ keyringFromSuri,
16
+ mnemonicGenerate,
17
+ parseSubaccountRange
18
+ } from "./chunk-RXCQYVE7.js";
19
+
20
+ // src/clis/index.ts
21
+ import { Command as Command6, Option } from "@commander-js/extra-typings";
22
+
23
+ // src/clis/accountCli.ts
24
+ import { Command } from "@commander-js/extra-typings";
25
+ import { printTable } from "console-table-printer";
26
+ import { cryptoWaitReady } from "@polkadot/util-crypto";
27
+ import { writeFileSync } from "node:fs";
28
+ import * as process2 from "node:process";
29
+ function accountCli() {
30
+ const program = new Command("accounts").description(
31
+ "Manage subaccounts from a single keypair"
32
+ );
33
+ program.command("watch").description("Watch for blocks closed by subaccounts").action(async () => {
34
+ const accountset = await accountsetFromCli(program);
35
+ const accountMiners = await accountset.watchBlocks();
36
+ accountMiners.events.on("mined", (_block, mined) => {
37
+ console.log("Your accounts authored a block", mined);
38
+ });
39
+ accountMiners.events.on("minted", (_block, minted) => {
40
+ console.log("Your accounts minted argons", minted);
41
+ });
42
+ });
43
+ program.command("list", { isDefault: true }).description("Show subaccounts").option("--addresses", "Just show a list of ids").action(async ({ addresses }) => {
44
+ const { subaccounts } = globalOptions(program);
45
+ const accountset = await accountsetFromCli(program);
46
+ if (addresses) {
47
+ const addresses2 = accountset.addresses;
48
+ console.log(addresses2.join(","));
49
+ process2.exit(0);
50
+ }
51
+ const [argonots, argons, seats, bids] = await Promise.all([
52
+ accountset.totalArgonotsAt(),
53
+ accountset.totalArgonsAt(),
54
+ accountset.miningSeats(),
55
+ accountset.bids()
56
+ ]);
57
+ const accountSubset = subaccounts ? accountset.getAccountsInRange(subaccounts) : void 0;
58
+ const status = accountset.status({
59
+ argons,
60
+ argonots,
61
+ accountSubset,
62
+ seats,
63
+ bids
64
+ });
65
+ printTable(status);
66
+ process2.exit(0);
67
+ });
68
+ program.command("create").description('Create an account "env" file and optionally register keys').requiredOption(
69
+ "--path <path>",
70
+ "The path to an env file to create (convention is .env.<name>)"
71
+ ).option(
72
+ "--register-keys-to <url>",
73
+ "Register the keys to a url (normally this is localhost)"
74
+ ).action(async ({ registerKeysTo, path }) => {
75
+ const { accountPassphrase, accountSuri, accountFilePath } = globalOptions(program);
76
+ const accountset = await accountsetFromCli(program);
77
+ process2.env.KEYS_MNEMONIC ||= mnemonicGenerate();
78
+ if (registerKeysTo) {
79
+ await accountset.registerKeys(registerKeysTo);
80
+ console.log("Keys registered to", registerKeysTo);
81
+ }
82
+ const envData = {
83
+ ACCOUNT_JSON_PATH: accountFilePath,
84
+ ACCOUNT_SURI: accountSuri,
85
+ ACCOUNT_PASSPHRASE: accountPassphrase,
86
+ KEYS_MNEMONIC: process2.env.KEYS_MNEMONIC,
87
+ SUBACCOUNT_RANGE: "0-49"
88
+ };
89
+ let envfile = "";
90
+ for (const [key, value] of Object.entries(envData)) {
91
+ if (key) {
92
+ const line = `${key}=${String(value)}`;
93
+ envfile += line + "\n";
94
+ }
95
+ }
96
+ writeFileSync(path, envfile);
97
+ console.log("Created env file at", path);
98
+ process2.exit();
99
+ });
100
+ program.command("new-key-seed").description("Create a new mnemonic for runtime keys").action(async () => {
101
+ await cryptoWaitReady();
102
+ const mnemonic = mnemonicGenerate();
103
+ console.log(
104
+ "New mnemonic (add this to your .env as KEYS_MNEMONIC):",
105
+ mnemonic
106
+ );
107
+ process2.exit(0);
108
+ });
109
+ program.command("register-keys").description("Create an insert-keys script with curl").argument(
110
+ "[node-rpc-url]",
111
+ "The url to your node host (should be installed on machine via localhost)",
112
+ "http://localhost:9944"
113
+ ).option(
114
+ "--print-only",
115
+ "Output as curl commands instead of direct registration"
116
+ ).action(async (nodeRpcUrl, { printOnly }) => {
117
+ const accountset = await accountsetFromCli(program);
118
+ if (printOnly) {
119
+ const { gran, seal } = accountset.keys();
120
+ const commands = [];
121
+ const data = [
122
+ {
123
+ jsonrpc: "2.0",
124
+ id: 0,
125
+ method: "author_insertKey",
126
+ params: ["gran", gran.privateKey, gran.publicKey]
127
+ },
128
+ {
129
+ jsonrpc: "2.0",
130
+ id: 1,
131
+ method: "author_insertKey",
132
+ params: ["seal", seal.privateKey, seal.publicKey]
133
+ }
134
+ ];
135
+ for (const key of data) {
136
+ commands.push(
137
+ `curl -X POST -H "Content-Type: application/json" -d '${JSON.stringify(key)}' ${nodeRpcUrl}`
138
+ );
139
+ }
140
+ console.log(commands.join(" && "));
141
+ } else {
142
+ await accountset.registerKeys(nodeRpcUrl);
143
+ }
144
+ process2.exit();
145
+ });
146
+ program.command("consolidate").description("Consolidate all argons into parent account").option(
147
+ "-s, --subaccounts <range>",
148
+ "Restrict this operation to a subset of the subaccounts (eg, 0-10)",
149
+ parseSubaccountRange
150
+ ).action(async ({ subaccounts }) => {
151
+ const accountset = await accountsetFromCli(program);
152
+ const result = await accountset.consolidate(subaccounts);
153
+ printTable(result);
154
+ process2.exit(0);
155
+ });
156
+ return program;
157
+ }
158
+
159
+ // src/clis/index.ts
160
+ import { configDotenv } from "dotenv";
161
+ import Path from "node:path";
162
+
163
+ // src/clis/vaultCli.ts
164
+ import { Command as Command2 } from "@commander-js/extra-typings";
165
+ function vaultCli() {
166
+ const program = new Command2("vaults").description(
167
+ "Monitor vaults and manage securitization"
168
+ );
169
+ program.command("list", { isDefault: true }).description("Show current state of vaults").action(async () => {
170
+ const accountset = await accountsetFromCli(program);
171
+ const vaults = new VaultMonitor(accountset, void 0, {
172
+ vaultOnlyWatchMode: true
173
+ });
174
+ await vaults.monitor(true);
175
+ process.exit(0);
176
+ });
177
+ program.command("modify-securitization").description("Change the vault securitization ratio").requiredOption("-v, --vault-id <id>", "The vault id to use", parseInt).requiredOption(
178
+ "-a, --argons <amount>",
179
+ "The number of argons to set as securitization",
180
+ parseFloat
181
+ ).option("--ratio <ratio>", "The new securitization ratio", parseFloat).option(
182
+ "--tip <amount>",
183
+ "The tip to include with the transaction",
184
+ parseFloat
185
+ ).action(async ({ tip, argons, vaultId, ratio }) => {
186
+ const accountset = await accountsetFromCli(program);
187
+ const client = await accountset.client;
188
+ const resolvedTip = tip ? BigInt(tip * MICROGONS_PER_ARGON) : 0n;
189
+ const microgons = BigInt(argons * MICROGONS_PER_ARGON);
190
+ const rawVault = (await client.query.vaults.vaultsById(vaultId)).unwrap();
191
+ if (rawVault.operatorAccountId.toHuman() !== accountset.seedAddress) {
192
+ console.error("Vault does not belong to this account");
193
+ process.exit(1);
194
+ }
195
+ const existingFunds = rawVault.securitization.toBigInt();
196
+ const additionalFunds = microgons > existingFunds ? microgons - existingFunds : 0n;
197
+ const tx = client.tx.vaults.modifyFunding(
198
+ vaultId,
199
+ microgons,
200
+ ratio !== void 0 ? BigNumber(ratio).times(BigNumber(2).pow(64)).toFixed(0) : rawVault.securitizationRatio.toBigInt()
201
+ );
202
+ const submit = new TxSubmitter(client, tx, accountset.txSubmitterPair);
203
+ const canAfford = await submit.canAfford({
204
+ tip: resolvedTip,
205
+ unavailableBalance: additionalFunds
206
+ });
207
+ if (!canAfford.canAfford) {
208
+ console.warn("Insufficient balance to modify vault securitization", {
209
+ ...canAfford,
210
+ addedSecuritization: additionalFunds
211
+ });
212
+ process.exit(1);
213
+ }
214
+ try {
215
+ const result = await submit.submit({ tip: resolvedTip });
216
+ await result.inBlockPromise;
217
+ console.log("Vault securitization modified");
218
+ process.exit();
219
+ } catch (error) {
220
+ console.error("Error modifying vault securitization", error);
221
+ process.exit(1);
222
+ }
223
+ });
224
+ program.command("make-bitcoin-space").description(
225
+ "Make bitcoin space in a vault and lock it immediately in the same tx."
226
+ ).requiredOption("-v, --vault-id <id>", "The vault id to use", parseInt).requiredOption(
227
+ "-a, --argons <amount>",
228
+ "The number of argons to add",
229
+ parseFloat
230
+ ).requiredOption(
231
+ "--bitcoin-pubkey <pubkey>",
232
+ "The pubkey to use for the bitcoin lock"
233
+ ).option(
234
+ "--tip <amount>",
235
+ "The tip to include with the transaction",
236
+ parseFloat
237
+ ).action(async ({ tip, argons, vaultId, bitcoinPubkey }) => {
238
+ let pubkey = bitcoinPubkey;
239
+ if (!bitcoinPubkey.startsWith("0x")) {
240
+ pubkey = `0x${bitcoinPubkey}`;
241
+ }
242
+ if (pubkey.length !== 68) {
243
+ throw new Error(
244
+ "Bitcoin pubkey must be 66 characters (add 0x in front optionally)"
245
+ );
246
+ }
247
+ const accountset = await accountsetFromCli(program);
248
+ const client = await accountset.client;
249
+ const resolvedTip = tip ? BigInt(tip * MICROGONS_PER_ARGON) : 0n;
250
+ const microgons = BigInt(argons * MICROGONS_PER_ARGON);
251
+ const bitcoinLocks = new BitcoinLocks(Promise.resolve(client));
252
+ const existentialDeposit = client.consts.balances.existentialDeposit.toBigInt();
253
+ const tickDuration = (await client.query.ticks.genesisTicker()).tickDurationMillis.toNumber();
254
+ const rawVault = (await client.query.vaults.vaultsById(vaultId)).unwrap();
255
+ if (rawVault.operatorAccountId.toHuman() !== accountset.seedAddress) {
256
+ console.error("Vault does not belong to this account");
257
+ process.exit(1);
258
+ }
259
+ const vaultModifyTx = client.tx.vaults.modifyFunding(
260
+ vaultId,
261
+ microgons,
262
+ rawVault.securitizationRatio.toBigInt()
263
+ );
264
+ const vaultTxFee = (await vaultModifyTx.paymentInfo(accountset.txSubmitterPair)).partialFee.toBigInt();
265
+ const vault = new Vault(vaultId, rawVault, tickDuration);
266
+ const argonsNeeded = microgons - vault.securitization;
267
+ const argonsAvailable = microgons - vault.availableBitcoinSpace();
268
+ const account = await client.query.system.account(accountset.seedAddress);
269
+ const freeBalance = account.data.free.toBigInt();
270
+ const {
271
+ tx: lockTx,
272
+ btcFee,
273
+ txFee
274
+ } = await bitcoinLocks.buildBitcoinLockTx({
275
+ vaultId,
276
+ keypair: accountset.txSubmitterPair,
277
+ amount: argonsAvailable,
278
+ bitcoinXpub: pubkey,
279
+ tip: resolvedTip,
280
+ reducedBalanceBy: argonsNeeded + vaultTxFee + resolvedTip
281
+ });
282
+ if (argonsNeeded + txFee + vaultTxFee + resolvedTip + btcFee + existentialDeposit > freeBalance) {
283
+ console.warn(
284
+ "Insufficient balance to add bitcoin space and use bitcoins",
285
+ {
286
+ freeBalance,
287
+ txFee,
288
+ vaultTxFee,
289
+ btcFee,
290
+ argonsAvailable,
291
+ vaultMicrogons: microgons,
292
+ existentialDeposit,
293
+ neededBalanceAboveED: argonsNeeded + txFee + resolvedTip + btcFee + vaultTxFee
294
+ }
295
+ );
296
+ process.exit(1);
297
+ }
298
+ console.log("Adding bitcoin space and locking bitcoins...", {
299
+ newArgonsAvailable: argonsAvailable,
300
+ txFee,
301
+ vaultTxFee,
302
+ btcFee,
303
+ resolvedTip
304
+ });
305
+ const txSubmitter = new TxSubmitter(
306
+ client,
307
+ client.tx.utility.batchAll([vaultModifyTx, lockTx]),
308
+ accountset.txSubmitterPair
309
+ );
310
+ const result = await txSubmitter.submit({ tip: resolvedTip });
311
+ try {
312
+ await result.inBlockPromise;
313
+ console.log("Bitcoin space done");
314
+ } catch (error) {
315
+ console.error("Error using bitcoin space", error);
316
+ process.exit(1);
317
+ }
318
+ });
319
+ return program;
320
+ }
321
+
322
+ // src/clis/miningCli.ts
323
+ import { Command as Command3 } from "@commander-js/extra-typings";
324
+ import { printTable as printTable2 } from "console-table-printer";
325
+ function miningCli() {
326
+ const program = new Command3("mining").description(
327
+ "Watch mining seats or setup bidding"
328
+ );
329
+ program.command("list", { isDefault: true }).description("Monitor all miners").action(async () => {
330
+ const accountset = await accountsetFromCli(program);
331
+ const bids = new MiningBids(accountset.client);
332
+ const api = await accountset.client;
333
+ let lastMiners = {};
334
+ function print(blockNumber) {
335
+ console.clear();
336
+ const toPrint = Object.entries(lastMiners).map(([seat, miner]) => ({
337
+ seat,
338
+ ...miner
339
+ }));
340
+ if (!toPrint.length) {
341
+ console.log("No active miners");
342
+ } else {
343
+ console.log(`Miners at block ${blockNumber}`);
344
+ printTable2(
345
+ toPrint.map((x) => ({
346
+ ...x,
347
+ bid: x.bid ? formatArgons(x.bid) : "-",
348
+ isLastDay: x.isLastDay ? "Y" : "",
349
+ miner: x.miner
350
+ }))
351
+ );
352
+ }
353
+ if (!bids.nextCohort.length) {
354
+ console.log(
355
+ "-------------------------------------\nNo bids for next cohort"
356
+ );
357
+ } else {
358
+ bids.print();
359
+ }
360
+ }
361
+ const { unsubscribe } = await bids.watch(
362
+ accountset.namedAccounts,
363
+ void 0,
364
+ print
365
+ );
366
+ console.log("Watching miners...");
367
+ const minMiners = api.consts.miningSlot.minCohortSize.toNumber();
368
+ const unsub = await api.query.miningSlot.nextFrameId(
369
+ async (nextFrameId) => {
370
+ const frames = new Array(nextFrameId.toNumber()).fill(0).map((_, i) => nextFrameId.toNumber() - i).sort();
371
+ const unseenFrames = new Set(frames);
372
+ const entries = await api.query.miningSlot.minersByCohort.entries();
373
+ const block = await api.query.system.number();
374
+ const sortedEntries = entries.sort((a, b) => {
375
+ const aIndex = a[0].args[0].toNumber();
376
+ const bIndex = b[0].args[0].toNumber();
377
+ return aIndex - bIndex;
378
+ });
379
+ for (const [rawFrameId, miners] of sortedEntries) {
380
+ const frameId = rawFrameId.args[0].toNumber();
381
+ unseenFrames.delete(frameId);
382
+ let i = 0;
383
+ for (const miner of miners) {
384
+ const address = miner.accountId.toHuman();
385
+ const startingFrameId = miner.startingFrameId.toNumber();
386
+ lastMiners[`${frameId}-${i}`] = {
387
+ miner: accountset.namedAccounts.get(address) ?? address,
388
+ bid: miner.bid.toBigInt(),
389
+ isLastDay: nextFrameId.toNumber() - startingFrameId === 10
390
+ };
391
+ i++;
392
+ }
393
+ while (i < minMiners) {
394
+ lastMiners[`${frameId}-${i}`] = {
395
+ miner: "none"
396
+ };
397
+ i++;
398
+ }
399
+ }
400
+ for (const frameId of unseenFrames) {
401
+ for (let i = 0; i < minMiners; i++) {
402
+ lastMiners[`${frameId}-${i}`] = {
403
+ miner: "none"
404
+ };
405
+ }
406
+ }
407
+ print(block.toNumber());
408
+ }
409
+ );
410
+ process.on("SIGINT", () => {
411
+ unsubscribe();
412
+ unsub();
413
+ process.exit(0);
414
+ });
415
+ });
416
+ program.command("bid").description("Submit mining bids within a range of parameters").option("--min-bid <amount>", "The minimum bid amount to use", parseFloat).option("--max-bid <amount>", "The maximum bid amount to use", parseFloat).option(
417
+ "--max-seats <n>",
418
+ "The maximum number of seats to bid on for the slot",
419
+ parseInt
420
+ ).option(
421
+ "--max-balance <argons>",
422
+ "Use a maximum amount of the user's balance for the slot. If this ends in a percent, it will be a percent of the funds"
423
+ ).option("--bid-increment <argons>", "The bid increment", parseFloat, 0.01).option("--bid-delay <ticks>", "Delay between bids in ticks", parseInt, 0).option("--run-continuous", "Keep running and rebid every day").option(
424
+ "--proxy-for-address <address>",
425
+ "The seed account to proxy for (eg: 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)"
426
+ ).action(
427
+ async ({
428
+ maxSeats,
429
+ runContinuous,
430
+ maxBid,
431
+ minBid,
432
+ maxBalance,
433
+ bidDelay,
434
+ bidIncrement,
435
+ proxyForAddress
436
+ }) => {
437
+ const accountset = await accountsetFromCli(program, proxyForAddress);
438
+ let cohortBidder;
439
+ const miningBids = new MiningBids(accountset.client, false);
440
+ const maxCohortSize = await miningBids.maxCohortSize();
441
+ const stopBidder = async (unsubscribe2) => {
442
+ if (cohortBidder) {
443
+ const stats = await cohortBidder.stop();
444
+ console.log("Final bidding result", {
445
+ cohortStartingFrameId: cohortBidder.cohortStartingFrameId,
446
+ ...stats
447
+ });
448
+ cohortBidder = void 0;
449
+ if (!runContinuous) {
450
+ unsubscribe2();
451
+ process.exit();
452
+ }
453
+ }
454
+ };
455
+ const { unsubscribe } = await miningBids.onCohortChange({
456
+ async onBiddingEnd(cohortStartingFrameId) {
457
+ if (cohortBidder?.cohortStartingFrameId === cohortStartingFrameId) {
458
+ await stopBidder(unsubscribe);
459
+ }
460
+ },
461
+ async onBiddingStart(cohortStartingFrameId) {
462
+ const seatsToWin = maxSeats ?? maxCohortSize;
463
+ const balance = await accountset.balance();
464
+ const feeWiggleRoom = BigInt(25e3);
465
+ const amountAvailable = balance - feeWiggleRoom;
466
+ let maxBidAmount = maxBid ? BigInt(maxBid * MICROGONS_PER_ARGON) : void 0;
467
+ let maxBalanceToUse = amountAvailable;
468
+ if (maxBalance !== void 0) {
469
+ if (maxBalance.endsWith("%")) {
470
+ let maxBalancePercent = parseInt(maxBalance);
471
+ let amountToBid = amountAvailable * BigInt(maxBalancePercent) / 100n;
472
+ if (amountToBid > balance) {
473
+ amountToBid = balance;
474
+ }
475
+ maxBalanceToUse = amountToBid;
476
+ } else {
477
+ maxBalanceToUse = BigInt(
478
+ Math.floor(parseFloat(maxBalance) * MICROGONS_PER_ARGON)
479
+ );
480
+ }
481
+ maxBidAmount ??= maxBalanceToUse / BigInt(seatsToWin);
482
+ }
483
+ if (maxBalanceToUse > amountAvailable) {
484
+ maxBalanceToUse = amountAvailable;
485
+ }
486
+ if (!maxBidAmount) {
487
+ console.error("No max bid amount set");
488
+ process.exit(1);
489
+ }
490
+ const subaccountRange = await accountset.getAvailableMinerAccounts(seatsToWin);
491
+ if (cohortBidder && cohortBidder?.cohortStartingFrameId !== cohortStartingFrameId) {
492
+ await stopBidder(unsubscribe);
493
+ }
494
+ cohortBidder = new CohortBidder(
495
+ accountset,
496
+ cohortStartingFrameId,
497
+ subaccountRange,
498
+ {
499
+ maxBid: maxBidAmount,
500
+ minBid: BigInt((minBid ?? 0) * MICROGONS_PER_ARGON),
501
+ bidIncrement: BigInt(
502
+ Math.floor(bidIncrement * MICROGONS_PER_ARGON)
503
+ ),
504
+ maxBudget: maxBalanceToUse,
505
+ bidDelay
506
+ }
507
+ );
508
+ await cohortBidder.start();
509
+ }
510
+ });
511
+ }
512
+ );
513
+ program.command("create-bid-proxy").description("Create a mining-bid proxy account for your main account").requiredOption(
514
+ "--outfile <path>",
515
+ "The file to use to store the proxy account json (eg: proxy.json)"
516
+ ).requiredOption(
517
+ "--fee-argons <argons>",
518
+ "How many argons should be sent to the proxy account for fees (proxies must pay fees)",
519
+ parseFloat
520
+ ).option(
521
+ "--proxy-passphrase <passphrase>",
522
+ "The passphrase for your proxy account"
523
+ ).action(async ({ outfile, proxyPassphrase, feeArgons }) => {
524
+ const { mainchainUrl } = globalOptions(program);
525
+ const client = await getClient(mainchainUrl);
526
+ const keyringPair = await saveKeyringPair({
527
+ filePath: outfile,
528
+ passphrase: proxyPassphrase
529
+ });
530
+ const address = keyringPair.address;
531
+ console.log(
532
+ `\u2705 Created proxy account at "${outfile}" with address ${address}`
533
+ );
534
+ const tx = client.tx.utility.batchAll([
535
+ client.tx.proxy.addProxy(address, "MiningBid", 0),
536
+ client.tx.balances.transferAllowDeath(
537
+ address,
538
+ BigInt(feeArgons * MICROGONS_PER_ARGON)
539
+ )
540
+ ]);
541
+ let keypair;
542
+ try {
543
+ const accountset = await accountsetFromCli(program);
544
+ keypair = accountset.txSubmitterPair;
545
+ } catch (e) {
546
+ const polkadotLink = `https://polkadot.js.org/apps/?rpc=${mainchainUrl}#/extrinsics/decode/${tx.toHex()}`;
547
+ console.log(`Complete the registration at this link:`, polkadotLink);
548
+ process.exit(0);
549
+ }
550
+ try {
551
+ await new TxSubmitter(client, tx, keypair).submit({
552
+ waitForBlock: true
553
+ });
554
+ console.log("Mining bid proxy added and funded.");
555
+ process.exit();
556
+ } catch (error) {
557
+ console.error("Error adding mining proxy", error);
558
+ process.exit(1);
559
+ }
560
+ });
561
+ return program;
562
+ }
563
+
564
+ // src/clis/liquidityCli.ts
565
+ import { Command as Command4 } from "@commander-js/extra-typings";
566
+ function liquidityCli() {
567
+ const program = new Command4("liquidity-pools").description(
568
+ "Monitor or bond to liquidity pools"
569
+ );
570
+ program.command("list", { isDefault: true }).description("Show or watch the vault bid pool rewards").action(async () => {
571
+ const accountset = await accountsetFromCli(program);
572
+ const bidPool = new BidPool(
573
+ accountset.client,
574
+ accountset.txSubmitterPair
575
+ );
576
+ await bidPool.watch();
577
+ });
578
+ program.command("bond").description("Bond argons to a liquidity pool").requiredOption("-v, --vault-id <id>", "The vault id to use", parseInt).requiredOption(
579
+ "-a, --argons <amount>",
580
+ "The number of argons to set the vault to",
581
+ parseFloat
582
+ ).option(
583
+ "--tip <amount>",
584
+ "The tip to include with the transaction",
585
+ parseFloat
586
+ ).action(async ({ tip, argons, vaultId }) => {
587
+ const accountset = await accountsetFromCli(program);
588
+ const resolvedTip = tip ? BigInt(tip * MICROGONS_PER_ARGON) : 0n;
589
+ const microgons = BigInt(argons * MICROGONS_PER_ARGON);
590
+ const bidPool = new BidPool(
591
+ accountset.client,
592
+ accountset.txSubmitterPair
593
+ );
594
+ await bidPool.bondArgons(vaultId, microgons, { tip: resolvedTip });
595
+ console.log("Bonded argons to liquidity pool bond");
596
+ process.exit();
597
+ });
598
+ program.command("wait-for-space").description(
599
+ "Add bonded argons to a liquidity pool when the market rate is favorable"
600
+ ).requiredOption(
601
+ "--max-argons <amount>",
602
+ "Max daily argons to use per slot",
603
+ parseFloat
604
+ ).option(
605
+ "--min-pct-sharing <percent>",
606
+ "The minimum profit sharing percent to allow",
607
+ parseInt,
608
+ 30
609
+ ).option(
610
+ "--tip <amount>",
611
+ "The tip to include with the transaction",
612
+ parseFloat
613
+ ).action(async ({ maxArgons, minPctSharing, tip }) => {
614
+ const maxAmountPerSlot = BigInt(maxArgons * MICROGONS_PER_ARGON);
615
+ const accountset = await accountsetFromCli(program);
616
+ const vaults = new VaultMonitor(
617
+ accountset,
618
+ {
619
+ liquidityPoolSpaceAvailable: 1000000n
620
+ },
621
+ { shouldLog: false }
622
+ );
623
+ const bidPool = new BidPool(
624
+ accountset.client,
625
+ accountset.txSubmitterPair
626
+ );
627
+ const resolvedTip = tip ? BigInt(tip * MICROGONS_PER_ARGON) : 0n;
628
+ console.log("Waiting for liquidity pool space...");
629
+ vaults.events.on(
630
+ "liquidity-pool-space-above",
631
+ async (vaultId, amount) => {
632
+ const vault = vaults.vaultsById[vaultId];
633
+ if (vault.terms.liquidityPoolProfitSharing.times(100).toNumber() < minPctSharing) {
634
+ console.info(
635
+ `Skipping vault ${vaultId} due to lower profit sharing than ${minPctSharing}%`
636
+ );
637
+ return;
638
+ }
639
+ let amountToAdd = amount;
640
+ if (amountToAdd > maxAmountPerSlot) {
641
+ amountToAdd = maxAmountPerSlot;
642
+ }
643
+ await bidPool.bondArgons(vaultId, amountToAdd, { tip: resolvedTip });
644
+ console.log("Bonding argons to vault liquidity pool", {
645
+ vaultId,
646
+ amount: formatArgons(amountToAdd)
647
+ });
648
+ }
649
+ );
650
+ await vaults.monitor();
651
+ });
652
+ return program;
653
+ }
654
+
655
+ // src/clis/bitcoinCli.ts
656
+ import { Command as Command5 } from "@commander-js/extra-typings";
657
+ function bitcoinCli() {
658
+ const program = new Command5("bitcoin").description("Wait for bitcoin space");
659
+ program.command("watch").requiredOption(
660
+ "-a, --argons <argons>",
661
+ "Alert when bitcoin space exceeds this amount",
662
+ parseFloat
663
+ ).description("Watch for bitcoin space available").action(async ({ argons }) => {
664
+ const accountset = await accountsetFromCli(program);
665
+ const bot = new VaultMonitor(accountset, {
666
+ bitcoinSpaceAvailable: argons ? BigInt(argons * MICROGONS_PER_ARGON) : 1n
667
+ });
668
+ bot.events.on("bitcoin-space-above", async (vaultId, amount) => {
669
+ const vault = bot.vaultsById[vaultId];
670
+ const fee = vault.calculateBitcoinFee(amount);
671
+ const ratio = 100n * fee / amount;
672
+ console.log(
673
+ `Vault ${vaultId} has ${formatArgons(amount)} argons available for bitcoin. Fee ratio is ${ratio}%`
674
+ );
675
+ });
676
+ await bot.monitor();
677
+ });
678
+ program.command("wait-for-space").description("Lock bitcoin when available at a given rate").requiredOption(
679
+ "-a, --argons <amount>",
680
+ "Bitcoin argons needed. NOTE: your account must have enough to cover fees + tip after this amount.",
681
+ parseFloat
682
+ ).requiredOption(
683
+ "--bitcoin-xpub <xpub>",
684
+ "The xpub key to use for bitcoin locking"
685
+ ).option(
686
+ "--max-lock-fee <argons>",
687
+ "The max lock fee you're willing to pay",
688
+ parseFloat
689
+ ).option(
690
+ "--tip <amount>",
691
+ "The tip to include with the transaction",
692
+ parseFloat,
693
+ 0
694
+ ).action(async ({ argons, bitcoinXpub, maxLockFee, tip }) => {
695
+ const amountToLock = BigInt(argons * MICROGONS_PER_ARGON);
696
+ const accountset = await accountsetFromCli(program);
697
+ await BitcoinLocks.waitForSpace(accountset, {
698
+ argonAmount: amountToLock,
699
+ bitcoinXpub,
700
+ maxLockFee: maxLockFee !== void 0 ? BigInt(maxLockFee * MICROGONS_PER_ARGON) : void 0,
701
+ tip: BigInt(tip * MICROGONS_PER_ARGON)
702
+ }).then(({ vaultId, satoshis, txFee, btcFee }) => {
703
+ console.log(
704
+ `Locked ${satoshis} satoshis in vault ${vaultId}. Tx fee=${formatArgons(
705
+ txFee
706
+ )}, Lock fee=${formatArgons(btcFee)}.`
707
+ );
708
+ process.exit(0);
709
+ });
710
+ });
711
+ return program;
712
+ }
713
+
714
+ // src/clis/keyringStore.ts
715
+ import { promises } from "node:fs";
716
+ import * as os from "node:os";
717
+ var { readFile, writeFile } = promises;
718
+ async function keyringFromFile(opts) {
719
+ if (!opts.filePath) {
720
+ throw new Error(
721
+ "No ACCOUNT account loaded (either ACCOUNT_SURI or ACCOUNT_JSON_PATH required)"
722
+ );
723
+ }
724
+ const path = opts.filePath.replace("~", os.homedir());
725
+ const json = JSON.parse(await readFile(path, "utf-8"));
726
+ let passphrase = opts.passphrase;
727
+ if (opts.passphraseFile) {
728
+ const passphrasePath = opts.passphraseFile.replace("~", os.homedir());
729
+ passphrase = await readFile(passphrasePath, "utf-8");
730
+ }
731
+ const mainAccount = new Keyring().createFromJson(json);
732
+ mainAccount.decodePkcs8(passphrase);
733
+ return mainAccount;
734
+ }
735
+ async function saveKeyringPair(opts) {
736
+ const { filePath, passphrase, cryptoType } = opts;
737
+ const keyring = createKeyringPair({ cryptoType });
738
+ if (filePath) {
739
+ const json = keyring.toJson(passphrase);
740
+ await writeFile(filePath, JSON.stringify(json, null, 2));
741
+ }
742
+ return keyring;
743
+ }
744
+
745
+ // src/clis/index.ts
746
+ function globalOptions(program) {
747
+ return program.optsWithGlobals();
748
+ }
749
+ function buildCli() {
750
+ return new Command6("Argon CLI").option("-e, --env <path>", "The path to the account .env file to load").addOption(
751
+ new Option("-u, --mainchain-url <url>", "The mainchain URL to connect to").default("wss://rpc.argon.network").env("MAINCHAIN_URL")
752
+ ).addOption(
753
+ new Option(
754
+ "--account-file-path <jsonPath>",
755
+ "The path to your json seed file from polkadotjs"
756
+ ).env("ACCOUNT_JSON_PATH")
757
+ ).addOption(
758
+ new Option(
759
+ "--account-suri <secretUri>",
760
+ "A secret uri (suri) to use for the account"
761
+ ).env("ACCOUNT_SURI")
762
+ ).addOption(
763
+ new Option(
764
+ "--account-passphrase <password>",
765
+ "The password for your seed file"
766
+ ).env("ACCOUNT_PASSPHRASE")
767
+ ).addOption(
768
+ new Option(
769
+ "--account-passphrase-file <path>",
770
+ "The path to a password for your seed file"
771
+ )
772
+ ).addOption(
773
+ new Option(
774
+ "-s, --subaccounts <range>",
775
+ "Restrict this operation to a subset of the subaccounts (eg, 0-10)"
776
+ ).env("SUBACCOUNT_RANGE").argParser(parseSubaccountRange)
777
+ ).addCommand(accountCli()).addCommand(vaultCli()).addCommand(miningCli()).addCommand(liquidityCli()).addCommand(bitcoinCli());
778
+ }
779
+ async function accountsetFromCli(program, proxyForAddress) {
780
+ const opts = program.parent?.optsWithGlobals();
781
+ let keypair;
782
+ if (opts.accountSuri) {
783
+ keypair = keyringFromSuri(opts.accountSuri);
784
+ }
785
+ if (opts.accountFilePath) {
786
+ keypair = await keyringFromFile({
787
+ filePath: opts.accountFilePath,
788
+ passphrase: opts.accountPassphrase,
789
+ passphraseFile: opts.accountPassphraseFile
790
+ });
791
+ }
792
+ if (!keypair) {
793
+ throw new Error(
794
+ "No ACCOUNT account loaded (either ACCOUNT_SURI or ACCOUNT_JSON_PATH required)"
795
+ );
796
+ }
797
+ const client = getClient(opts.mainchainUrl);
798
+ if (proxyForAddress) {
799
+ return new Accountset({
800
+ client,
801
+ isProxy: true,
802
+ seedAddress: proxyForAddress,
803
+ txSubmitter: keypair
804
+ });
805
+ } else {
806
+ return new Accountset({
807
+ seedAccount: keypair,
808
+ client
809
+ });
810
+ }
811
+ }
812
+ function addGlobalArgs(program) {
813
+ for (const command of program.commands) {
814
+ command.configureHelp({
815
+ showGlobalOptions: true
816
+ });
817
+ for (const nested of command.commands) {
818
+ nested.configureHelp({
819
+ showGlobalOptions: true
820
+ });
821
+ }
822
+ }
823
+ }
824
+ function applyEnv(program) {
825
+ program.parseOptions(process.argv);
826
+ const { env: env2 } = program.optsWithGlobals();
827
+ if (env2) {
828
+ const envPath = Path.resolve(process.cwd(), env2);
829
+ const res = configDotenv({ path: envPath });
830
+ if (res.parsed?.ACCOUNT_JSON_PATH) {
831
+ process.env.ACCOUNT_JSON_PATH = Path.resolve(
832
+ Path.dirname(envPath),
833
+ process.env.ACCOUNT_JSON_PATH
834
+ );
835
+ }
836
+ }
837
+ return env2;
838
+ }
839
+
840
+ export {
841
+ accountCli,
842
+ vaultCli,
843
+ miningCli,
844
+ liquidityCli,
845
+ bitcoinCli,
846
+ keyringFromFile,
847
+ saveKeyringPair,
848
+ globalOptions,
849
+ buildCli,
850
+ accountsetFromCli,
851
+ addGlobalArgs,
852
+ applyEnv
853
+ };
854
+ //# sourceMappingURL=chunk-BQR6FEVP.js.map