@caatinga/cli 2.1.0 → 2.2.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @caatinga/cli
2
2
 
3
- Developer toolkit for Stellar / Soroban dApps — `init`, `build`, `deploy`, `generate`, and `invoke`.
3
+ Developer toolkit for Stellar / Soroban dApps — `init`, `build`, `deploy`, `generate`, `status`, and `invoke`.
4
4
 
5
5
  ## Install
6
6
 
@@ -35,11 +35,11 @@ npm install
35
35
 
36
36
  npx caatinga build counter
37
37
  npx caatinga deploy counter --network testnet --source alice
38
- npx caatinga generate counter --network testnet
38
+ npx caatinga status --network testnet
39
39
  npx caatinga invoke counter.increment --network testnet --source alice
40
40
  ```
41
41
 
42
- `build` only compiles the WASM file. `deploy` writes contract IDs to `caatinga.artifacts.json`, and the frontend/client flow needs those IDs before it can resolve a contract. `generate` creates TypeScript bindings under the path configured in `caatinga.config.ts` (templates default to `contracts/generated/`).
42
+ `build` only compiles the WASM file. `deploy` writes contract IDs to `caatinga.artifacts.json` and then generates TypeScript bindings automatically under the path configured in `caatinga.config.ts` (templates default to `contracts/generated/`); pass `--no-generate` to skip. `status` shows what's deployed per network and whether bindings are fresh.
43
43
 
44
44
  ## Commands
45
45
 
@@ -49,10 +49,11 @@ npx caatinga invoke counter.increment --network testnet --source alice
49
49
  | `caatinga doctor [--network <network>] [--source <identity>]` | Check local Node, Stellar CLI, Rust, config, artifacts, network, and source identity setup |
50
50
  | `caatinga build [contract]` | Compile contract WASM through Stellar CLI (default contract: `counter`) |
51
51
  | `caatinga deploy [contract]` | Deploy one contract or the full configured graph; record IDs in artifacts |
52
- | `caatinga generate [contract]` | Generate TypeScript bindings from a deployed contract ID; omit the name to generate for all deployed contracts |
52
+ | `caatinga generate [contract]` | (Re)generate TypeScript bindings; omit the name to generate for all deployed contracts |
53
+ | `caatinga status [--network <name>] [--json]` | Show deployed contracts and binding freshness per network |
53
54
  | `caatinga invoke <contract.method>` | Invoke a deployed contract method; extra args forward to Stellar CLI |
54
55
 
55
- The supported CLI flow is `init -> build -> deploy -> generate -> invoke`.
56
+ The supported CLI flow is `init -> build -> deploy (bindings auto-generate) -> invoke`.
56
57
 
57
58
  ### `init`
58
59
 
@@ -78,12 +79,17 @@ The supported CLI flow is `init -> build -> deploy -> generate -> invoke`.
78
79
  - `-s, --source <identity>` is required; must be a Stellar CLI identity alias that can sign (for example `alice`)
79
80
  - `--force` redeploys even when artifacts already store a contract ID
80
81
  - `--no-deps` skips dependency deployment for a single named contract (`--no-deps` requires `[contract]`)
82
+ - `--no-generate` skips the automatic bindings generation after deploy
81
83
 
82
84
  Dependencies listed in `dependsOn` deploy first unless `--no-deps` is set. Deploy args may reference `${contracts.<name>.contractId}` placeholders resolved from artifacts.
83
85
 
84
- ### `generate` and `invoke`
86
+ After a successful deploy, bindings generate automatically for the deployed contracts. A generation failure never fails the deploy — the CLI prints a warning plus the recovery command (`npx caatinga generate --network <network>`).
87
+
88
+ ### `generate`, `status`, and `invoke`
85
89
 
86
90
  - `-n, --network <network>` selects the network used to resolve deployed contract IDs
91
+ - `generate` prints binding freshness per contract before regenerating in all-contracts mode
92
+ - `status` prints a per-network table (contract ID, WASM hash, deployed, binding freshness, dependencies); `--json` emits the machine-readable structure
87
93
  - `invoke` expects `<contract.method>` (for example `counter.increment`) and forwards `[args...]` to the underlying Stellar invocation
88
94
 
89
95
  `caatinga dev` is reserved, hidden in pre-v1 builds, and not part of the stability promise. Use your frontend dev server (for example Vite) alongside the commands above.
package/dist/index.js CHANGED
@@ -161,9 +161,16 @@ async function warnIfDefaultNetworkNeedsDeploy(config) {
161
161
  }
162
162
 
163
163
  // src/commands/deploy.command.ts
164
- import { deployContractGraph, CaatingaError as CaatingaError2, CaatingaErrorCode as CaatingaErrorCode2, loadConfig as loadConfig3 } from "@caatinga/core";
164
+ import {
165
+ deployContractGraph,
166
+ generateBindingsGraph,
167
+ toCaatingaError as toCaatingaError2,
168
+ CaatingaError as CaatingaError2,
169
+ CaatingaErrorCode as CaatingaErrorCode2,
170
+ loadConfig as loadConfig3
171
+ } from "@caatinga/core";
165
172
  function registerDeployCommand(program2) {
166
- program2.command("deploy").description("Deploy one or all configured Soroban contracts").argument("[contract]", "Contract name").option("-n, --network <network>", "Configured network name").requiredOption("-s, --source <source>", "Stellar CLI identity alias that can sign (for example alice)").option("--force", "Redeploy contracts even if artifacts already contain contract IDs").option("--no-deps", "Do not deploy missing dependencies for a selected contract").option("--no-stale-check", "Do not warn when WASM may be older than contract sources").option("--verify-deps", "Verify dependency contract IDs exist on-chain before deploy").action((contractName, options) => runCliAction(async () => {
173
+ program2.command("deploy").description("Deploy one or all configured Soroban contracts").argument("[contract]", "Contract name").option("-n, --network <network>", "Configured network name").requiredOption("-s, --source <source>", "Stellar CLI identity alias that can sign (for example alice)").option("--force", "Redeploy contracts even if artifacts already contain contract IDs").option("--no-deps", "Do not deploy missing dependencies for a selected contract").option("--no-stale-check", "Do not warn when WASM may be older than contract sources").option("--verify-deps", "Verify dependency contract IDs exist on-chain before deploy").option("--no-generate", "Skip TypeScript bindings generation after deploy").action((contractName, options) => runCliAction(async () => {
167
174
  if (options.deps === false && !contractName) {
168
175
  throw new CaatingaError2(
169
176
  "`--no-deps` requires a contract name.",
@@ -197,15 +204,44 @@ function registerDeployCommand(program2) {
197
204
  logger.info(` Contract ID: ${contract.contractId}`);
198
205
  }
199
206
  logger.info("Artifacts updated: caatinga.artifacts.json");
200
- if (result.deployedContracts.length > 0) {
207
+ if (result.deployedContracts.length === 0) {
208
+ return;
209
+ }
210
+ if (options.generate === false) {
201
211
  logger.info("");
212
+ logger.info("Bindings generation skipped (--no-generate).");
202
213
  logger.info("Next:");
203
214
  for (const contract of result.deployedContracts) {
204
215
  logger.info(` npx caatinga generate ${contract.name} --network ${result.network.name}`);
205
216
  }
206
217
  logger.info(" npm run dev");
218
+ return;
219
+ }
220
+ try {
221
+ const generated = await generateBindingsGraph({
222
+ config,
223
+ contractNames: result.deployedContracts.map((contract) => contract.name),
224
+ networkName: result.network.name
225
+ });
226
+ logger.info("");
227
+ logger.success("Bindings generated");
228
+ for (const binding of generated.results) {
229
+ logger.info(` ${binding.contractName} \u2192 ${binding.importPath}`);
230
+ }
231
+ logger.info("");
232
+ logger.info("Next:");
233
+ logger.info(" npm run dev");
234
+ } catch (error) {
235
+ const caatingaError = toCaatingaError2(error);
207
236
  logger.info("");
208
- logger.info("Run generate before npm run dev so the app uses real bindings, not the stub.");
237
+ logger.warn("Deploy succeeded, but bindings generation failed.");
238
+ logger.warn(` ${caatingaError.message} (${caatingaError.code})`);
239
+ if (caatingaError.hint) {
240
+ logger.warn(` Hint: ${caatingaError.hint}`);
241
+ }
242
+ logger.info("");
243
+ logger.info("Recover with:");
244
+ logger.info(` npx caatinga generate --network ${result.network.name}`);
209
245
  }
210
246
  }));
211
247
  }
@@ -398,6 +434,33 @@ function printFixes(diagnostics) {
398
434
  }
399
435
  }
400
436
 
437
+ // src/commands/doctor-bindings.ts
438
+ import {
439
+ evaluateBindingsFreshness,
440
+ loadConfig as loadConfig5,
441
+ readArtifacts as readArtifacts3,
442
+ resolveNetwork as resolveNetwork3
443
+ } from "@caatinga/core";
444
+ async function evaluateBindingCoverage(options) {
445
+ const cwd = options.cwd;
446
+ const config = await loadConfig5({ cwd });
447
+ const network = resolveNetwork3(config, options.networkName);
448
+ const artifacts = await readArtifacts3(cwd);
449
+ const freshness = await evaluateBindingsFreshness({
450
+ config,
451
+ artifacts,
452
+ networkName: network.name,
453
+ cwd
454
+ });
455
+ const lines = freshness.map((entry) => ({
456
+ name: entry.contractName,
457
+ status: entry.status,
458
+ reason: entry.reason,
459
+ ...entry.status === "fresh" ? {} : { fix: `Run: caatinga generate ${entry.contractName} --network ${network.name}` }
460
+ }));
461
+ return { lines, allFresh: lines.every((line) => line.status === "fresh") };
462
+ }
463
+
401
464
  // src/commands/doctor.command.ts
402
465
  function printDeployCoverageLine(line) {
403
466
  if (line.ok) {
@@ -424,6 +487,25 @@ async function reportDeployCoverage(networkName) {
424
487
  }
425
488
  return true;
426
489
  }
490
+ function printBindingCoverageLine(line) {
491
+ if (line.status === "fresh") {
492
+ logger.info(`\u2713 ${line.name} \u2014 bindings fresh`);
493
+ return;
494
+ }
495
+ logger.info(`\u2717 ${line.name} \u2014 bindings ${line.status}${line.reason ? ` (${line.reason})` : ""}`);
496
+ if (line.fix) logger.info(` ${line.fix}`);
497
+ }
498
+ async function reportBindingCoverage(networkName) {
499
+ const coverage = await evaluateBindingCoverage({ networkName });
500
+ if (coverage.lines.length === 0) {
501
+ return;
502
+ }
503
+ logger.info("");
504
+ logger.info(`Bindings (${networkName}):`);
505
+ for (const line of coverage.lines) {
506
+ printBindingCoverageLine(line);
507
+ }
508
+ }
427
509
  function registerDoctorCommand(program2) {
428
510
  program2.command("doctor").description("Check local Caatinga, Stellar CLI, Rust, config, and source identity setup").option("-n, --network <network>", "Configured network name to validate").option("-s, --source <source>", "Stellar CLI identity alias to validate").action((options) => runCliAction(async () => {
429
511
  logger.info("Caatinga Doctor");
@@ -441,6 +523,7 @@ function registerDoctorCommand(program2) {
441
523
  ready = false;
442
524
  throw error;
443
525
  }
526
+ await reportBindingCoverage(options.network);
444
527
  }
445
528
  logger.info("");
446
529
  logger.info(`Status: ${ready ? "ready" : "blocked"}`);
@@ -451,11 +534,41 @@ function registerDoctorCommand(program2) {
451
534
  }
452
535
 
453
536
  // src/commands/generate.command.ts
454
- import { generateBindingsGraph, loadConfig as loadConfig5 } from "@caatinga/core";
537
+ import {
538
+ evaluateBindingsFreshness as evaluateBindingsFreshness2,
539
+ generateBindingsGraph as generateBindingsGraph2,
540
+ loadConfig as loadConfig6,
541
+ readArtifacts as readArtifacts4,
542
+ resolveNetwork as resolveNetwork4
543
+ } from "@caatinga/core";
544
+ async function printFreshnessPreState(config, networkName) {
545
+ try {
546
+ const network = resolveNetwork4(config, networkName);
547
+ const artifacts = await readArtifacts4();
548
+ const freshness = await evaluateBindingsFreshness2({
549
+ config,
550
+ artifacts,
551
+ networkName: network.name
552
+ });
553
+ if (freshness.length === 0) {
554
+ return;
555
+ }
556
+ logger.info("Current bindings:");
557
+ for (const entry of freshness) {
558
+ const reason = entry.reason ? ` \u2014 ${entry.reason}` : "";
559
+ logger.info(` [${entry.status}] ${entry.contractName}${reason}`);
560
+ }
561
+ logger.info("");
562
+ } catch {
563
+ }
564
+ }
455
565
  function registerGenerateCommand(program2) {
456
566
  program2.command("generate").description("Generate TypeScript bindings for deployed contracts").argument("[contract]", "Contract name (defaults to all deployed contracts)").option("-n, --network <network>", "Configured network name").action((contractName, options) => runCliAction(async () => {
457
- const config = await loadConfig5();
458
- const { network, results } = await generateBindingsGraph({
567
+ const config = await loadConfig6();
568
+ if (!contractName) {
569
+ await printFreshnessPreState(config, options.network);
570
+ }
571
+ const { network, results } = await generateBindingsGraph2({
459
572
  config,
460
573
  contractName,
461
574
  networkName: options.network
@@ -562,26 +675,25 @@ function registerInitCommand(program2) {
562
675
  logger.info(
563
676
  ` npx caatinga deploy ${defaultContract} --network testnet --source <identity>`
564
677
  );
565
- logger.info(` npx caatinga generate ${defaultContract} --network testnet`);
566
678
  } else {
567
679
  logger.info(" npx caatinga build");
568
680
  logger.info(" npx caatinga deploy --network testnet --source <identity>");
569
- logger.info(" npx caatinga generate --network testnet");
570
681
  }
571
682
  logger.info(" npm run dev");
572
683
  logger.info("");
573
684
  logger.info(
574
- "Note: deploy and generate the contract before interacting in the frontend \u2014"
685
+ "Note: deploy generates TypeScript bindings automatically (--no-generate to skip) \u2014"
575
686
  );
576
687
  logger.info("the dApp reads the contract ID from caatinga.artifacts.json.");
688
+ logger.info("If generation fails, recover with: npx caatinga generate --network testnet");
577
689
  }));
578
690
  }
579
691
 
580
692
  // src/commands/invoke.command.ts
581
- import { invokeContract, loadConfig as loadConfig6 } from "@caatinga/core";
693
+ import { invokeContract, loadConfig as loadConfig7 } from "@caatinga/core";
582
694
  function registerInvokeCommand(program2) {
583
695
  program2.command("invoke").description("Invoke a deployed contract function").argument("<target>", "Invoke target in contract.method format").argument("[args...]", "Arguments forwarded to Stellar CLI after the method name").option("-n, --network <network>", "Configured network name").requiredOption("-s, --source <source>", "Stellar CLI identity alias that can sign (for example alice)").allowUnknownOption(true).allowExcessArguments(true).action((target, args, options) => runCliAction(async () => {
584
- const config = await loadConfig6();
696
+ const config = await loadConfig7();
585
697
  const result = await invokeContract({
586
698
  config,
587
699
  target,
@@ -601,8 +713,77 @@ function registerInvokeCommand(program2) {
601
713
  }));
602
714
  }
603
715
 
716
+ // src/commands/status.command.ts
717
+ import { collectProjectStatus, loadConfig as loadConfig8 } from "@caatinga/core";
718
+
719
+ // src/utils/table.ts
720
+ function renderTable(headers, rows) {
721
+ const widths = headers.map(
722
+ (header, column) => Math.max(header.length, ...rows.map((row) => (row[column] ?? "").length))
723
+ );
724
+ const renderRow = (cells) => cells.map((cell, column) => (cell ?? "").padEnd(widths[column])).join(" ").trimEnd();
725
+ return [
726
+ renderRow(headers),
727
+ widths.map((width) => "\u2500".repeat(width)).join(" "),
728
+ ...rows.map((row) => renderRow(row))
729
+ ];
730
+ }
731
+
732
+ // src/commands/status.command.ts
733
+ function shortId(value) {
734
+ if (!value) return "\u2014";
735
+ return value.length > 12 ? `${value.slice(0, 5)}\u2026${value.slice(-4)}` : value;
736
+ }
737
+ function shortHash(value) {
738
+ if (!value) return "\u2014";
739
+ return value.slice(0, 8);
740
+ }
741
+ function toRow(entry) {
742
+ return [
743
+ entry.name,
744
+ shortId(entry.contractId),
745
+ shortHash(entry.wasmHash),
746
+ entry.deployed ? "\u2713" : "\u2717",
747
+ entry.bindings.status,
748
+ entry.dependencies.length > 0 ? entry.dependencies.join(", ") : "\u2014"
749
+ ];
750
+ }
751
+ function registerStatusCommand(program2) {
752
+ program2.command("status").description("Show deployed contracts and binding freshness per network").option("-n, --network <network>", "Configured network name").option("--json", "Print machine-readable JSON instead of the table").action((options) => runCliAction(async () => {
753
+ const config = await loadConfig8();
754
+ const status = await collectProjectStatus({
755
+ config,
756
+ networkName: options.network
757
+ });
758
+ if (options.json) {
759
+ console.log(JSON.stringify(status, null, 2));
760
+ return;
761
+ }
762
+ logger.success(`Project: ${status.project}`);
763
+ for (const network of status.networks) {
764
+ logger.info("");
765
+ logger.info(`Network: ${network.network}`);
766
+ const lines = renderTable(
767
+ ["CONTRACT", "CONTRACT ID", "WASM HASH", "DEPLOYED", "BINDINGS", "DEPS"],
768
+ network.contracts.map(toRow)
769
+ );
770
+ for (const line of lines) {
771
+ logger.info(line);
772
+ }
773
+ const needsAttention = network.contracts.filter(
774
+ (entry) => entry.deployed && entry.bindings.status !== "fresh"
775
+ );
776
+ for (const entry of needsAttention) {
777
+ logger.warn(
778
+ `Bindings ${entry.bindings.status} for ${entry.name}${entry.bindings.reason ? ` (${entry.bindings.reason})` : ""} \u2014 run: npx caatinga generate ${entry.name} --network ${network.network}`
779
+ );
780
+ }
781
+ }
782
+ }));
783
+ }
784
+
604
785
  // src/version.ts
605
- var CAATINGA_CLI_VERSION = "2.1.0";
786
+ var CAATINGA_CLI_VERSION = "2.2.1";
606
787
 
607
788
  // src/program.ts
608
789
  function createProgram() {
@@ -615,6 +796,7 @@ function createProgram() {
615
796
  registerDeployCommand(program2);
616
797
  registerGenerateCommand(program2);
617
798
  registerInvokeCommand(program2);
799
+ registerStatusCommand(program2);
618
800
  return program2;
619
801
  }
620
802
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caatinga/cli",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
4
4
  "description": "Caatinga CLI for building dApps on Stellar/Soroban",
5
5
  "keywords": [
6
6
  "stellar",
@@ -43,7 +43,7 @@
43
43
  "LICENSE"
44
44
  ],
45
45
  "dependencies": {
46
- "@caatinga/core": "^2.1.0",
46
+ "@caatinga/core": "^2.2.1",
47
47
  "chalk": "^5.4.1",
48
48
  "commander": "^12.1.0"
49
49
  },
@@ -54,7 +54,7 @@
54
54
  "vitest": "^2.1.8"
55
55
  },
56
56
  "scripts": {
57
- "build": "tsup src/index.ts --format esm --dts --clean && rm -rf ./templates && cp -r ../templates ./templates",
57
+ "build": "tsup src/index.ts --format esm --dts --clean && rm -rf ./templates && mkdir ./templates && (cd ../templates && tar --exclude=node_modules --exclude=dist -cf - .) | (cd ./templates && tar -xf -)",
58
58
  "predev": "pnpm --filter @caatinga/core build",
59
59
  "dev": "tsx src/index.ts",
60
60
  "test": "vitest run --pool=threads",
@@ -3,7 +3,7 @@
3
3
  "version": "0.1.0",
4
4
  "description": "Experimental multi-contract Soroban template with token dependency injection.",
5
5
  "caatinga": {
6
- "compatibleCore": "^2.1.0",
6
+ "compatibleCore": "^2.2.1",
7
7
  "templateVersion": 1
8
8
  },
9
9
  "frontend": {
@@ -12,15 +12,15 @@
12
12
  "caatinga:generate": "caatinga generate token && caatinga generate marketplace"
13
13
  },
14
14
  "dependencies": {
15
- "@caatinga/client": "^2.1.0",
16
- "@caatinga/core": "^2.1.0",
15
+ "@caatinga/client": "^2.2.1",
16
+ "@caatinga/core": "^2.2.1",
17
17
  "@vitejs/plugin-react": "^4.3.4",
18
18
  "react": "^18.3.1",
19
19
  "react-dom": "^18.3.1",
20
20
  "vite": "^6.0.6"
21
21
  },
22
22
  "devDependencies": {
23
- "@caatinga/cli": "^2.1.0",
23
+ "@caatinga/cli": "^2.2.1",
24
24
  "@types/react": "^18.3.18",
25
25
  "@types/react-dom": "^18.3.5",
26
26
  "typescript": "^5.7.2"
@@ -3,7 +3,7 @@
3
3
  "version": "0.1.0",
4
4
  "description": "Minimal Vite + React + Soroban counter dApp.",
5
5
  "caatinga": {
6
- "compatibleCore": "^2.1.0",
6
+ "compatibleCore": "^2.2.1",
7
7
  "templateVersion": 1
8
8
  },
9
9
  "frontend": {
@@ -7,14 +7,14 @@
7
7
  "dev": "vite",
8
8
  "build": "tsc && vite build",
9
9
  "preview": "vite preview",
10
- "caatinga:build": "caatinga build counter",
11
- "caatinga:deploy": "caatinga deploy counter",
12
- "caatinga:generate": "caatinga generate counter"
10
+ "caatinga:build": "npx caatinga build counter",
11
+ "caatinga:deploy": "npx caatinga deploy counter --network testnet --source ${CAATINGA_SOURCE:-alice}",
12
+ "caatinga:generate": "npx caatinga generate counter --network testnet"
13
13
  },
14
14
  "dependencies": {
15
- "@caatinga/client": "^2.1.0",
16
- "@caatinga/core": "^2.1.0",
17
- "@creit.tech/stellar-wallets-kit": "^1.9.5",
15
+ "@caatinga/client": "^2.2.1",
16
+ "@caatinga/core": "^2.2.1",
17
+ "@creit.tech/stellar-wallets-kit": "^2.3.0",
18
18
  "@stellar/stellar-sdk": "^14.5.0",
19
19
  "buffer": "^6.0.3",
20
20
  "@vitejs/plugin-react": "^4.3.4",
@@ -23,9 +23,24 @@
23
23
  "vite": "^6.0.6"
24
24
  },
25
25
  "devDependencies": {
26
- "@caatinga/cli": "^2.1.0",
26
+ "@caatinga/cli": "^2.2.1",
27
27
  "@types/react": "^18.3.18",
28
28
  "@types/react-dom": "^18.3.5",
29
29
  "typescript": "^5.7.2"
30
+ },
31
+ "overrides": {
32
+ "uuid": "^14.0.0",
33
+ "@creit.tech/stellar-wallets-kit": {
34
+ "@trezor/connect-web": "file:./src/stubs/empty-wallet-dep",
35
+ "@trezor/connect-plugin-stellar": "file:./src/stubs/empty-wallet-dep",
36
+ "@hot-wallet/sdk": "file:./src/stubs/hot-wallet-sdk"
37
+ },
38
+ "@reown/appkit-utils": {
39
+ "@safe-global/safe-apps-sdk": "-",
40
+ "@safe-global/safe-apps-provider": "-"
41
+ },
42
+ "@safe-global/safe-apps-sdk": {
43
+ "@safe-global/safe-gateway-typescript-sdk": "-"
44
+ }
30
45
  }
31
46
  }
@@ -1,12 +1,19 @@
1
- # pnpm 10.26+ / 11.x block lifecycle scripts by default; vite depends on esbuild.
1
+ # pnpm-only file (npm / yarn / bun ignore it). pnpm 10.26+/11.x block lifecycle
2
+ # scripts by default; vite depends on esbuild's, so allow it.
3
+ # Install-time overrides block unused wallet SDK branches; vite.config.ts aliases
4
+ # provide a second layer so yarn/bun bundles stay clean too.
2
5
  allowBuilds:
3
6
  esbuild: true
4
7
 
5
- # stellar-wallets-kit@0.0.7 is unpublished from npm and is therefore pinned
6
- # to a github: URL in package.json. Its own dependencies also reference
7
- # @creit.tech/xbull-wallet-connect via a github: URL, and pnpm 10.26+ (and
8
- # 11.x) defaults blockExoticSubdeps to true, which would refuse to install
9
- # that exotic subdep. Allow it explicitly. This is a targeted opt-out for
10
- # this one transitive dep; direct dependencies in package.json still must
11
- # come from a trusted source.
12
- blockExoticSubdeps: false
8
+ ignoredOptionalDependencies:
9
+ - "@safe-global/safe-apps-provider"
10
+ - "@safe-global/safe-apps-sdk"
11
+
12
+ overrides:
13
+ uuid: "^14.0.0"
14
+ "@creit.tech/stellar-wallets-kit>@trezor/connect-web": "-"
15
+ "@creit.tech/stellar-wallets-kit>@trezor/connect-plugin-stellar": "-"
16
+ "@creit.tech/stellar-wallets-kit>@hot-wallet/sdk": "-"
17
+ "@reown/appkit-utils>@safe-global/safe-apps-sdk": "-"
18
+ "@reown/appkit-utils>@safe-global/safe-apps-provider": "-"
19
+ "@safe-global/safe-apps-sdk>@safe-global/safe-gateway-typescript-sdk": "-"
@@ -1,6 +1,15 @@
1
+ import { WalletProvider, useWallet } from "@caatinga/client/react";
2
+ import type { CaatingaArtifacts } from "@caatinga/core/browser";
3
+ import artifactsJson from "../caatinga.artifacts.json";
4
+ import { ContractNotDeployed } from "./components/ContractNotDeployed";
1
5
  import { CounterCard } from "./components/CounterCard";
2
6
  import { WalletButton } from "./components/WalletButton";
3
- import { WalletProvider, useWallet } from "./context/WalletContext";
7
+ import { WalletModal } from "./components/WalletModal";
8
+ import { stellarWalletAdapter } from "./wallet.js";
9
+
10
+ const artifacts = artifactsJson as CaatingaArtifacts;
11
+ const counterContractId = artifacts.networks?.testnet?.contracts?.counter?.contractId;
12
+ const isDeployed = Boolean(counterContractId);
4
13
 
5
14
  function AppBody() {
6
15
  const { publicKey } = useWallet();
@@ -15,7 +24,9 @@ function AppBody() {
15
24
  <WalletButton />
16
25
  </header>
17
26
 
18
- {publicKey ? (
27
+ {!isDeployed ? (
28
+ <ContractNotDeployed />
29
+ ) : publicKey ? (
19
30
  <CounterCard />
20
31
  ) : (
21
32
  <section className="counter-panel" aria-labelledby="connect-title">
@@ -35,8 +46,11 @@ function AppBody() {
35
46
 
36
47
  export default function App() {
37
48
  return (
38
- <WalletProvider>
49
+ // persist keeps the session across reloads; the provider silently
50
+ // reconnects on mount (autoConnect defaults to true when persisting).
51
+ <WalletProvider adapter={stellarWalletAdapter} options={{ persist: true }}>
39
52
  <AppBody />
53
+ <WalletModal />
40
54
  </WalletProvider>
41
55
  );
42
56
  }
@@ -0,0 +1,27 @@
1
+ export function ContractNotDeployed() {
2
+ return (
3
+ <section className="counter-panel" aria-labelledby="not-deployed-title">
4
+ <div className="counter-panel__header">
5
+ <div>
6
+ <p className="eyebrow">Get started</p>
7
+ <h2 id="not-deployed-title">Contract not deployed</h2>
8
+ </div>
9
+ <span className="network-pill">testnet</span>
10
+ </div>
11
+ <p>
12
+ The counter contract has no on-chain ID yet, so the frontend can&apos;t read or update it.
13
+ Build and deploy first — the dApp reads the contract ID from{" "}
14
+ <code>caatinga.artifacts.json</code>. Deploy also generates TypeScript bindings
15
+ automatically.
16
+ </p>
17
+ <pre className="counter-error" role="note">
18
+ {`npx caatinga build counter
19
+ npx caatinga deploy counter --network testnet --source <identity>
20
+ npm run dev
21
+
22
+ # If bindings generation failed after deploy:
23
+ npx caatinga generate counter --network testnet`}
24
+ </pre>
25
+ </section>
26
+ );
27
+ }
@@ -1,7 +1,7 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from "react";
2
2
  import { caatingaClient } from "../caatinga.js";
3
3
  import { formatCaatingaError } from "@caatinga/core/browser";
4
- import { useWallet } from "../context/WalletContext.js";
4
+ import { useWallet } from "@caatinga/client/react";
5
5
  import { LoadingModal } from "./LoadingModal.js";
6
6
 
7
7
  export function CounterCard() {
@@ -1,4 +1,5 @@
1
- import { useWallet } from "../context/WalletContext.js";
1
+ import { useWallet } from "@caatinga/client/react";
2
+ import { WALLET_SELECTION_CLOSED_ERROR } from "../wallet-modal-controller.js";
2
3
 
3
4
  function shortenAddress(address: string): string {
4
5
  if (address.length <= 12) {
@@ -9,23 +10,23 @@ function shortenAddress(address: string): string {
9
10
  }
10
11
 
11
12
  export function WalletButton() {
12
- const { publicKey, loading, error, connect, disconnect } = useWallet();
13
+ const { publicKey, connecting, error, connect, disconnect } = useWallet();
13
14
 
14
15
  return (
15
16
  <div className="wallet-shell">
16
17
  <button
17
18
  className="wallet-button"
18
19
  type="button"
19
- onClick={publicKey ? () => void disconnect() : () => void connect()}
20
- disabled={loading}
20
+ onClick={publicKey ? () => void disconnect() : () => void connect().catch(() => {})}
21
+ disabled={connecting}
21
22
  aria-live="polite"
22
23
  >
23
24
  <span className={publicKey ? "status-dot status-dot--on" : "status-dot"} />
24
- {loading ? "Connecting..." : publicKey ? shortenAddress(publicKey) : "Connect"}
25
+ {connecting ? "Connecting..." : publicKey ? shortenAddress(publicKey) : "Connect"}
25
26
  </button>
26
- {error ? (
27
+ {error && error.name !== WALLET_SELECTION_CLOSED_ERROR ? (
27
28
  <p className="wallet-error" role="alert">
28
- {error}
29
+ {error.message}
29
30
  </p>
30
31
  ) : null}
31
32
  </div>