@secondlayer/cli 9.8.0 → 9.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +184 -7
- package/dist/cli.js.map +8 -7
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -34817,7 +34817,7 @@ var {
|
|
|
34817
34817
|
// package.json
|
|
34818
34818
|
var package_default = {
|
|
34819
34819
|
name: "@secondlayer/cli",
|
|
34820
|
-
version: "9.
|
|
34820
|
+
version: "9.9.0",
|
|
34821
34821
|
repository: {
|
|
34822
34822
|
type: "git",
|
|
34823
34823
|
url: "git+https://github.com/ryanwaits/secondlayer.git",
|
|
@@ -34861,8 +34861,8 @@ var package_default = {
|
|
|
34861
34861
|
"@secondlayer/bundler": "^0.4.1",
|
|
34862
34862
|
"@secondlayer/scaffold": "^1.5.1",
|
|
34863
34863
|
"@secondlayer/sdk": "^6.51.0",
|
|
34864
|
-
"@secondlayer/shared": "^7.
|
|
34865
|
-
"@secondlayer/stacks": "^3.
|
|
34864
|
+
"@secondlayer/shared": "^7.8.0",
|
|
34865
|
+
"@secondlayer/stacks": "^3.3.1",
|
|
34866
34866
|
"@secondlayer/subgraphs": "^3.26.4",
|
|
34867
34867
|
esbuild: "^0.27.4",
|
|
34868
34868
|
"smol-toml": "^1.6.1",
|
|
@@ -38638,6 +38638,11 @@ import { getDb as getDb2 } from "@secondlayer/shared/db";
|
|
|
38638
38638
|
import { createHash as createHash2 } from "node:crypto";
|
|
38639
38639
|
import { readFile as readFile4 } from "node:fs/promises";
|
|
38640
38640
|
import { dirname as dirname16, join as join13, resolve as resolve12 } from "node:path";
|
|
38641
|
+
import {
|
|
38642
|
+
checkKeyTrust,
|
|
38643
|
+
publicKeyFor,
|
|
38644
|
+
verifyRegistry
|
|
38645
|
+
} from "@secondlayer/shared/archive/key-registry";
|
|
38641
38646
|
import { verifyStreamsBulkManifestSignature } from "@secondlayer/shared/streams-bulk-manifest";
|
|
38642
38647
|
function isLatestPointer(value) {
|
|
38643
38648
|
const pointer = value;
|
|
@@ -39143,7 +39148,7 @@ import {
|
|
|
39143
39148
|
writeRowsToCopyStream
|
|
39144
39149
|
} from "@secondlayer/shared/archive/copy-loader";
|
|
39145
39150
|
import { computeRangeDigest as computeRangeDigest3 } from "@secondlayer/shared/archive/range-digest";
|
|
39146
|
-
import { getDb as getDb4, getRawClient } from "@secondlayer/shared/db";
|
|
39151
|
+
import { getDb as getDb4, getRawClient, sql as sql3 } from "@secondlayer/shared/db";
|
|
39147
39152
|
init_output();
|
|
39148
39153
|
var BOOTSTRAP_EXIT = {
|
|
39149
39154
|
OK: 0,
|
|
@@ -39152,6 +39157,20 @@ var BOOTSTRAP_EXIT = {
|
|
|
39152
39157
|
};
|
|
39153
39158
|
var BLOCKS_PER_DAY_ESTIMATE = 5400;
|
|
39154
39159
|
var RESTORE_ROWS_PER_SECOND = 30000;
|
|
39160
|
+
async function readNodeTip() {
|
|
39161
|
+
const url = process.env.STACKS_NODE_RPC_URL ?? "http://localhost:20443";
|
|
39162
|
+
try {
|
|
39163
|
+
const response2 = await fetch(`${url.replace(/\/$/, "")}/v2/info`, {
|
|
39164
|
+
signal: AbortSignal.timeout(5000)
|
|
39165
|
+
});
|
|
39166
|
+
if (!response2.ok)
|
|
39167
|
+
return null;
|
|
39168
|
+
const info2 = await response2.json();
|
|
39169
|
+
return typeof info2.stacks_tip_height === "number" ? info2.stacks_tip_height : null;
|
|
39170
|
+
} catch {
|
|
39171
|
+
return null;
|
|
39172
|
+
}
|
|
39173
|
+
}
|
|
39155
39174
|
function formatDuration2(seconds2) {
|
|
39156
39175
|
if (seconds2 < 90)
|
|
39157
39176
|
return `${Math.round(seconds2)}s`;
|
|
@@ -39256,6 +39275,7 @@ Exit codes:
|
|
|
39256
39275
|
process.exit(BOOTSTRAP_EXIT.REFUSED);
|
|
39257
39276
|
}
|
|
39258
39277
|
}
|
|
39278
|
+
const nodeTipAtStart = await readNodeTip();
|
|
39259
39279
|
const order = ["blocks", "transactions", "events"];
|
|
39260
39280
|
const rawClient = getRawClient("source");
|
|
39261
39281
|
const loaded = { blocks: 0, transactions: 0, events: 0 };
|
|
@@ -39279,6 +39299,24 @@ Exit codes:
|
|
|
39279
39299
|
divergent++;
|
|
39280
39300
|
}
|
|
39281
39301
|
}
|
|
39302
|
+
const network = process.env.STACKS_NETWORK ?? "mainnet";
|
|
39303
|
+
await sql3`
|
|
39304
|
+
INSERT INTO index_progress (
|
|
39305
|
+
network, last_indexed_block, last_contiguous_block,
|
|
39306
|
+
highest_seen_block, updated_at
|
|
39307
|
+
) VALUES (
|
|
39308
|
+
${network}, ${tipHeight}, ${tipHeight}, ${tipHeight}, NOW()
|
|
39309
|
+
)
|
|
39310
|
+
ON CONFLICT (network) DO UPDATE SET
|
|
39311
|
+
last_indexed_block = GREATEST(index_progress.last_indexed_block, EXCLUDED.last_indexed_block),
|
|
39312
|
+
last_contiguous_block = GREATEST(index_progress.last_contiguous_block, EXCLUDED.last_contiguous_block),
|
|
39313
|
+
highest_seen_block = GREATEST(index_progress.highest_seen_block, EXCLUDED.highest_seen_block),
|
|
39314
|
+
updated_at = NOW()
|
|
39315
|
+
`.execute(db);
|
|
39316
|
+
const seam = nodeTipAtStart !== null && nodeTipAtStart > tipHeight ? {
|
|
39317
|
+
nodeTip: nodeTipAtStart,
|
|
39318
|
+
gap: nodeTipAtStart - tipHeight
|
|
39319
|
+
} : null;
|
|
39282
39320
|
const elapsed = (Date.now() - startedAt) / 1000;
|
|
39283
39321
|
const report = {
|
|
39284
39322
|
status: divergent === 0 ? "restored" : "divergent",
|
|
@@ -39287,7 +39325,10 @@ Exit codes:
|
|
|
39287
39325
|
rows: loaded,
|
|
39288
39326
|
ranges_verified: referenceDigests.length,
|
|
39289
39327
|
divergent_ranges: divergent,
|
|
39290
|
-
elapsed_seconds: Math.round(elapsed)
|
|
39328
|
+
elapsed_seconds: Math.round(elapsed),
|
|
39329
|
+
resume_from: tipHeight + 1,
|
|
39330
|
+
node_tip_at_start: nodeTipAtStart,
|
|
39331
|
+
catch_up_blocks: seam?.gap ?? null
|
|
39291
39332
|
};
|
|
39292
39333
|
output({
|
|
39293
39334
|
json: opts.json,
|
|
@@ -39301,7 +39342,13 @@ Exit codes:
|
|
|
39301
39342
|
warn(" archive published no block digests — restore is unverified");
|
|
39302
39343
|
}
|
|
39303
39344
|
console.error("");
|
|
39304
|
-
console.error(dim(`Your instance holds history through ${tipHeight.toLocaleString()}
|
|
39345
|
+
console.error(dim(`Your instance holds history through ${tipHeight.toLocaleString()} and will resume at ${(tipHeight + 1).toLocaleString()}.`));
|
|
39346
|
+
if (seam) {
|
|
39347
|
+
console.error(dim(`The chain advanced to ${seam.nodeTip.toLocaleString()} while restoring — ${seam.gap.toLocaleString()} blocks to catch up.`));
|
|
39348
|
+
console.error(dim(" Start the indexer; it backfills that range before following the tip."));
|
|
39349
|
+
} else {
|
|
39350
|
+
console.error(dim(" Start the indexer to continue from there. (Node unreachable — catch-up range unknown.)"));
|
|
39351
|
+
}
|
|
39305
39352
|
} else {
|
|
39306
39353
|
warn(`Restored ${loaded.blocks.toLocaleString()} blocks but ${divergent} ranges do not match the archive.`);
|
|
39307
39354
|
console.error(dim(" Investigate with: sl verify --against <manifest>"));
|
|
@@ -39317,6 +39364,135 @@ Exit codes:
|
|
|
39317
39364
|
}
|
|
39318
39365
|
});
|
|
39319
39366
|
}
|
|
39367
|
+
// src/commands/faucet.ts
|
|
39368
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
39369
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
39370
|
+
import { join as join16 } from "node:path";
|
|
39371
|
+
import { http as http3, createWalletClient } from "@secondlayer/stacks";
|
|
39372
|
+
import { mnemonicToAccount } from "@secondlayer/stacks/accounts";
|
|
39373
|
+
import { devnet } from "@secondlayer/stacks/chains";
|
|
39374
|
+
import { Cl } from "@secondlayer/stacks/clarity";
|
|
39375
|
+
import { parse as parseToml2 } from "smol-toml";
|
|
39376
|
+
init_output();
|
|
39377
|
+
var DUST_LIMIT_SATS = 546n;
|
|
39378
|
+
var DEFAULT_AMOUNT_SATS = 1000000n;
|
|
39379
|
+
async function readDeployerMnemonic(projectDir) {
|
|
39380
|
+
const path11 = join16(projectDir, "settings", "Devnet.toml");
|
|
39381
|
+
let raw;
|
|
39382
|
+
try {
|
|
39383
|
+
raw = await readFile5(path11, "utf8");
|
|
39384
|
+
} catch {
|
|
39385
|
+
throw new Error(`could not read ${path11}`);
|
|
39386
|
+
}
|
|
39387
|
+
const parsed = parseToml2(raw);
|
|
39388
|
+
const mnemonic = parsed.accounts?.deployer?.mnemonic;
|
|
39389
|
+
if (!mnemonic) {
|
|
39390
|
+
throw new Error(`no accounts.deployer.mnemonic in ${path11}`);
|
|
39391
|
+
}
|
|
39392
|
+
return mnemonic;
|
|
39393
|
+
}
|
|
39394
|
+
async function readBurnAnchor(rpcUrl) {
|
|
39395
|
+
const info2 = await (await fetch(`${rpcUrl.replace(/\/$/, "")}/v2/info`, {
|
|
39396
|
+
signal: AbortSignal.timeout(1e4)
|
|
39397
|
+
})).json();
|
|
39398
|
+
const height = info2.burn_block_height;
|
|
39399
|
+
if (typeof height !== "number") {
|
|
39400
|
+
throw new Error("node did not report a burn block height");
|
|
39401
|
+
}
|
|
39402
|
+
const burn = await (await fetch(`${rpcUrl.replace(/\/$/, "")}/extended/v2/burn-blocks/${height}`, { signal: AbortSignal.timeout(1e4) })).json();
|
|
39403
|
+
if (!burn.burn_block_hash) {
|
|
39404
|
+
throw new Error(`node did not report a hash for burn block ${height}`);
|
|
39405
|
+
}
|
|
39406
|
+
return { height, hash: burn.burn_block_hash.replace(/^0x/, "") };
|
|
39407
|
+
}
|
|
39408
|
+
function parseAmount(value) {
|
|
39409
|
+
if (value === undefined)
|
|
39410
|
+
return DEFAULT_AMOUNT_SATS;
|
|
39411
|
+
const parsed = BigInt(value);
|
|
39412
|
+
if (parsed < DUST_LIMIT_SATS) {
|
|
39413
|
+
throw new Error(`--amount must be at least ${DUST_LIMIT_SATS} sats (the protocol dust limit), got ${parsed}`);
|
|
39414
|
+
}
|
|
39415
|
+
return parsed;
|
|
39416
|
+
}
|
|
39417
|
+
function registerFaucetCommand(program2) {
|
|
39418
|
+
const faucet = program2.command("faucet").description("Mint test assets on a local devnet");
|
|
39419
|
+
faucet.command("sbtc").description("Mint mock sBTC to an address on your local devnet").requiredOption("--to <address>", "recipient Stacks address").option("--amount <sats>", `amount in sats (default ${DEFAULT_AMOUNT_SATS}, minimum ${DUST_LIMIT_SATS})`).option("--contract <id>", "sBTC deposit contract (default: <deployer>.sbtc-deposit)").option("--rpc-url <url>", "devnet node RPC (default http://localhost:3999)").option("--json", "Output as JSON").addHelpText("after", `
|
|
39420
|
+
Examples:
|
|
39421
|
+
$ sl faucet sbtc --to ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM
|
|
39422
|
+
$ sl faucet sbtc --to ST2... --amount 5000000
|
|
39423
|
+
|
|
39424
|
+
Requires a devnet running with the sBTC contract set deployed by your
|
|
39425
|
+
Clarinet deployer — that deployer is the signer set on your own chain, which
|
|
39426
|
+
is what makes minting possible without Bitcoin.`).action(async (opts) => {
|
|
39427
|
+
try {
|
|
39428
|
+
const network = process.env.STACKS_NETWORK ?? "devnet";
|
|
39429
|
+
if (network !== "devnet" && network !== "local") {
|
|
39430
|
+
printError(`sl faucet only works on devnet, not ${network}.`, {
|
|
39431
|
+
hint: "Mock sBTC is mintable because you deployed the contracts. On testnet or mainnet the signer set is not yours."
|
|
39432
|
+
});
|
|
39433
|
+
process.exit(2);
|
|
39434
|
+
}
|
|
39435
|
+
const rpcUrl = opts.rpcUrl ?? "http://localhost:3999";
|
|
39436
|
+
const amount = parseAmount(opts.amount);
|
|
39437
|
+
const projectDir = findClarinetProject(process.cwd());
|
|
39438
|
+
if (!projectDir) {
|
|
39439
|
+
printError("No Clarinet project found.", {
|
|
39440
|
+
hint: "Run this from a project containing Clarinet.toml, or pass --contract explicitly."
|
|
39441
|
+
});
|
|
39442
|
+
process.exit(2);
|
|
39443
|
+
}
|
|
39444
|
+
const mnemonic = await readDeployerMnemonic(projectDir);
|
|
39445
|
+
const account = mnemonicToAccount(mnemonic, {
|
|
39446
|
+
addressVersion: devnet.addressVersion.singleSig
|
|
39447
|
+
});
|
|
39448
|
+
const contract = opts.contract ?? `${account.address}.sbtc-deposit`;
|
|
39449
|
+
const anchor = await readBurnAnchor(rpcUrl);
|
|
39450
|
+
const client3 = createWalletClient({
|
|
39451
|
+
account,
|
|
39452
|
+
chain: devnet,
|
|
39453
|
+
transport: http3(rpcUrl)
|
|
39454
|
+
});
|
|
39455
|
+
const depositTxid = randomBytes2(32).toString("hex");
|
|
39456
|
+
const sweepTxid = randomBytes2(32).toString("hex");
|
|
39457
|
+
note(` minting ${amount} sats to ${opts.to}`);
|
|
39458
|
+
note(` via ${contract} as signer ${account.address}`);
|
|
39459
|
+
const txid = await client3.callContract({
|
|
39460
|
+
contract,
|
|
39461
|
+
functionName: "complete-deposit-wrapper",
|
|
39462
|
+
functionArgs: [
|
|
39463
|
+
Cl.bufferFromHex(depositTxid),
|
|
39464
|
+
Cl.uint(0),
|
|
39465
|
+
Cl.uint(amount),
|
|
39466
|
+
Cl.principal(opts.to),
|
|
39467
|
+
Cl.bufferFromHex(anchor.hash),
|
|
39468
|
+
Cl.uint(anchor.height),
|
|
39469
|
+
Cl.bufferFromHex(sweepTxid)
|
|
39470
|
+
],
|
|
39471
|
+
postConditionMode: "allow"
|
|
39472
|
+
});
|
|
39473
|
+
output({
|
|
39474
|
+
json: opts.json,
|
|
39475
|
+
data: {
|
|
39476
|
+
txid,
|
|
39477
|
+
recipient: opts.to,
|
|
39478
|
+
amount_sats: amount.toString(),
|
|
39479
|
+
contract,
|
|
39480
|
+
burn_height: anchor.height
|
|
39481
|
+
},
|
|
39482
|
+
human: () => {
|
|
39483
|
+
success(`Minted ${amount} sats of mock sBTC to ${opts.to}.`);
|
|
39484
|
+
note(` tx ${txid}`);
|
|
39485
|
+
console.error(dim(" Mock sBTC on your devnet — not the canonical asset."));
|
|
39486
|
+
}
|
|
39487
|
+
});
|
|
39488
|
+
} catch (err) {
|
|
39489
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
39490
|
+
const hint = /ECONNREFUSED|fetch failed/.test(message) ? "Is your devnet running? Start it with `clarinet devnet start`." : /NoSuchContract|does not exist/.test(message) ? "Deploy the sBTC contract set to your devnet first, or pass --contract." : "Check that your Clarinet deployer matches the sBTC contract deployer.";
|
|
39491
|
+
printError(message, { hint });
|
|
39492
|
+
process.exit(1);
|
|
39493
|
+
}
|
|
39494
|
+
});
|
|
39495
|
+
}
|
|
39320
39496
|
// src/cli.ts
|
|
39321
39497
|
var { version } = package_default;
|
|
39322
39498
|
program.name("secondlayer").alias("sl").description("SecondLayer CLI — dedicated Stacks indexing + real-time subgraphs").version(version).option("--network <network>", "Override network (local, testnet, mainnet)").option("--api-key <key>", "API credential (overrides SL_API_KEY)").option("--api-url <url>", "API endpoint (overrides SL_API_URL)").showSuggestionAfterError(true).showHelpAfterError("(run `sl --help` to see available commands)");
|
|
@@ -39368,6 +39544,7 @@ Examples:
|
|
|
39368
39544
|
program.commandsGroup("Local development:");
|
|
39369
39545
|
registerLocalCommand(program);
|
|
39370
39546
|
registerDevnetCommand(program);
|
|
39547
|
+
registerFaucetCommand(program);
|
|
39371
39548
|
program.commandsGroup("Diagnostics:");
|
|
39372
39549
|
registerStatusCommand(program);
|
|
39373
39550
|
registerDoctorCommand(program);
|
|
@@ -39381,5 +39558,5 @@ registerAccountCommand(program);
|
|
|
39381
39558
|
registerKeysCommand(program);
|
|
39382
39559
|
program.parse();
|
|
39383
39560
|
|
|
39384
|
-
//# debugId=
|
|
39561
|
+
//# debugId=F913417C10273C0264756E2164756E21
|
|
39385
39562
|
//# sourceMappingURL=cli.js.map
|