@caatinga/cli 0.2.4 → 2.0.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/README.md CHANGED
@@ -14,7 +14,7 @@ Inside a generated project, prefer `npx caatinga` so the project-local workflow
14
14
  ## Requirements
15
15
 
16
16
  - Node.js `>=20`
17
- - [Stellar CLI](https://developers.stellar.org/docs/tools/developer-tools/cli/stellar-cli) `>=23.0.0` and `<=25.2.0` on `PATH` (22.x breaks `caatinga invoke` signing)
17
+ - [Stellar CLI](https://developers.stellar.org/docs/tools/developer-tools/cli/stellar-cli) `>=23.0.0` on `PATH` (22.x breaks `caatinga invoke` signing)
18
18
  - Rust 1.84.0 or newer with the `wasm32v1-none` target (contract builds)
19
19
  - A funded Stellar CLI identity for `deploy` and `invoke` (for example `alice`)
20
20
 
@@ -23,7 +23,7 @@ rustup target add wasm32v1-none
23
23
  stellar keys generate alice --fund --network testnet
24
24
  ```
25
25
 
26
- If your machine runs a newer Stellar CLI, `--allow-untested-stellar-cli` is the local-only escape hatch. CI and release workflows must stay on the supported range.
26
+ Stellar CLI versions newer than the last-tested `25.2.0` are accepted with a non-fatal stderr advisory and a `caatinga doctor` warning; no override flag is required.
27
27
 
28
28
  ## Quick start
29
29
 
@@ -31,6 +31,7 @@ If your machine runs a newer Stellar CLI, `--allow-untested-stellar-cli` is the
31
31
  caatinga init my-dapp
32
32
  cd my-dapp
33
33
  npm install
34
+ # pnpm alternative: pnpm install (template includes pnpm-workspace.yaml for pnpm 10.26+/11)
34
35
 
35
36
  npx caatinga build counter
36
37
  npx caatinga deploy counter --network testnet --source alice
@@ -38,7 +39,7 @@ npx caatinga generate counter --network testnet
38
39
  npx caatinga invoke counter.increment --network testnet --source alice
39
40
  ```
40
41
 
41
- `deploy` writes contract IDs to `caatinga.artifacts.json`. `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 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
43
 
43
44
  ## Commands
44
45
 
@@ -62,7 +63,7 @@ The supported CLI flow is `init -> build -> deploy -> generate -> invoke`.
62
63
  ### `build`
63
64
 
64
65
  - `[contract]` defaults to `counter` when omitted
65
- - `--allow-untested-stellar-cli` allows a Stellar CLI newer than Caatinga's tested maximum (local only)
66
+ - prints a deploy reminder when the default network lacks a `contractId` in `caatinga.artifacts.json`; this warning does not fail the build
66
67
 
67
68
  ### `doctor`
68
69
 
@@ -77,7 +78,6 @@ The supported CLI flow is `init -> build -> deploy -> generate -> invoke`.
77
78
  - `-s, --source <identity>` is required; must be a Stellar CLI identity alias that can sign (for example `alice`)
78
79
  - `--force` redeploys even when artifacts already store a contract ID
79
80
  - `--no-deps` skips dependency deployment for a single named contract (`--no-deps` requires `[contract]`)
80
- - `--allow-untested-stellar-cli` for local experiments only
81
81
 
82
82
  Dependencies listed in `dependsOn` deploy first unless `--no-deps` is set. Deploy args may reference `${contracts.<name>.contractId}` placeholders resolved from artifacts.
83
83
 
@@ -85,7 +85,6 @@ Dependencies listed in `dependsOn` deploy first unless `--no-deps` is set. Deplo
85
85
 
86
86
  - `-n, --network <network>` selects the network used to resolve deployed contract IDs
87
87
  - `invoke` expects `<contract.method>` (for example `counter.increment`) and forwards `[args...]` to the underlying Stellar invocation
88
- - Both accept `--allow-untested-stellar-cli` for local experiments only
89
88
 
90
89
  `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.
91
90
 
@@ -107,7 +106,7 @@ Unsupported input posture:
107
106
  Common codes:
108
107
 
109
108
  - `CAATINGA_CONFIG_NOT_FOUND`, `CAATINGA_INVALID_CONFIG`
110
- - `CAATINGA_STELLAR_CLI_NOT_FOUND`, `CAATINGA_UNSUPPORTED_CLI_VERSION`, `CAATINGA_UNTESTED_CLI_VERSION`
109
+ - `CAATINGA_STELLAR_CLI_NOT_FOUND`, `CAATINGA_UNSUPPORTED_CLI_VERSION`
111
110
  - `CAATINGA_BUILD_FAILED`, `CAATINGA_DEPLOY_FAILED`, `CAATINGA_BINDINGS_FAILED`, `CAATINGA_INVOKE_FAILED`
112
111
  - `CAATINGA_CONTRACT_ID_NOT_FOUND`, `CAATINGA_SOURCE_ACCOUNT_REQUIRED`, `CAATINGA_UNSAFE_SOURCE_ACCOUNT`
113
112
  - `CAATINGA_CONTRACT_DEPENDENCY_NOT_FOUND`, `CAATINGA_CONTRACT_DEPENDENCY_CYCLE`
package/dist/index.js CHANGED
@@ -2,21 +2,33 @@
2
2
 
3
3
  // src/utils/preflight.ts
4
4
  import chalk from "chalk";
5
- var NODE_MIN_MAJOR = 20;
6
- function checkNodeVersion() {
7
- const major = parseInt(process.versions.node.split(".")[0] ?? "0", 10);
5
+ import { NODE_MIN_MAJOR as NODE_MIN_MAJOR2 } from "@caatinga/core/runtime/requirements";
6
+
7
+ // src/diagnostics/node-diagnostic.ts
8
+ import { NODE_MIN_MAJOR } from "@caatinga/core/runtime/requirements";
9
+ function nodeDiagnostic() {
10
+ const version = process.versions.node;
11
+ const major = Number.parseInt(version.split(".")[0] ?? "0", 10);
8
12
  if (major < NODE_MIN_MAJOR) {
9
- return `Node.js ${process.versions.node} is below the required minimum v${NODE_MIN_MAJOR}.
10
- Install Node.js ${NODE_MIN_MAJOR} or newer: https://nodejs.org/`;
13
+ return {
14
+ ok: false,
15
+ label: `Node.js ${version} is below the required minimum ${NODE_MIN_MAJOR}.0.0`,
16
+ fix: `Install Node.js ${NODE_MIN_MAJOR} or newer.`
17
+ };
11
18
  }
12
- return null;
19
+ return { ok: true, label: `Node.js ${version}` };
20
+ }
21
+
22
+ // src/utils/preflight.ts
23
+ function formatNodePreflightFailure() {
24
+ const version = process.versions.node;
25
+ return `Node.js ${version} is below the required minimum v${NODE_MIN_MAJOR2}.
26
+ Install Node.js ${NODE_MIN_MAJOR2} or newer: https://nodejs.org/`;
13
27
  }
14
28
  function runPreflight() {
15
- const failures = [];
16
- const nodeFailure = checkNodeVersion();
17
- if (nodeFailure) failures.push(nodeFailure);
18
- if (failures.length > 0) return { ok: false, failures };
19
- return { ok: true };
29
+ const diagnostic = nodeDiagnostic();
30
+ if (diagnostic.ok) return { ok: true };
31
+ return { ok: false, failures: [formatNodePreflightFailure()] };
20
32
  }
21
33
  function assertPreflight() {
22
34
  const result = runPreflight();
@@ -33,7 +45,7 @@ function assertPreflight() {
33
45
  import { Command } from "commander";
34
46
 
35
47
  // src/commands/build.command.ts
36
- import { buildContract, loadConfig } from "@caatinga/core";
48
+ import { buildContract, CaatingaError, CaatingaErrorCode, loadConfig as loadConfig2 } from "@caatinga/core";
37
49
 
38
50
  // src/utils/errors.ts
39
51
  import chalk2 from "chalk";
@@ -84,34 +96,82 @@ var logger = {
84
96
  }
85
97
  };
86
98
 
99
+ // src/commands/doctor-deploy-coverage.ts
100
+ import { loadConfig, readArtifacts, resolveNetwork } from "@caatinga/core";
101
+ async function evaluateDeployCoverage(options) {
102
+ const cwd = options.cwd;
103
+ const config = await loadConfig({ cwd });
104
+ const network = resolveNetwork(config, options.networkName);
105
+ const artifacts = await readArtifacts(cwd);
106
+ const lines = [];
107
+ for (const name of Object.keys(config.contracts)) {
108
+ const contractId = artifacts.networks[network.name]?.contracts[name]?.contractId;
109
+ if (contractId) {
110
+ lines.push({ name, ok: true, contractId });
111
+ } else {
112
+ lines.push({
113
+ name,
114
+ ok: false,
115
+ fix: `Run: caatinga deploy ${name} --network ${network.name} --source <identity>`
116
+ });
117
+ }
118
+ }
119
+ return { lines, complete: lines.every((line) => line.ok) };
120
+ }
121
+
87
122
  // src/commands/build.command.ts
88
123
  function registerBuildCommand(program2) {
89
- program2.command("build").description("Build a configured Soroban contract").argument("[contract]", "Contract name", "counter").option("--allow-untested-stellar-cli", "Allow local use of a Stellar CLI version newer than Caatinga's tested maximum").action((contractName, options) => runCliAction(async () => {
90
- const config = await loadConfig();
124
+ program2.command("build").description("Build a configured Soroban contract").argument("[contract]", "Contract name", "counter").action((contractName) => runCliAction(async () => {
125
+ const config = await loadConfig2();
91
126
  const result = await buildContract({
92
127
  config,
93
- contractName,
94
- allowUntestedStellarCli: options.allowUntestedStellarCli === true
128
+ contractName
95
129
  });
96
130
  logger.success("Contract built");
97
131
  logger.info("");
98
132
  logger.info(`Contract: ${result.contract.name}`);
99
133
  logger.info(`WASM: ${result.contract.config.wasm}`);
134
+ await warnIfDefaultNetworkNeedsDeploy(config);
100
135
  }));
101
136
  }
137
+ async function warnIfDefaultNetworkNeedsDeploy(config) {
138
+ let missingDeployCommands;
139
+ try {
140
+ const coverage = await evaluateDeployCoverage({ networkName: config.defaultNetwork });
141
+ if (coverage.complete) {
142
+ return;
143
+ }
144
+ missingDeployCommands = coverage.lines.filter((line) => !line.ok).map((line) => line.fix?.replace(/^Run: /, "")).filter((fix) => Boolean(fix));
145
+ } catch (error) {
146
+ if (!(error instanceof CaatingaError) || error.code !== CaatingaErrorCode.ARTIFACT_NOT_FOUND) {
147
+ throw error;
148
+ }
149
+ missingDeployCommands = Object.keys(config.contracts).map(
150
+ (name) => `caatinga deploy ${name} --network ${config.defaultNetwork} --source <identity>`
151
+ );
152
+ }
153
+ if (missingDeployCommands.length === 0) {
154
+ return;
155
+ }
156
+ logger.warn("");
157
+ for (const command of missingDeployCommands) {
158
+ logger.warn(`Next: ${command}`);
159
+ }
160
+ logger.warn("The frontend needs contractId in caatinga.artifacts.json; build alone is not enough.");
161
+ }
102
162
 
103
163
  // src/commands/deploy.command.ts
104
- import { deployContractGraph, CaatingaError, CaatingaErrorCode, loadConfig as loadConfig2 } from "@caatinga/core";
164
+ import { deployContractGraph, CaatingaError as CaatingaError2, CaatingaErrorCode as CaatingaErrorCode2, loadConfig as loadConfig3 } from "@caatinga/core";
105
165
  function registerDeployCommand(program2) {
106
- 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("--allow-untested-stellar-cli", "Allow local use of a Stellar CLI version newer than Caatinga's tested maximum").option("--verify-deps", "Verify dependency contract IDs exist on-chain before deploy").action((contractName, options) => runCliAction(async () => {
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 () => {
107
167
  if (options.deps === false && !contractName) {
108
- throw new CaatingaError(
168
+ throw new CaatingaError2(
109
169
  "`--no-deps` requires a contract name.",
110
- CaatingaErrorCode.INVALID_CONFIG,
170
+ CaatingaErrorCode2.INVALID_CONFIG,
111
171
  "Select a single contract or omit `--no-deps` to deploy the full graph."
112
172
  );
113
173
  }
114
- const config = await loadConfig2();
174
+ const config = await loadConfig3();
115
175
  const result = await deployContractGraph({
116
176
  config,
117
177
  contractName,
@@ -120,8 +180,7 @@ function registerDeployCommand(program2) {
120
180
  includeDependencies: options.deps !== false,
121
181
  force: options.force === true,
122
182
  checkStaleWasm: options.staleCheck !== false,
123
- verifyDeps: options.verifyDeps === true,
124
- allowUntestedStellarCli: options.allowUntestedStellarCli === true
183
+ verifyDeps: options.verifyDeps === true
125
184
  });
126
185
  for (const warning of result.staleWasmWarnings) {
127
186
  logger.warn(warning.message);
@@ -154,76 +213,56 @@ function registerDevCommand(program2) {
154
213
  }
155
214
 
156
215
  // src/commands/doctor.command.ts
157
- import { access } from "fs/promises";
158
- import {
159
- assertSupportedStellarCliVersion,
160
- CaatingaError as CaatingaError2,
161
- CaatingaErrorCode as CaatingaErrorCode2,
162
- loadConfig as loadConfig4,
163
- parseStellarCliVersion,
164
- readArtifacts as readArtifacts2,
165
- resolveNetwork as resolveNetwork2,
166
- runCommand,
167
- validateSourceShape
168
- } from "@caatinga/core";
216
+ import { CaatingaError as CaatingaError5, CaatingaErrorCode as CaatingaErrorCode3 } from "@caatinga/core";
169
217
 
170
- // src/commands/doctor-deploy-coverage.ts
171
- import { loadConfig as loadConfig3, readArtifacts, resolveNetwork } from "@caatinga/core";
172
- async function evaluateDeployCoverage(options) {
173
- const cwd = options.cwd;
174
- const config = await loadConfig3({ cwd });
175
- const network = resolveNetwork(config, options.networkName);
176
- const artifacts = await readArtifacts(cwd);
177
- const lines = [];
178
- for (const name of Object.keys(config.contracts)) {
179
- const contractId = artifacts.networks[network.name]?.contracts[name]?.contractId;
180
- if (contractId) {
181
- lines.push({ name, ok: true, contractId });
182
- } else {
183
- lines.push({
184
- name,
185
- ok: false,
186
- fix: `Run: caatinga deploy ${name} --network ${network.name}`
187
- });
188
- }
218
+ // src/diagnostics/project-diagnostic.ts
219
+ import { access } from "fs/promises";
220
+ import { CaatingaError as CaatingaError3, loadConfig as loadConfig4, readArtifacts as readArtifacts2, resolveNetwork as resolveNetwork2 } from "@caatinga/core";
221
+ async function configDiagnostic() {
222
+ try {
223
+ await loadConfig4();
224
+ return { ok: true, label: "caatinga.config.ts found" };
225
+ } catch (error) {
226
+ const hint = error instanceof CaatingaError3 ? error.hint : void 0;
227
+ return {
228
+ ok: false,
229
+ label: "caatinga.config.ts not ready",
230
+ fix: hint ?? "Run this command from a Caatinga project root."
231
+ };
189
232
  }
190
- return { lines, complete: lines.every((line) => line.ok) };
191
233
  }
192
-
193
- // src/commands/doctor.command.ts
194
- var NODE_MIN_MAJOR2 = 20;
195
- var WASM_TARGET = "wasm32v1-none";
196
- function nodeDiagnostic() {
197
- const version = process.versions.node;
198
- const major = Number.parseInt(version.split(".")[0] ?? "0", 10);
199
- if (major < NODE_MIN_MAJOR2) {
234
+ async function artifactsDiagnostic() {
235
+ try {
236
+ await access("caatinga.artifacts.json");
237
+ await readArtifacts2();
238
+ return { ok: true, label: "caatinga.artifacts.json found" };
239
+ } catch {
200
240
  return {
201
241
  ok: false,
202
- label: `Node.js ${version} is below the required minimum ${NODE_MIN_MAJOR2}.0.0`,
203
- fix: `Install Node.js ${NODE_MIN_MAJOR2} or newer.`
242
+ label: "caatinga.artifacts.json not found or invalid",
243
+ fix: "Run caatinga init, or restore a valid caatinga.artifacts.json file."
204
244
  };
205
245
  }
206
- return { ok: true, label: `Node.js ${version}` };
207
246
  }
208
- async function stellarDiagnostic(allowUntested) {
247
+ async function networkDiagnostic(networkName) {
248
+ if (!networkName) return void 0;
209
249
  try {
210
- const result = await runCommand("stellar", ["--version"], {
211
- skipStellarVersionCheck: true
212
- });
213
- const version = assertSupportedStellarCliVersion({
214
- version: parseStellarCliVersion(result.all || result.stdout || result.stderr),
215
- allowUntested
216
- });
217
- return { ok: true, label: `Stellar CLI ${version}` };
250
+ const config = await loadConfig4();
251
+ const network = resolveNetwork2(config, networkName);
252
+ return { ok: true, label: `network ${network.name} found` };
218
253
  } catch (error) {
219
- const hint = error instanceof CaatingaError2 ? error.hint : void 0;
254
+ const hint = error instanceof CaatingaError3 ? error.hint : void 0;
220
255
  return {
221
256
  ok: false,
222
- label: "Stellar CLI not ready",
223
- fix: hint ?? "Install Stellar CLI: cargo install --locked stellar-cli --version 25.2.0"
257
+ label: `network ${networkName} not found`,
258
+ fix: hint ?? `Add "${networkName}" to caatinga.config.ts networks.`
224
259
  };
225
260
  }
226
261
  }
262
+
263
+ // src/diagnostics/rust-diagnostic.ts
264
+ import { CURRENT_RUST_WASM_TARGET } from "@caatinga/core/runtime/requirements";
265
+ import { runCommand } from "@caatinga/core";
227
266
  async function rustDiagnostic() {
228
267
  try {
229
268
  const result = await runCommand("rustc", ["--version"]);
@@ -232,7 +271,7 @@ async function rustDiagnostic() {
232
271
  return {
233
272
  ok: false,
234
273
  label: "Rust not found",
235
- fix: "Install Rust, then run: rustup target add wasm32v1-none"
274
+ fix: `Install Rust, then run: rustup target add ${CURRENT_RUST_WASM_TARGET}`
236
275
  };
237
276
  }
238
277
  }
@@ -240,63 +279,25 @@ async function wasmTargetDiagnostic() {
240
279
  try {
241
280
  const result = await runCommand("rustup", ["target", "list", "--installed"]);
242
281
  const installedTargets = result.stdout || result.all;
243
- if (installedTargets.split(/\r?\n/).includes(WASM_TARGET)) {
244
- return { ok: true, label: `${WASM_TARGET} target installed` };
282
+ if (installedTargets.split(/\r?\n/).includes(CURRENT_RUST_WASM_TARGET)) {
283
+ return { ok: true, label: `${CURRENT_RUST_WASM_TARGET} target installed` };
245
284
  }
246
285
  return {
247
286
  ok: false,
248
- label: `${WASM_TARGET} target not installed`,
249
- fix: `Run: rustup target add ${WASM_TARGET}`
287
+ label: `${CURRENT_RUST_WASM_TARGET} target not installed`,
288
+ fix: `Run: rustup target add ${CURRENT_RUST_WASM_TARGET}`
250
289
  };
251
290
  } catch {
252
291
  return {
253
292
  ok: false,
254
293
  label: "rustup not found",
255
- fix: "Install rustup, then run: rustup target add wasm32v1-none"
256
- };
257
- }
258
- }
259
- async function configDiagnostic() {
260
- try {
261
- await loadConfig4();
262
- return { ok: true, label: "caatinga.config.ts found" };
263
- } catch (error) {
264
- const hint = error instanceof CaatingaError2 ? error.hint : void 0;
265
- return {
266
- ok: false,
267
- label: "caatinga.config.ts not ready",
268
- fix: hint ?? "Run this command from a Caatinga project root."
269
- };
270
- }
271
- }
272
- async function artifactsDiagnostic() {
273
- try {
274
- await access("caatinga.artifacts.json");
275
- await readArtifacts2();
276
- return { ok: true, label: "caatinga.artifacts.json found" };
277
- } catch {
278
- return {
279
- ok: false,
280
- label: "caatinga.artifacts.json not found or invalid",
281
- fix: "Run caatinga init, or restore a valid caatinga.artifacts.json file."
282
- };
283
- }
284
- }
285
- async function networkDiagnostic(networkName) {
286
- if (!networkName) return void 0;
287
- try {
288
- const config = await loadConfig4();
289
- const network = resolveNetwork2(config, networkName);
290
- return { ok: true, label: `network ${network.name} found` };
291
- } catch (error) {
292
- const hint = error instanceof CaatingaError2 ? error.hint : void 0;
293
- return {
294
- ok: false,
295
- label: `network ${networkName} not found`,
296
- fix: hint ?? `Add "${networkName}" to caatinga.config.ts networks.`
294
+ fix: `Install rustup, then run: rustup target add ${CURRENT_RUST_WASM_TARGET}`
297
295
  };
298
296
  }
299
297
  }
298
+
299
+ // src/diagnostics/source-diagnostic.ts
300
+ import { runCommand as runCommand2, validateSourceShape } from "@caatinga/core";
300
301
  async function sourceDiagnostic(source) {
301
302
  if (!source) return void 0;
302
303
  const unsafeSource = validateSourceShape(source);
@@ -308,7 +309,7 @@ async function sourceDiagnostic(source) {
308
309
  };
309
310
  }
310
311
  try {
311
- await runCommand("stellar", ["keys", "public-key", source]);
312
+ await runCommand2("stellar", ["keys", "public-key", source]);
312
313
  return { ok: true, label: `source identity ${source} found` };
313
314
  } catch {
314
315
  return {
@@ -318,8 +319,64 @@ async function sourceDiagnostic(source) {
318
319
  };
319
320
  }
320
321
  }
322
+
323
+ // src/diagnostics/stellar-diagnostic.ts
324
+ import {
325
+ CaatingaError as CaatingaError4,
326
+ checkStellarCliVersion
327
+ } from "@caatinga/core";
328
+ async function stellarDiagnostic() {
329
+ const warnings = [];
330
+ try {
331
+ const report = await checkStellarCliVersion({
332
+ onWarning: (warning) => {
333
+ warnings.push({ code: warning.code, message: warning.message });
334
+ }
335
+ });
336
+ return {
337
+ ok: true,
338
+ label: `Stellar CLI ${report.version}`,
339
+ warnings: warnings.length > 0 ? warnings : void 0
340
+ };
341
+ } catch (error) {
342
+ if (error instanceof CaatingaError4) {
343
+ return {
344
+ ok: false,
345
+ label: "Stellar CLI not ready",
346
+ fix: error.hint ?? "Install Stellar CLI: cargo install --locked stellar-cli --version 25.2.0"
347
+ };
348
+ }
349
+ logger.error(String(error));
350
+ return {
351
+ ok: false,
352
+ label: "Stellar CLI not ready",
353
+ fix: "Install Stellar CLI: cargo install --locked stellar-cli --version 25.2.0"
354
+ };
355
+ }
356
+ }
357
+
358
+ // src/diagnostics/run-all.ts
359
+ async function runAllDiagnostics(options) {
360
+ return [
361
+ nodeDiagnostic(),
362
+ await stellarDiagnostic(),
363
+ await rustDiagnostic(),
364
+ await wasmTargetDiagnostic(),
365
+ await configDiagnostic(),
366
+ await artifactsDiagnostic(),
367
+ await networkDiagnostic(options.network),
368
+ await sourceDiagnostic(options.source)
369
+ ].filter((diagnostic) => diagnostic !== void 0);
370
+ }
371
+
372
+ // src/diagnostics/types.ts
321
373
  function printDiagnostic(diagnostic) {
322
- logger.info(`${diagnostic.ok ? "\u2713" : "\u2717"} ${diagnostic.label}`);
374
+ const warnings = diagnostic.warnings ?? [];
375
+ const suffix = warnings.length > 0 ? ` (${warnings.length} warning${warnings.length === 1 ? "" : "s"})` : "";
376
+ logger.info(`${diagnostic.ok ? "\u2713" : "\u2717"} ${diagnostic.label}${suffix}`);
377
+ for (const warning of warnings) {
378
+ logger.info(` ! ${warning.code}: ${warning.message}`);
379
+ }
323
380
  }
324
381
  function printFixes(diagnostics) {
325
382
  const failures = diagnostics.filter((diagnostic) => !diagnostic.ok);
@@ -330,6 +387,8 @@ function printFixes(diagnostics) {
330
387
  if (failure.fix) logger.info(failure.fix);
331
388
  }
332
389
  }
390
+
391
+ // src/commands/doctor.command.ts
333
392
  function printDeployCoverageLine(line) {
334
393
  if (line.ok) {
335
394
  logger.info(`\u2713 ${line.name} \u2014 ${line.contractId}`);
@@ -347,28 +406,19 @@ async function reportDeployCoverage(networkName) {
347
406
  }
348
407
  if (!coverage.complete) {
349
408
  const missing = coverage.lines.filter((line) => !line.ok).map((line) => line.name);
350
- throw new CaatingaError2(
409
+ throw new CaatingaError5(
351
410
  `Not all configured contracts are deployed on ${networkName}.`,
352
- CaatingaErrorCode2.DOCTOR_PARTIAL_DEPLOY,
411
+ CaatingaErrorCode3.DOCTOR_PARTIAL_DEPLOY,
353
412
  `Deploy missing contracts: ${missing.join(", ")}. See the commands above.`
354
413
  );
355
414
  }
356
415
  return true;
357
416
  }
358
417
  function registerDoctorCommand(program2) {
359
- 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").option("--allow-untested-stellar-cli", "Allow local use of a Stellar CLI version newer than Caatinga's tested maximum").action((options) => runCliAction(async () => {
418
+ 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 () => {
360
419
  logger.info("Caatinga Doctor");
361
420
  logger.info("");
362
- const diagnostics = [
363
- nodeDiagnostic(),
364
- await stellarDiagnostic(options.allowUntestedStellarCli === true),
365
- await rustDiagnostic(),
366
- await wasmTargetDiagnostic(),
367
- await configDiagnostic(),
368
- await artifactsDiagnostic(),
369
- await networkDiagnostic(options.network),
370
- await sourceDiagnostic(options.source)
371
- ].filter((diagnostic) => diagnostic !== void 0);
421
+ const diagnostics = await runAllDiagnostics(options);
372
422
  for (const diagnostic of diagnostics) {
373
423
  printDiagnostic(diagnostic);
374
424
  }
@@ -393,13 +443,12 @@ function registerDoctorCommand(program2) {
393
443
  // src/commands/generate.command.ts
394
444
  import { generateBindings, loadConfig as loadConfig5 } from "@caatinga/core";
395
445
  function registerGenerateCommand(program2) {
396
- program2.command("generate").description("Generate TypeScript bindings for a deployed contract").argument("<contract>", "Contract name").option("-n, --network <network>", "Configured network name").option("--allow-untested-stellar-cli", "Allow local use of a Stellar CLI version newer than Caatinga's tested maximum").action((contractName, options) => runCliAction(async () => {
446
+ program2.command("generate").description("Generate TypeScript bindings for a deployed contract").argument("<contract>", "Contract name").option("-n, --network <network>", "Configured network name").action((contractName, options) => runCliAction(async () => {
397
447
  const config = await loadConfig5();
398
448
  const result = await generateBindings({
399
449
  config,
400
450
  contractName,
401
- networkName: options.network,
402
- allowUntestedStellarCli: options.allowUntestedStellarCli === true
451
+ networkName: options.network
403
452
  });
404
453
  logger.success("Client generated");
405
454
  logger.info("");
@@ -417,7 +466,7 @@ import { createProjectFromTemplate } from "@caatinga/core";
417
466
  import { access as access2 } from "fs/promises";
418
467
  import path from "path";
419
468
  import { fileURLToPath } from "url";
420
- import { CaatingaError as CaatingaError3, CaatingaErrorCode as CaatingaErrorCode3 } from "@caatinga/core";
469
+ import { CaatingaError as CaatingaError6, CaatingaErrorCode as CaatingaErrorCode4 } from "@caatinga/core";
421
470
  async function resolveTemplateDir(templateName) {
422
471
  const candidates = buildTemplateCandidates(templateName);
423
472
  if (process.env.CAATINGA_DEBUG_TEMPLATE_RESOLUTION === "1") {
@@ -439,9 +488,9 @@ async function resolveTemplateDir(templateName) {
439
488
  } catch {
440
489
  }
441
490
  }
442
- throw new CaatingaError3(
491
+ throw new CaatingaError6(
443
492
  `Template "${templateName}" was not found.`,
444
- CaatingaErrorCode3.TEMPLATE_NOT_FOUND,
493
+ CaatingaErrorCode4.TEMPLATE_NOT_FOUND,
445
494
  "Set CAATINGA_TEMPLATES_DIR, run `pnpm build` before `pnpm pack`, or run from a Caatinga checkout that includes packages/templates."
446
495
  );
447
496
  }
@@ -498,15 +547,14 @@ function registerInitCommand(program2) {
498
547
  // src/commands/invoke.command.ts
499
548
  import { invokeContract, loadConfig as loadConfig6 } from "@caatinga/core";
500
549
  function registerInvokeCommand(program2) {
501
- 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)").option("--allow-untested-stellar-cli", "Allow local use of a Stellar CLI version newer than Caatinga's tested maximum").allowUnknownOption(true).allowExcessArguments(true).action((target, args, options) => runCliAction(async () => {
550
+ 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 () => {
502
551
  const config = await loadConfig6();
503
552
  const result = await invokeContract({
504
553
  config,
505
554
  target,
506
555
  args,
507
556
  networkName: options.network,
508
- source: options.source,
509
- allowUntestedStellarCli: options.allowUntestedStellarCli === true
557
+ source: options.source
510
558
  });
511
559
  logger.success("Invoke complete");
512
560
  logger.info("");
@@ -521,7 +569,7 @@ function registerInvokeCommand(program2) {
521
569
  }
522
570
 
523
571
  // src/version.ts
524
- var CAATINGA_CLI_VERSION = "0.2.4";
572
+ var CAATINGA_CLI_VERSION = "2.0.0";
525
573
 
526
574
  // src/program.ts
527
575
  function createProgram() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caatinga/cli",
3
- "version": "0.2.4",
3
+ "version": "2.0.0",
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": "^0.2.4",
46
+ "@caatinga/core": "^2.0.0",
47
47
  "chalk": "^5.4.1",
48
48
  "commander": "^12.1.0"
49
49
  },
@@ -55,6 +55,7 @@
55
55
  },
56
56
  "scripts": {
57
57
  "build": "tsup src/index.ts --format esm --dts --clean && rm -rf ./templates && cp -r ../templates ./templates",
58
+ "predev": "pnpm --filter @caatinga/core build",
58
59
  "dev": "tsx src/index.ts",
59
60
  "test": "vitest run --pool=threads",
60
61
  "typecheck": "tsc --noEmit"
@@ -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": "^0.2.4",
6
+ "compatibleCore": "^2.0.0",
7
7
  "templateVersion": 1
8
8
  },
9
9
  "frontend": {
@@ -0,0 +1,86 @@
1
+ {
2
+ "generators": {
3
+ "address": 2,
4
+ "nonce": 0
5
+ },
6
+ "auth": [
7
+ [],
8
+ [],
9
+ []
10
+ ],
11
+ "ledger": {
12
+ "protocol_version": 22,
13
+ "sequence_number": 0,
14
+ "timestamp": 0,
15
+ "network_id": "0000000000000000000000000000000000000000000000000000000000000000",
16
+ "base_reserve": 0,
17
+ "min_persistent_entry_ttl": 4096,
18
+ "min_temp_entry_ttl": 16,
19
+ "max_entry_ttl": 6312000,
20
+ "ledger_entries": [
21
+ [
22
+ {
23
+ "contract_data": {
24
+ "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4",
25
+ "key": "ledger_key_contract_instance",
26
+ "durability": "persistent"
27
+ }
28
+ },
29
+ [
30
+ {
31
+ "last_modified_ledger_seq": 0,
32
+ "data": {
33
+ "contract_data": {
34
+ "ext": "v0",
35
+ "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4",
36
+ "key": "ledger_key_contract_instance",
37
+ "durability": "persistent",
38
+ "val": {
39
+ "contract_instance": {
40
+ "executable": {
41
+ "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
42
+ },
43
+ "storage": [
44
+ {
45
+ "key": {
46
+ "symbol": "TOKEN"
47
+ },
48
+ "val": {
49
+ "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM"
50
+ }
51
+ }
52
+ ]
53
+ }
54
+ }
55
+ }
56
+ },
57
+ "ext": "v0"
58
+ },
59
+ 4095
60
+ ]
61
+ ],
62
+ [
63
+ {
64
+ "contract_code": {
65
+ "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
66
+ }
67
+ },
68
+ [
69
+ {
70
+ "last_modified_ledger_seq": 0,
71
+ "data": {
72
+ "contract_code": {
73
+ "ext": "v0",
74
+ "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
75
+ "code": ""
76
+ }
77
+ },
78
+ "ext": "v0"
79
+ },
80
+ 4095
81
+ ]
82
+ ]
83
+ ]
84
+ },
85
+ "events": []
86
+ }
@@ -12,15 +12,15 @@
12
12
  "caatinga:generate": "caatinga generate token && caatinga generate marketplace"
13
13
  },
14
14
  "dependencies": {
15
- "@caatinga/client": "^0.2.4",
16
- "@caatinga/core": "^0.2.4",
15
+ "@caatinga/client": "^2.0.0",
16
+ "@caatinga/core": "^2.0.0",
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": "^0.2.4",
23
+ "@caatinga/cli": "^2.0.0",
24
24
  "@types/react": "^18.3.18",
25
25
  "@types/react-dom": "^18.3.5",
26
26
  "typescript": "^5.7.2"
@@ -5,16 +5,32 @@ Caatinga counter dApp for Stellar/Soroban.
5
5
  ## CLI Flow
6
6
 
7
7
  ```bash
8
- npm install
8
+ npm install # or: pnpm install
9
9
  npx caatinga build counter
10
10
  npx caatinga deploy counter --network testnet --source alice
11
11
  npx caatinga generate counter --network testnet
12
12
  npx caatinga invoke counter.increment --network testnet --source alice
13
- npm run dev
13
+ npm run dev # or: pnpm dev
14
14
  ```
15
15
 
16
+ Run `build` before `deploy` (WASM required) and `deploy` before `generate` (contract ID required).
17
+
16
18
  Use a local Stellar CLI identity alias for `--source`; public `G...` addresses, seed phrases, and secret keys are rejected for signing operations.
17
19
 
20
+ ## Package managers
21
+
22
+ Templates default to npm, but pnpm 10.26+/11.x is supported via the shipped `pnpm-workspace.yaml` (`allowBuilds.esbuild: true`, `blockExoticSubdeps: false`).
23
+
24
+ Package scripts wrap the CLI:
25
+
26
+ ```bash
27
+ npm run caatinga:build
28
+ npm run caatinga:deploy -- --network testnet --source alice
29
+ npm run caatinga:generate -- --network testnet
30
+ ```
31
+
32
+ With pnpm, use `pnpm run caatinga:build` (and the same pattern for deploy/generate). `npx caatinga build counter` works without going through the package manager.
33
+
18
34
  ## Client Smoke Path
19
35
 
20
36
  After `caatinga generate`, wire generated bindings to the client:
@@ -3,7 +3,7 @@
3
3
  "version": "0.1.0",
4
4
  "description": "Minimal Vite + React + Soroban counter dApp.",
5
5
  "caatinga": {
6
- "compatibleCore": "^0.2.4",
6
+ "compatibleCore": "^2.0.0",
7
7
  "templateVersion": 1
8
8
  },
9
9
  "frontend": {
@@ -1,20 +1,49 @@
1
1
  #![no_std]
2
2
 
3
- use soroban_sdk::{contract, contractimpl, Env};
3
+ use soroban_sdk::{contract, contracterror, contractimpl, contracttype, Env};
4
+
5
+ const INSTANCE_TTL_THRESHOLD: u32 = 100;
6
+ const INSTANCE_TTL_EXTEND_TO: u32 = 518_400;
7
+
8
+ #[contracttype]
9
+ #[derive(Clone)]
10
+ pub enum DataKey {
11
+ Count,
12
+ }
13
+
14
+ #[contracterror]
15
+ #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
16
+ #[repr(u32)]
17
+ pub enum CounterError {
18
+ Overflow = 1,
19
+ }
4
20
 
5
21
  #[contract]
6
22
  pub struct CounterContract;
7
23
 
24
+ fn refresh_instance_ttl(env: &Env) {
25
+ env.storage()
26
+ .instance()
27
+ .extend_ttl(INSTANCE_TTL_THRESHOLD, INSTANCE_TTL_EXTEND_TO);
28
+ }
29
+
8
30
  #[contractimpl]
9
31
  impl CounterContract {
10
32
  pub fn get(env: Env) -> u32 {
11
- env.storage().instance().get(&"count").unwrap_or(0)
33
+ let count = env.storage().instance().get(&DataKey::Count).unwrap_or(0);
34
+ refresh_instance_ttl(&env);
35
+ count
12
36
  }
13
37
 
14
- pub fn increment(env: Env) -> u32 {
15
- let count = Self::get(env.clone()) + 1;
16
- env.storage().instance().set(&"count", &count);
17
- count
38
+ pub fn increment(env: Env) -> Result<u32, CounterError> {
39
+ let count = Self::get(env.clone())
40
+ .checked_add(1)
41
+ .ok_or(CounterError::Overflow)?;
42
+
43
+ env.storage().instance().set(&DataKey::Count, &count);
44
+ refresh_instance_ttl(&env);
45
+
46
+ Ok(count)
18
47
  }
19
48
  }
20
49
 
@@ -33,4 +62,39 @@ mod test {
33
62
  assert_eq!(client.increment(), 1);
34
63
  assert_eq!(client.get(), 1);
35
64
  }
65
+
66
+ #[test]
67
+ fn get_returns_zero_before_increment() {
68
+ let env = Env::default();
69
+ let contract_id = env.register(CounterContract, ());
70
+ let client = CounterContractClient::new(&env, &contract_id);
71
+
72
+ assert_eq!(client.get(), 0);
73
+ }
74
+
75
+ #[test]
76
+ fn repeated_increments_preserve_state() {
77
+ let env = Env::default();
78
+ let contract_id = env.register(CounterContract, ());
79
+ let client = CounterContractClient::new(&env, &contract_id);
80
+
81
+ assert_eq!(client.increment(), 1);
82
+ assert_eq!(client.increment(), 2);
83
+ assert_eq!(client.increment(), 3);
84
+ assert_eq!(client.get(), 3);
85
+ }
86
+
87
+ #[test]
88
+ fn increment_returns_overflow_error() {
89
+ let env = Env::default();
90
+ let contract_id = env.register(CounterContract, ());
91
+ let client = CounterContractClient::new(&env, &contract_id);
92
+
93
+ env.as_contract(&contract_id, || {
94
+ env.storage().instance().set(&DataKey::Count, &u32::MAX);
95
+ });
96
+
97
+ assert_eq!(client.try_increment(), Err(Ok(CounterError::Overflow)));
98
+ assert_eq!(client.get(), u32::MAX);
99
+ }
36
100
  }
@@ -0,0 +1,76 @@
1
+ {
2
+ "generators": {
3
+ "address": 1,
4
+ "nonce": 0
5
+ },
6
+ "auth": [
7
+ [],
8
+ []
9
+ ],
10
+ "ledger": {
11
+ "protocol_version": 22,
12
+ "sequence_number": 0,
13
+ "timestamp": 0,
14
+ "network_id": "0000000000000000000000000000000000000000000000000000000000000000",
15
+ "base_reserve": 0,
16
+ "min_persistent_entry_ttl": 4096,
17
+ "min_temp_entry_ttl": 16,
18
+ "max_entry_ttl": 6312000,
19
+ "ledger_entries": [
20
+ [
21
+ {
22
+ "contract_data": {
23
+ "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
24
+ "key": "ledger_key_contract_instance",
25
+ "durability": "persistent"
26
+ }
27
+ },
28
+ [
29
+ {
30
+ "last_modified_ledger_seq": 0,
31
+ "data": {
32
+ "contract_data": {
33
+ "ext": "v0",
34
+ "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
35
+ "key": "ledger_key_contract_instance",
36
+ "durability": "persistent",
37
+ "val": {
38
+ "contract_instance": {
39
+ "executable": {
40
+ "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
41
+ },
42
+ "storage": null
43
+ }
44
+ }
45
+ }
46
+ },
47
+ "ext": "v0"
48
+ },
49
+ 4095
50
+ ]
51
+ ],
52
+ [
53
+ {
54
+ "contract_code": {
55
+ "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
56
+ }
57
+ },
58
+ [
59
+ {
60
+ "last_modified_ledger_seq": 0,
61
+ "data": {
62
+ "contract_code": {
63
+ "ext": "v0",
64
+ "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
65
+ "code": ""
66
+ }
67
+ },
68
+ "ext": "v0"
69
+ },
70
+ 4095
71
+ ]
72
+ ]
73
+ ]
74
+ },
75
+ "events": []
76
+ }
@@ -0,0 +1,91 @@
1
+ {
2
+ "generators": {
3
+ "address": 1,
4
+ "nonce": 0
5
+ },
6
+ "auth": [
7
+ [],
8
+ [],
9
+ [],
10
+ []
11
+ ],
12
+ "ledger": {
13
+ "protocol_version": 22,
14
+ "sequence_number": 0,
15
+ "timestamp": 0,
16
+ "network_id": "0000000000000000000000000000000000000000000000000000000000000000",
17
+ "base_reserve": 0,
18
+ "min_persistent_entry_ttl": 4096,
19
+ "min_temp_entry_ttl": 16,
20
+ "max_entry_ttl": 6312000,
21
+ "ledger_entries": [
22
+ [
23
+ {
24
+ "contract_data": {
25
+ "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
26
+ "key": "ledger_key_contract_instance",
27
+ "durability": "persistent"
28
+ }
29
+ },
30
+ [
31
+ {
32
+ "last_modified_ledger_seq": 0,
33
+ "data": {
34
+ "contract_data": {
35
+ "ext": "v0",
36
+ "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
37
+ "key": "ledger_key_contract_instance",
38
+ "durability": "persistent",
39
+ "val": {
40
+ "contract_instance": {
41
+ "executable": {
42
+ "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
43
+ },
44
+ "storage": [
45
+ {
46
+ "key": {
47
+ "vec": [
48
+ {
49
+ "symbol": "Count"
50
+ }
51
+ ]
52
+ },
53
+ "val": {
54
+ "u32": 4294967295
55
+ }
56
+ }
57
+ ]
58
+ }
59
+ }
60
+ }
61
+ },
62
+ "ext": "v0"
63
+ },
64
+ 4095
65
+ ]
66
+ ],
67
+ [
68
+ {
69
+ "contract_code": {
70
+ "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
71
+ }
72
+ },
73
+ [
74
+ {
75
+ "last_modified_ledger_seq": 0,
76
+ "data": {
77
+ "contract_code": {
78
+ "ext": "v0",
79
+ "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
80
+ "code": ""
81
+ }
82
+ },
83
+ "ext": "v0"
84
+ },
85
+ 4095
86
+ ]
87
+ ]
88
+ ]
89
+ },
90
+ "events": []
91
+ }
@@ -0,0 +1,91 @@
1
+ {
2
+ "generators": {
3
+ "address": 1,
4
+ "nonce": 0
5
+ },
6
+ "auth": [
7
+ [],
8
+ [],
9
+ [],
10
+ []
11
+ ],
12
+ "ledger": {
13
+ "protocol_version": 22,
14
+ "sequence_number": 0,
15
+ "timestamp": 0,
16
+ "network_id": "0000000000000000000000000000000000000000000000000000000000000000",
17
+ "base_reserve": 0,
18
+ "min_persistent_entry_ttl": 4096,
19
+ "min_temp_entry_ttl": 16,
20
+ "max_entry_ttl": 6312000,
21
+ "ledger_entries": [
22
+ [
23
+ {
24
+ "contract_data": {
25
+ "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
26
+ "key": "ledger_key_contract_instance",
27
+ "durability": "persistent"
28
+ }
29
+ },
30
+ [
31
+ {
32
+ "last_modified_ledger_seq": 0,
33
+ "data": {
34
+ "contract_data": {
35
+ "ext": "v0",
36
+ "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
37
+ "key": "ledger_key_contract_instance",
38
+ "durability": "persistent",
39
+ "val": {
40
+ "contract_instance": {
41
+ "executable": {
42
+ "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
43
+ },
44
+ "storage": [
45
+ {
46
+ "key": {
47
+ "vec": [
48
+ {
49
+ "symbol": "Count"
50
+ }
51
+ ]
52
+ },
53
+ "val": {
54
+ "u32": 1
55
+ }
56
+ }
57
+ ]
58
+ }
59
+ }
60
+ }
61
+ },
62
+ "ext": "v0"
63
+ },
64
+ 4095
65
+ ]
66
+ ],
67
+ [
68
+ {
69
+ "contract_code": {
70
+ "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
71
+ }
72
+ },
73
+ [
74
+ {
75
+ "last_modified_ledger_seq": 0,
76
+ "data": {
77
+ "contract_code": {
78
+ "ext": "v0",
79
+ "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
80
+ "code": ""
81
+ }
82
+ },
83
+ "ext": "v0"
84
+ },
85
+ 4095
86
+ ]
87
+ ]
88
+ ]
89
+ },
90
+ "events": []
91
+ }
@@ -0,0 +1,92 @@
1
+ {
2
+ "generators": {
3
+ "address": 1,
4
+ "nonce": 0
5
+ },
6
+ "auth": [
7
+ [],
8
+ [],
9
+ [],
10
+ [],
11
+ []
12
+ ],
13
+ "ledger": {
14
+ "protocol_version": 22,
15
+ "sequence_number": 0,
16
+ "timestamp": 0,
17
+ "network_id": "0000000000000000000000000000000000000000000000000000000000000000",
18
+ "base_reserve": 0,
19
+ "min_persistent_entry_ttl": 4096,
20
+ "min_temp_entry_ttl": 16,
21
+ "max_entry_ttl": 6312000,
22
+ "ledger_entries": [
23
+ [
24
+ {
25
+ "contract_data": {
26
+ "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
27
+ "key": "ledger_key_contract_instance",
28
+ "durability": "persistent"
29
+ }
30
+ },
31
+ [
32
+ {
33
+ "last_modified_ledger_seq": 0,
34
+ "data": {
35
+ "contract_data": {
36
+ "ext": "v0",
37
+ "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
38
+ "key": "ledger_key_contract_instance",
39
+ "durability": "persistent",
40
+ "val": {
41
+ "contract_instance": {
42
+ "executable": {
43
+ "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
44
+ },
45
+ "storage": [
46
+ {
47
+ "key": {
48
+ "vec": [
49
+ {
50
+ "symbol": "Count"
51
+ }
52
+ ]
53
+ },
54
+ "val": {
55
+ "u32": 3
56
+ }
57
+ }
58
+ ]
59
+ }
60
+ }
61
+ }
62
+ },
63
+ "ext": "v0"
64
+ },
65
+ 4095
66
+ ]
67
+ ],
68
+ [
69
+ {
70
+ "contract_code": {
71
+ "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
72
+ }
73
+ },
74
+ [
75
+ {
76
+ "last_modified_ledger_seq": 0,
77
+ "data": {
78
+ "contract_code": {
79
+ "ext": "v0",
80
+ "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
81
+ "code": ""
82
+ }
83
+ },
84
+ "ext": "v0"
85
+ },
86
+ 4095
87
+ ]
88
+ ]
89
+ ]
90
+ },
91
+ "events": []
92
+ }
@@ -12,9 +12,9 @@
12
12
  "caatinga:generate": "caatinga generate counter"
13
13
  },
14
14
  "dependencies": {
15
- "@caatinga/client": "^0.2.4",
16
- "@caatinga/core": "^0.2.4",
17
- "@creit.tech/xbull-wallet-connect": "github:Creit-Tech/xBull-Wallet-Connect",
15
+ "@caatinga/client": "^2.0.0",
16
+ "@caatinga/core": "^2.0.0",
17
+ "@creit.tech/xbull-wallet-connect": "^0.4.0",
18
18
  "@vitejs/plugin-react": "^4.3.4",
19
19
  "react": "^18.3.1",
20
20
  "react-dom": "^18.3.1",
@@ -22,7 +22,7 @@
22
22
  "vite": "^6.0.6"
23
23
  },
24
24
  "devDependencies": {
25
- "@caatinga/cli": "^0.2.4",
25
+ "@caatinga/cli": "^2.0.0",
26
26
  "@types/react": "^18.3.18",
27
27
  "@types/react-dom": "^18.3.5",
28
28
  "typescript": "^5.7.2"
@@ -0,0 +1,12 @@
1
+ # pnpm 10.26+ / 11.x block lifecycle scripts by default; vite depends on esbuild.
2
+ allowBuilds:
3
+ esbuild: true
4
+
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
@@ -1,4 +1,4 @@
1
- import { useCallback, useMemo, useState } from "react";
1
+ import { useCallback, useEffect, useMemo, useState } from "react";
2
2
  import { caatingaClient } from "../caatinga.js";
3
3
  import { CaatingaError } from "@caatinga/core/browser";
4
4
 
@@ -15,7 +15,7 @@ export function CounterCard() {
15
15
  const [loading, setLoading] = useState(false);
16
16
  const [error, setError] = useState<string | null>(null);
17
17
  const formattedCount = useMemo(
18
- () => (count === null ? "Unknown" : new Intl.NumberFormat().format(count)),
18
+ () => (count === null ? "Not loaded" : new Intl.NumberFormat().format(count)),
19
19
  [count]
20
20
  );
21
21
 
@@ -33,6 +33,10 @@ export function CounterCard() {
33
33
  }
34
34
  }, []);
35
35
 
36
+ useEffect(() => {
37
+ void refresh();
38
+ }, [refresh]);
39
+
36
40
  async function increment() {
37
41
  setLoading(true);
38
42
  setError(null);
@@ -45,7 +45,7 @@ export class Client {
45
45
  ) {}
46
46
 
47
47
  increment(): ExampleTransaction {
48
- return new ExampleTransaction("increment");
48
+ return new ExampleTransaction("increment", 1);
49
49
  }
50
50
 
51
51
  get(): ExampleTransaction {
@@ -1,3 +1,4 @@
1
+ // Keep in sync with examples/counter-web/src/wallet.ts (WalletConnect defaults differ per project).
1
2
  import {
2
3
  createStellarWalletsKitAdapter,
3
4
  type StellarWalletsKitMetadata
@@ -4,7 +4,9 @@ import { createRequire } from "node:module";
4
4
  import { dirname, join } from "node:path";
5
5
 
6
6
  const require = createRequire(import.meta.url);
7
- const xbullWalletConnectRoot = dirname(require.resolve("@creit.tech/xbull-wallet-connect/package.json"));
7
+ const xbullWalletConnectRoot = dirname(
8
+ require.resolve("@creit-tech/xbull-wallet-connect/package.json")
9
+ );
8
10
 
9
11
  export default defineConfig({
10
12
  plugins: [react()],