@secondlayer/cli 9.9.0 → 9.10.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 +172 -170
- package/dist/cli.js.map +7 -7
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -33160,12 +33160,12 @@ __export(exports_dev_state, {
|
|
|
33160
33160
|
clearDevState: () => clearDevState
|
|
33161
33161
|
});
|
|
33162
33162
|
import { homedir as homedir4 } from "node:os";
|
|
33163
|
-
import { join as
|
|
33163
|
+
import { join as join11 } from "node:path";
|
|
33164
33164
|
function getLogsDir() {
|
|
33165
33165
|
return LOGS_DIR;
|
|
33166
33166
|
}
|
|
33167
33167
|
function getLogFile(service) {
|
|
33168
|
-
return
|
|
33168
|
+
return join11(LOGS_DIR, `${service}.log`);
|
|
33169
33169
|
}
|
|
33170
33170
|
async function ensureDirs() {
|
|
33171
33171
|
await Bun.$`mkdir -p ${STATE_DIR}`.quiet();
|
|
@@ -33222,9 +33222,9 @@ async function isDevRunning() {
|
|
|
33222
33222
|
}
|
|
33223
33223
|
var STATE_DIR, DEV_STATE_PATH, LOGS_DIR;
|
|
33224
33224
|
var init_dev_state = __esm(() => {
|
|
33225
|
-
STATE_DIR =
|
|
33226
|
-
DEV_STATE_PATH =
|
|
33227
|
-
LOGS_DIR =
|
|
33225
|
+
STATE_DIR = join11(homedir4(), ".secondlayer");
|
|
33226
|
+
DEV_STATE_PATH = join11(STATE_DIR, "dev.json");
|
|
33227
|
+
LOGS_DIR = join11(STATE_DIR, "logs");
|
|
33228
33228
|
});
|
|
33229
33229
|
|
|
33230
33230
|
// src/lib/network.ts
|
|
@@ -33708,7 +33708,7 @@ __export(exports_dev_impl, {
|
|
|
33708
33708
|
isDevAlreadyRunning: () => isDevAlreadyRunning
|
|
33709
33709
|
});
|
|
33710
33710
|
import { mkdirSync as mkdirSync5 } from "node:fs";
|
|
33711
|
-
import { dirname as dirname14, join as
|
|
33711
|
+
import { dirname as dirname14, join as join12, resolve as resolve10 } from "node:path";
|
|
33712
33712
|
async function isDevAlreadyRunning() {
|
|
33713
33713
|
if (await isDevRunning()) {
|
|
33714
33714
|
const running = await getRunningServices();
|
|
@@ -34317,7 +34317,7 @@ async function ensureDevPostgres(dataDir) {
|
|
|
34317
34317
|
if (check.stdout.toString().trim()) {
|
|
34318
34318
|
return false;
|
|
34319
34319
|
}
|
|
34320
|
-
const pgDataDir =
|
|
34320
|
+
const pgDataDir = join12(dataDir, "postgres");
|
|
34321
34321
|
mkdirSync5(pgDataDir, { recursive: true });
|
|
34322
34322
|
const stopped = await Bun.$`docker ps -aq -f name=secondlayer-dev-postgres`.quiet().nothrow();
|
|
34323
34323
|
if (stopped.stdout.toString().trim()) {
|
|
@@ -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.10.0",
|
|
34821
34821
|
repository: {
|
|
34822
34822
|
type: "git",
|
|
34823
34823
|
url: "git+https://github.com/ryanwaits/secondlayer.git",
|
|
@@ -36522,7 +36522,7 @@ POST result (client-side, not logged):`));
|
|
|
36522
36522
|
// src/commands/devnet.ts
|
|
36523
36523
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
36524
36524
|
import { existsSync as existsSync6, mkdirSync as mkdirSync4, writeFileSync as writeFileSync6 } from "node:fs";
|
|
36525
|
-
import { dirname as dirname8, join as
|
|
36525
|
+
import { dirname as dirname8, join as join9 } from "node:path";
|
|
36526
36526
|
|
|
36527
36527
|
// src/lib/devnet-compose.ts
|
|
36528
36528
|
var DEFAULT_IMAGE_OWNER = "ryanwaits";
|
|
@@ -36719,7 +36719,138 @@ stacks_node_events_observers = [${entry}]
|
|
|
36719
36719
|
|
|
36720
36720
|
// src/commands/devnet.ts
|
|
36721
36721
|
init_output();
|
|
36722
|
-
|
|
36722
|
+
|
|
36723
|
+
// src/commands/faucet.ts
|
|
36724
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
36725
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
36726
|
+
import { join as join8 } from "node:path";
|
|
36727
|
+
import { http as http3, createWalletClient } from "@secondlayer/stacks";
|
|
36728
|
+
import { mnemonicToAccount } from "@secondlayer/stacks/accounts";
|
|
36729
|
+
import { devnet } from "@secondlayer/stacks/chains";
|
|
36730
|
+
import { Cl } from "@secondlayer/stacks/clarity";
|
|
36731
|
+
import { parse as parseToml2 } from "smol-toml";
|
|
36732
|
+
init_output();
|
|
36733
|
+
var DUST_LIMIT_SATS = 546n;
|
|
36734
|
+
var DEFAULT_AMOUNT_SATS = 1000000n;
|
|
36735
|
+
async function readDeployerMnemonic(projectDir) {
|
|
36736
|
+
const path11 = join8(projectDir, "settings", "Devnet.toml");
|
|
36737
|
+
let raw;
|
|
36738
|
+
try {
|
|
36739
|
+
raw = await readFile3(path11, "utf8");
|
|
36740
|
+
} catch {
|
|
36741
|
+
throw new Error(`could not read ${path11}`);
|
|
36742
|
+
}
|
|
36743
|
+
const parsed = parseToml2(raw);
|
|
36744
|
+
const mnemonic = parsed.accounts?.deployer?.mnemonic;
|
|
36745
|
+
if (!mnemonic) {
|
|
36746
|
+
throw new Error(`no accounts.deployer.mnemonic in ${path11}`);
|
|
36747
|
+
}
|
|
36748
|
+
return mnemonic;
|
|
36749
|
+
}
|
|
36750
|
+
async function readBurnAnchor(rpcUrl) {
|
|
36751
|
+
const info2 = await (await fetch(`${rpcUrl.replace(/\/$/, "")}/v2/info`, {
|
|
36752
|
+
signal: AbortSignal.timeout(1e4)
|
|
36753
|
+
})).json();
|
|
36754
|
+
const height = info2.burn_block_height;
|
|
36755
|
+
if (typeof height !== "number") {
|
|
36756
|
+
throw new Error("node did not report a burn block height");
|
|
36757
|
+
}
|
|
36758
|
+
const burn = await (await fetch(`${rpcUrl.replace(/\/$/, "")}/extended/v2/burn-blocks/${height}`, { signal: AbortSignal.timeout(1e4) })).json();
|
|
36759
|
+
if (!burn.burn_block_hash) {
|
|
36760
|
+
throw new Error(`node did not report a hash for burn block ${height}`);
|
|
36761
|
+
}
|
|
36762
|
+
return { height, hash: burn.burn_block_hash.replace(/^0x/, "") };
|
|
36763
|
+
}
|
|
36764
|
+
function parseAmount(value) {
|
|
36765
|
+
if (value === undefined)
|
|
36766
|
+
return DEFAULT_AMOUNT_SATS;
|
|
36767
|
+
const parsed = BigInt(value);
|
|
36768
|
+
if (parsed < DUST_LIMIT_SATS) {
|
|
36769
|
+
throw new Error(`--amount must be at least ${DUST_LIMIT_SATS} sats (the protocol dust limit), got ${parsed}`);
|
|
36770
|
+
}
|
|
36771
|
+
return parsed;
|
|
36772
|
+
}
|
|
36773
|
+
function registerFaucetCommand(devnetCommand) {
|
|
36774
|
+
devnetCommand.command("faucet").description("Mint mock sBTC to an address on your 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", `
|
|
36775
|
+
Examples:
|
|
36776
|
+
$ sl devnet faucet --to ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM
|
|
36777
|
+
$ sl devnet faucet --to ST2... --amount 5000000
|
|
36778
|
+
|
|
36779
|
+
Requires a devnet running with the sBTC contract set deployed by your
|
|
36780
|
+
Clarinet deployer — that deployer is the signer set on your own chain, which
|
|
36781
|
+
is what makes minting possible without Bitcoin.`).action(async (opts) => {
|
|
36782
|
+
try {
|
|
36783
|
+
const network = process.env.STACKS_NETWORK ?? "devnet";
|
|
36784
|
+
if (network !== "devnet" && network !== "local") {
|
|
36785
|
+
printError(`sl devnet faucet only works on devnet, not ${network}.`, {
|
|
36786
|
+
hint: "Mock sBTC is mintable because you deployed the contracts. On testnet or mainnet the signer set is not yours."
|
|
36787
|
+
});
|
|
36788
|
+
process.exit(2);
|
|
36789
|
+
}
|
|
36790
|
+
const rpcUrl = opts.rpcUrl ?? "http://localhost:3999";
|
|
36791
|
+
const amount = parseAmount(opts.amount);
|
|
36792
|
+
const projectDir = findClarinetProject(process.cwd());
|
|
36793
|
+
if (!projectDir) {
|
|
36794
|
+
printError("No Clarinet project found.", {
|
|
36795
|
+
hint: "Run this from a project containing Clarinet.toml, or pass --contract explicitly."
|
|
36796
|
+
});
|
|
36797
|
+
process.exit(2);
|
|
36798
|
+
}
|
|
36799
|
+
const mnemonic = await readDeployerMnemonic(projectDir);
|
|
36800
|
+
const account = mnemonicToAccount(mnemonic, {
|
|
36801
|
+
addressVersion: devnet.addressVersion.singleSig
|
|
36802
|
+
});
|
|
36803
|
+
const contract = opts.contract ?? `${account.address}.sbtc-deposit`;
|
|
36804
|
+
const anchor = await readBurnAnchor(rpcUrl);
|
|
36805
|
+
const client2 = createWalletClient({
|
|
36806
|
+
account,
|
|
36807
|
+
chain: devnet,
|
|
36808
|
+
transport: http3(rpcUrl)
|
|
36809
|
+
});
|
|
36810
|
+
const depositTxid = randomBytes2(32).toString("hex");
|
|
36811
|
+
const sweepTxid = randomBytes2(32).toString("hex");
|
|
36812
|
+
note(` minting ${amount} sats to ${opts.to}`);
|
|
36813
|
+
note(` via ${contract} as signer ${account.address}`);
|
|
36814
|
+
const txid = await client2.callContract({
|
|
36815
|
+
contract,
|
|
36816
|
+
functionName: "complete-deposit-wrapper",
|
|
36817
|
+
functionArgs: [
|
|
36818
|
+
Cl.bufferFromHex(depositTxid),
|
|
36819
|
+
Cl.uint(0),
|
|
36820
|
+
Cl.uint(amount),
|
|
36821
|
+
Cl.principal(opts.to),
|
|
36822
|
+
Cl.bufferFromHex(anchor.hash),
|
|
36823
|
+
Cl.uint(anchor.height),
|
|
36824
|
+
Cl.bufferFromHex(sweepTxid)
|
|
36825
|
+
],
|
|
36826
|
+
postConditionMode: "allow"
|
|
36827
|
+
});
|
|
36828
|
+
output({
|
|
36829
|
+
json: opts.json,
|
|
36830
|
+
data: {
|
|
36831
|
+
txid,
|
|
36832
|
+
recipient: opts.to,
|
|
36833
|
+
amount_sats: amount.toString(),
|
|
36834
|
+
contract,
|
|
36835
|
+
burn_height: anchor.height
|
|
36836
|
+
},
|
|
36837
|
+
human: () => {
|
|
36838
|
+
success(`Minted ${amount} sats of mock sBTC to ${opts.to}.`);
|
|
36839
|
+
note(` tx ${txid}`);
|
|
36840
|
+
console.error(dim(" Mock sBTC on your devnet — not the canonical asset."));
|
|
36841
|
+
}
|
|
36842
|
+
});
|
|
36843
|
+
} catch (err) {
|
|
36844
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
36845
|
+
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.";
|
|
36846
|
+
printError(message, { hint });
|
|
36847
|
+
process.exit(1);
|
|
36848
|
+
}
|
|
36849
|
+
});
|
|
36850
|
+
}
|
|
36851
|
+
|
|
36852
|
+
// src/commands/devnet.ts
|
|
36853
|
+
var COMPOSE_REL = join9(".secondlayer", "docker-compose.yml");
|
|
36723
36854
|
function ensureDocker() {
|
|
36724
36855
|
const probe = spawnSync2("docker", ["info"], { stdio: "ignore" });
|
|
36725
36856
|
if (probe.error || probe.status !== 0) {
|
|
@@ -36747,23 +36878,24 @@ var SERVICES = [
|
|
|
36747
36878
|
"migrate"
|
|
36748
36879
|
];
|
|
36749
36880
|
function registerDevnetCommand(program2) {
|
|
36750
|
-
const
|
|
36751
|
-
|
|
36881
|
+
const devnet2 = program2.command("devnet").description("Run Secondlayer services against a local Clarinet devnet");
|
|
36882
|
+
registerFaucetCommand(devnet2);
|
|
36883
|
+
devnet2.command("connect").description("Point your clarinet project's devnet at a local Secondlayer stack and start it").option("--project <dir>", "Clarinet project directory (defaults to the nearest Clarinet.toml)").option("--image-tag <tag>", "Published image tag to run", DEFAULT_IMAGE_TAG).option("--owner <owner>", "ghcr image owner (namespace) to pull from").option("--no-up", "Patch config + write compose without starting docker").action(async (options3) => {
|
|
36752
36884
|
await connect(options3);
|
|
36753
36885
|
});
|
|
36754
|
-
|
|
36886
|
+
devnet2.command("down").description("Stop the local Secondlayer stack started by `sl devnet connect`").option("--project <dir>", "Clarinet project directory").option("--purge", "Also remove volumes (wipes the local index)").action(async (options3) => {
|
|
36755
36887
|
await down(options3);
|
|
36756
36888
|
});
|
|
36757
|
-
|
|
36889
|
+
devnet2.command("status").description("Snapshot of the local stack: ingest, subgraphs, and recent activity").option("-w, --watch", "Refresh every 2s until Ctrl-C").option("--limit <n>", "Recent activity rows to show", "12").action(async (options3) => {
|
|
36758
36890
|
await status(options3);
|
|
36759
36891
|
});
|
|
36760
|
-
|
|
36892
|
+
devnet2.command("logs [service]").description("Tail stack logs — all services, or one of: indexer, api, subgraph-processor, postgres").option("--project <dir>", "Clarinet project directory").option("-f, --follow", "Follow log output").option("-n, --lines <n>", "Lines to show from the end of each log").action(async (service, options3) => {
|
|
36761
36893
|
await logs(service, options3);
|
|
36762
36894
|
});
|
|
36763
36895
|
}
|
|
36764
36896
|
function resolveProject(explicit) {
|
|
36765
36897
|
if (explicit) {
|
|
36766
|
-
if (!existsSync6(
|
|
36898
|
+
if (!existsSync6(join9(explicit, "Clarinet.toml"))) {
|
|
36767
36899
|
error(`No Clarinet.toml in ${explicit}`);
|
|
36768
36900
|
process.exit(1);
|
|
36769
36901
|
}
|
|
@@ -36779,7 +36911,7 @@ function resolveProject(explicit) {
|
|
|
36779
36911
|
}
|
|
36780
36912
|
async function connect(options3) {
|
|
36781
36913
|
const project = resolveProject(options3.project);
|
|
36782
|
-
const devnetToml =
|
|
36914
|
+
const devnetToml = join9(project, "settings", "Devnet.toml");
|
|
36783
36915
|
mkdirSync4(dirname8(devnetToml), { recursive: true });
|
|
36784
36916
|
const result = ensureEventObserver(devnetToml, DEFAULT_INDEXER_OBSERVER);
|
|
36785
36917
|
console.log(`${green("✓")} found Clarinet.toml`);
|
|
@@ -36789,7 +36921,7 @@ async function connect(options3) {
|
|
|
36789
36921
|
console.log(`${green("✓")} ${result.status === "created" ? "created" : "patched"} settings/Devnet.toml`);
|
|
36790
36922
|
console.log(dim(` + stacks_node_events_observers = ["${DEFAULT_INDEXER_OBSERVER}"]`));
|
|
36791
36923
|
}
|
|
36792
|
-
const composePath =
|
|
36924
|
+
const composePath = join9(project, COMPOSE_REL);
|
|
36793
36925
|
mkdirSync4(dirname8(composePath), { recursive: true });
|
|
36794
36926
|
writeFileSync6(composePath, buildDevnetCompose({ owner: options3.owner, imageTag: options3.imageTag }));
|
|
36795
36927
|
console.log(`${green("✓")} wrote ${COMPOSE_REL}`);
|
|
@@ -36819,7 +36951,7 @@ Stop with: sl devnet down`));
|
|
|
36819
36951
|
}
|
|
36820
36952
|
async function down(options3) {
|
|
36821
36953
|
const project = resolveProject(options3.project);
|
|
36822
|
-
const composePath =
|
|
36954
|
+
const composePath = join9(project, COMPOSE_REL);
|
|
36823
36955
|
if (!existsSync6(composePath)) {
|
|
36824
36956
|
error(`No ${COMPOSE_REL} in ${project} — nothing to stop. Run \`sl devnet connect\` first.`);
|
|
36825
36957
|
process.exit(1);
|
|
@@ -36834,7 +36966,7 @@ async function down(options3) {
|
|
|
36834
36966
|
}
|
|
36835
36967
|
async function logs(service, options3) {
|
|
36836
36968
|
const project = resolveProject(options3.project);
|
|
36837
|
-
const composePath =
|
|
36969
|
+
const composePath = join9(project, COMPOSE_REL);
|
|
36838
36970
|
if (!existsSync6(composePath)) {
|
|
36839
36971
|
error(`No ${COMPOSE_REL} in ${project} — run \`sl devnet connect\` first.`);
|
|
36840
36972
|
process.exit(1);
|
|
@@ -36960,7 +37092,7 @@ init_subgraphs2();
|
|
|
36960
37092
|
init_output();
|
|
36961
37093
|
init_resolve_auth();
|
|
36962
37094
|
import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
|
|
36963
|
-
import { dirname as dirname9, join as
|
|
37095
|
+
import { dirname as dirname9, join as join10 } from "node:path";
|
|
36964
37096
|
import { createStreamsClient } from "@secondlayer/sdk";
|
|
36965
37097
|
import { STREAMS_EVENT_TYPES } from "@secondlayer/shared";
|
|
36966
37098
|
var DEFAULT_BASE_URL3 = "https://api.secondlayer.tools";
|
|
@@ -37136,7 +37268,7 @@ Examples:
|
|
|
37136
37268
|
note(`# downloading ${files.length} file(s) to ${options3.to}`);
|
|
37137
37269
|
for (const file of files) {
|
|
37138
37270
|
const bytes = await dumps.download(file);
|
|
37139
|
-
const dest =
|
|
37271
|
+
const dest = join10(options3.to, file.path);
|
|
37140
37272
|
await mkdir3(dirname9(dest), { recursive: true });
|
|
37141
37273
|
await writeFile3(dest, bytes);
|
|
37142
37274
|
process.stderr.write(`# ${file.path} (${file.row_count} rows, ${bytes.byteLength} bytes)
|
|
@@ -38424,9 +38556,9 @@ init_output();
|
|
|
38424
38556
|
|
|
38425
38557
|
// src/lib/project-file.ts
|
|
38426
38558
|
import { existsSync as existsSync7 } from "node:fs";
|
|
38427
|
-
import { mkdir as mkdir4, readFile as
|
|
38559
|
+
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
|
|
38428
38560
|
import { homedir as homedir5 } from "node:os";
|
|
38429
|
-
import { dirname as dirname15, join as
|
|
38561
|
+
import { dirname as dirname15, join as join13, parse as parsePath2, resolve as resolve11 } from "node:path";
|
|
38430
38562
|
import { z as z4 } from "zod";
|
|
38431
38563
|
var ProjectFileSchema = z4.object({
|
|
38432
38564
|
slug: z4.string().min(1)
|
|
@@ -38438,15 +38570,15 @@ async function readActiveProject(cwd, globalDefault) {
|
|
|
38438
38570
|
const home2 = resolve11(homedir5());
|
|
38439
38571
|
const fsRoot = parsePath2(dir).root;
|
|
38440
38572
|
while (true) {
|
|
38441
|
-
const candidate =
|
|
38573
|
+
const candidate = join13(dir, DIRNAME, FILENAME);
|
|
38442
38574
|
if (existsSync7(candidate)) {
|
|
38443
38575
|
try {
|
|
38444
|
-
const raw = await
|
|
38576
|
+
const raw = await readFile4(candidate, "utf8");
|
|
38445
38577
|
const parsed = ProjectFileSchema.parse(JSON.parse(raw));
|
|
38446
38578
|
return { slug: parsed.slug, resolvedFrom: candidate };
|
|
38447
38579
|
} catch {}
|
|
38448
38580
|
}
|
|
38449
|
-
if (existsSync7(
|
|
38581
|
+
if (existsSync7(join13(dir, ".git")))
|
|
38450
38582
|
break;
|
|
38451
38583
|
if (dir === home2 || dir === fsRoot)
|
|
38452
38584
|
break;
|
|
@@ -38458,15 +38590,15 @@ async function readActiveProject(cwd, globalDefault) {
|
|
|
38458
38590
|
if (globalDefault) {
|
|
38459
38591
|
return {
|
|
38460
38592
|
slug: globalDefault,
|
|
38461
|
-
resolvedFrom:
|
|
38593
|
+
resolvedFrom: join13(homedir5(), ".secondlayer", "config.json")
|
|
38462
38594
|
};
|
|
38463
38595
|
}
|
|
38464
38596
|
return null;
|
|
38465
38597
|
}
|
|
38466
38598
|
async function writeActiveProject(slug, cwd) {
|
|
38467
|
-
const dir =
|
|
38599
|
+
const dir = join13(resolve11(cwd), DIRNAME);
|
|
38468
38600
|
await mkdir4(dir, { recursive: true });
|
|
38469
|
-
const file =
|
|
38601
|
+
const file = join13(dir, FILENAME);
|
|
38470
38602
|
await writeFile4(file, `${JSON.stringify({ slug }, null, 2)}
|
|
38471
38603
|
`, "utf8");
|
|
38472
38604
|
return file;
|
|
@@ -38636,8 +38768,8 @@ import { getDb as getDb2 } from "@secondlayer/shared/db";
|
|
|
38636
38768
|
|
|
38637
38769
|
// src/lib/archive-reference.ts
|
|
38638
38770
|
import { createHash as createHash2 } from "node:crypto";
|
|
38639
|
-
import { readFile as
|
|
38640
|
-
import { dirname as dirname16, join as
|
|
38771
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
38772
|
+
import { dirname as dirname16, join as join14, resolve as resolve12 } from "node:path";
|
|
38641
38773
|
import {
|
|
38642
38774
|
checkKeyTrust,
|
|
38643
38775
|
publicKeyFor,
|
|
@@ -38666,7 +38798,7 @@ async function loadReference(source) {
|
|
|
38666
38798
|
root = url.toString();
|
|
38667
38799
|
} else {
|
|
38668
38800
|
const path11 = resolve12(source);
|
|
38669
|
-
manifest = JSON.parse(await
|
|
38801
|
+
manifest = JSON.parse(await readFile5(path11, "utf8"));
|
|
38670
38802
|
root = isLatestPointer(manifest) ? dirname16(path11) : dirname16(dirname16(path11));
|
|
38671
38803
|
}
|
|
38672
38804
|
if (!isLatestPointer(manifest)) {
|
|
@@ -38674,7 +38806,7 @@ async function loadReference(source) {
|
|
|
38674
38806
|
}
|
|
38675
38807
|
const pointer = manifest;
|
|
38676
38808
|
const snapshotPath = pointer.snapshot_path;
|
|
38677
|
-
const resolved = isRemote ? await fetchJson(`${root.replace(/\/$/, "")}/${snapshotPath}`) : JSON.parse(await
|
|
38809
|
+
const resolved = isRemote ? await fetchJson(`${root.replace(/\/$/, "")}/${snapshotPath}`) : JSON.parse(await readFile5(join14(root, snapshotPath), "utf8"));
|
|
38678
38810
|
if (pointer.snapshot_digest) {
|
|
38679
38811
|
const digest = createHash2("sha256").update(JSON.stringify((({ signature: _s, key_id: _k, ...rest }) => rest)(resolved))).digest("hex");
|
|
38680
38812
|
if (digest !== pointer.snapshot_digest) {
|
|
@@ -38731,7 +38863,7 @@ async function fetchVerifiedPartition(reference, partition) {
|
|
|
38731
38863
|
}
|
|
38732
38864
|
bytes = Buffer.from(await response2.arrayBuffer());
|
|
38733
38865
|
} else {
|
|
38734
|
-
bytes = await
|
|
38866
|
+
bytes = await readFile5(join14(reference.root, partition.path));
|
|
38735
38867
|
}
|
|
38736
38868
|
const digest = createHash2("sha256").update(bytes).digest("hex");
|
|
38737
38869
|
if (digest !== partition.sha256) {
|
|
@@ -38891,7 +39023,7 @@ Exit codes:
|
|
|
38891
39023
|
// src/commands/repair.ts
|
|
38892
39024
|
import { unlink as unlink2, writeFile as writeFile5 } from "node:fs/promises";
|
|
38893
39025
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
38894
|
-
import { join as
|
|
39026
|
+
import { join as join15 } from "node:path";
|
|
38895
39027
|
import { ParquetReader } from "@dsnp/parquetjs";
|
|
38896
39028
|
import {
|
|
38897
39029
|
compareRangeDigests as compareRangeDigests2,
|
|
@@ -38914,7 +39046,7 @@ function asOptionalText(value) {
|
|
|
38914
39046
|
return value === null || value === undefined ? null : asText(value);
|
|
38915
39047
|
}
|
|
38916
39048
|
async function readBlocksPartition(bytes, label) {
|
|
38917
|
-
const path11 =
|
|
39049
|
+
const path11 = join15(tmpdir2(), `sl-repair-${label}-${process.pid}.parquet`);
|
|
38918
39050
|
await writeFile5(path11, bytes);
|
|
38919
39051
|
try {
|
|
38920
39052
|
const reader = await ParquetReader.openFile(path11);
|
|
@@ -39140,7 +39272,7 @@ Exit codes:
|
|
|
39140
39272
|
// src/commands/bootstrap.ts
|
|
39141
39273
|
import { unlink as unlink3, writeFile as writeFile6 } from "node:fs/promises";
|
|
39142
39274
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
39143
|
-
import { join as
|
|
39275
|
+
import { join as join16 } from "node:path";
|
|
39144
39276
|
import { ParquetReader as ParquetReader2 } from "@dsnp/parquetjs";
|
|
39145
39277
|
import { confirm as confirm7 } from "@inquirer/prompts";
|
|
39146
39278
|
import {
|
|
@@ -39181,7 +39313,7 @@ function formatDuration2(seconds2) {
|
|
|
39181
39313
|
return `${(seconds2 / 86400).toFixed(1)} days`;
|
|
39182
39314
|
}
|
|
39183
39315
|
async function* readPartitionRows(bytes, label) {
|
|
39184
|
-
const path11 =
|
|
39316
|
+
const path11 = join16(tmpdir3(), `sl-bootstrap-${label}-${process.pid}.parquet`);
|
|
39185
39317
|
await writeFile6(path11, bytes);
|
|
39186
39318
|
try {
|
|
39187
39319
|
const reader = await ParquetReader2.openFile(path11);
|
|
@@ -39364,135 +39496,6 @@ Exit codes:
|
|
|
39364
39496
|
}
|
|
39365
39497
|
});
|
|
39366
39498
|
}
|
|
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
|
-
}
|
|
39496
39499
|
// src/cli.ts
|
|
39497
39500
|
var { version } = package_default;
|
|
39498
39501
|
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)");
|
|
@@ -39544,7 +39547,6 @@ Examples:
|
|
|
39544
39547
|
program.commandsGroup("Local development:");
|
|
39545
39548
|
registerLocalCommand(program);
|
|
39546
39549
|
registerDevnetCommand(program);
|
|
39547
|
-
registerFaucetCommand(program);
|
|
39548
39550
|
program.commandsGroup("Diagnostics:");
|
|
39549
39551
|
registerStatusCommand(program);
|
|
39550
39552
|
registerDoctorCommand(program);
|
|
@@ -39558,5 +39560,5 @@ registerAccountCommand(program);
|
|
|
39558
39560
|
registerKeysCommand(program);
|
|
39559
39561
|
program.parse();
|
|
39560
39562
|
|
|
39561
|
-
//# debugId=
|
|
39563
|
+
//# debugId=D839AA90F21AC3D964756E2164756E21
|
|
39562
39564
|
//# sourceMappingURL=cli.js.map
|