@caatinga/cli 2.0.2 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -6
- package/dist/index.js +226 -17
- package/package.json +3 -3
- package/templates/marketplace-with-token/caatinga.template.json +1 -1
- package/templates/marketplace-with-token/package.json +3 -3
- package/templates/react-vite-counter/caatinga.template.json +1 -1
- package/templates/react-vite-counter/package.json +7 -7
- package/templates/react-vite-counter/pnpm-workspace.yaml +4 -10
- package/templates/react-vite-counter/src/App.tsx +17 -3
- package/templates/react-vite-counter/src/components/ContractNotDeployed.tsx +27 -0
- package/templates/react-vite-counter/src/components/CounterCard.tsx +2 -10
- package/templates/react-vite-counter/src/components/WalletButton.tsx +8 -7
- package/templates/react-vite-counter/src/components/WalletModal.tsx +248 -0
- package/templates/react-vite-counter/src/contracts/generated/counter/src/index.ts +23 -38
- package/templates/react-vite-counter/src/stubs/empty-wallet-dep/index.cjs +3 -0
- package/templates/react-vite-counter/src/stubs/empty-wallet-dep/package.json +6 -0
- package/templates/react-vite-counter/src/stubs/hot-wallet-sdk/index.cjs +7 -0
- package/templates/react-vite-counter/src/stubs/hot-wallet-sdk/package.json +6 -0
- package/templates/react-vite-counter/src/styles.css +261 -0
- package/templates/react-vite-counter/src/wallet-modal-controller.ts +73 -0
- package/templates/react-vite-counter/src/wallet.ts +9 -1
- package/templates/react-vite-counter/vite.config.ts +17 -1
- package/templates/react-vite-counter/src/context/WalletContext.tsx +0 -64
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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,6 +204,45 @@ function registerDeployCommand(program2) {
|
|
|
197
204
|
logger.info(` Contract ID: ${contract.contractId}`);
|
|
198
205
|
}
|
|
199
206
|
logger.info("Artifacts updated: caatinga.artifacts.json");
|
|
207
|
+
if (result.deployedContracts.length === 0) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (options.generate === false) {
|
|
211
|
+
logger.info("");
|
|
212
|
+
logger.info("Bindings generation skipped (--no-generate).");
|
|
213
|
+
logger.info("Next:");
|
|
214
|
+
for (const contract of result.deployedContracts) {
|
|
215
|
+
logger.info(` npx caatinga generate ${contract.name} --network ${result.network.name}`);
|
|
216
|
+
}
|
|
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);
|
|
236
|
+
logger.info("");
|
|
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}`);
|
|
245
|
+
}
|
|
200
246
|
}));
|
|
201
247
|
}
|
|
202
248
|
|
|
@@ -388,6 +434,33 @@ function printFixes(diagnostics) {
|
|
|
388
434
|
}
|
|
389
435
|
}
|
|
390
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
|
+
|
|
391
464
|
// src/commands/doctor.command.ts
|
|
392
465
|
function printDeployCoverageLine(line) {
|
|
393
466
|
if (line.ok) {
|
|
@@ -414,6 +487,25 @@ async function reportDeployCoverage(networkName) {
|
|
|
414
487
|
}
|
|
415
488
|
return true;
|
|
416
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
|
+
}
|
|
417
509
|
function registerDoctorCommand(program2) {
|
|
418
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 () => {
|
|
419
511
|
logger.info("Caatinga Doctor");
|
|
@@ -431,6 +523,7 @@ function registerDoctorCommand(program2) {
|
|
|
431
523
|
ready = false;
|
|
432
524
|
throw error;
|
|
433
525
|
}
|
|
526
|
+
await reportBindingCoverage(options.network);
|
|
434
527
|
}
|
|
435
528
|
logger.info("");
|
|
436
529
|
logger.info(`Status: ${ready ? "ready" : "blocked"}`);
|
|
@@ -441,23 +534,56 @@ function registerDoctorCommand(program2) {
|
|
|
441
534
|
}
|
|
442
535
|
|
|
443
536
|
// src/commands/generate.command.ts
|
|
444
|
-
import {
|
|
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
|
+
}
|
|
445
565
|
function registerGenerateCommand(program2) {
|
|
446
|
-
program2.command("generate").description("Generate TypeScript bindings for
|
|
447
|
-
const config = await
|
|
448
|
-
|
|
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 () => {
|
|
567
|
+
const config = await loadConfig6();
|
|
568
|
+
if (!contractName) {
|
|
569
|
+
await printFreshnessPreState(config, options.network);
|
|
570
|
+
}
|
|
571
|
+
const { network, results } = await generateBindingsGraph2({
|
|
449
572
|
config,
|
|
450
573
|
contractName,
|
|
451
574
|
networkName: options.network
|
|
452
575
|
});
|
|
453
576
|
logger.success("Client generated");
|
|
454
577
|
logger.info("");
|
|
455
|
-
logger.info(`
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
logger.info(`
|
|
578
|
+
logger.info(`Network: ${network.name}`);
|
|
579
|
+
for (const result of results) {
|
|
580
|
+
logger.info("");
|
|
581
|
+
logger.info(`Contract: ${result.contractName}`);
|
|
582
|
+
logger.info(`Output: ${result.outputDir}`);
|
|
583
|
+
logger.info(`Import path: ${result.importPath}`);
|
|
584
|
+
if (result.legacyStubRemoved) {
|
|
585
|
+
logger.info(`Removed legacy stub: ${config.frontend.bindingsOutput}/${result.contractName}.ts`);
|
|
586
|
+
}
|
|
461
587
|
}
|
|
462
588
|
logger.info("");
|
|
463
589
|
logger.info("Next: import bindings from the import path above, then run npm run dev");
|
|
@@ -540,21 +666,34 @@ function registerInitCommand(program2) {
|
|
|
540
666
|
logger.info(`Template: ${result.template.name}@${result.template.version}`);
|
|
541
667
|
logger.info(`Path: ${targetDir}`);
|
|
542
668
|
logger.info("");
|
|
669
|
+
const defaultContract = result.template.contracts.default;
|
|
543
670
|
logger.info("Next steps:");
|
|
544
671
|
logger.info(` cd ${projectDirectory}`);
|
|
545
672
|
logger.info(" npm install");
|
|
546
|
-
|
|
673
|
+
if (defaultContract) {
|
|
674
|
+
logger.info(` npx caatinga build ${defaultContract}`);
|
|
675
|
+
logger.info(
|
|
676
|
+
` npx caatinga deploy ${defaultContract} --network testnet --source <identity>`
|
|
677
|
+
);
|
|
678
|
+
} else {
|
|
679
|
+
logger.info(" npx caatinga build");
|
|
680
|
+
logger.info(" npx caatinga deploy --network testnet --source <identity>");
|
|
681
|
+
}
|
|
682
|
+
logger.info(" npm run dev");
|
|
683
|
+
logger.info("");
|
|
547
684
|
logger.info(
|
|
548
|
-
|
|
685
|
+
"Note: deploy generates TypeScript bindings automatically (--no-generate to skip) \u2014"
|
|
549
686
|
);
|
|
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");
|
|
550
689
|
}));
|
|
551
690
|
}
|
|
552
691
|
|
|
553
692
|
// src/commands/invoke.command.ts
|
|
554
|
-
import { invokeContract, loadConfig as
|
|
693
|
+
import { invokeContract, loadConfig as loadConfig7 } from "@caatinga/core";
|
|
555
694
|
function registerInvokeCommand(program2) {
|
|
556
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 () => {
|
|
557
|
-
const config = await
|
|
696
|
+
const config = await loadConfig7();
|
|
558
697
|
const result = await invokeContract({
|
|
559
698
|
config,
|
|
560
699
|
target,
|
|
@@ -574,8 +713,77 @@ function registerInvokeCommand(program2) {
|
|
|
574
713
|
}));
|
|
575
714
|
}
|
|
576
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
|
+
|
|
577
785
|
// src/version.ts
|
|
578
|
-
var CAATINGA_CLI_VERSION = "2.0
|
|
786
|
+
var CAATINGA_CLI_VERSION = "2.2.0";
|
|
579
787
|
|
|
580
788
|
// src/program.ts
|
|
581
789
|
function createProgram() {
|
|
@@ -588,6 +796,7 @@ function createProgram() {
|
|
|
588
796
|
registerDeployCommand(program2);
|
|
589
797
|
registerGenerateCommand(program2);
|
|
590
798
|
registerInvokeCommand(program2);
|
|
799
|
+
registerStatusCommand(program2);
|
|
591
800
|
return program2;
|
|
592
801
|
}
|
|
593
802
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@caatinga/cli",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.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": "^2.0
|
|
46
|
+
"@caatinga/core": "^2.2.0",
|
|
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 &&
|
|
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",
|
|
@@ -12,15 +12,15 @@
|
|
|
12
12
|
"caatinga:generate": "caatinga generate token && caatinga generate marketplace"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@caatinga/client": "^2.0
|
|
16
|
-
"@caatinga/core": "^2.0
|
|
15
|
+
"@caatinga/client": "^2.2.0",
|
|
16
|
+
"@caatinga/core": "^2.2.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": "^2.0
|
|
23
|
+
"@caatinga/cli": "^2.2.0",
|
|
24
24
|
"@types/react": "^18.3.18",
|
|
25
25
|
"@types/react-dom": "^18.3.5",
|
|
26
26
|
"typescript": "^5.7.2"
|
|
@@ -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.0
|
|
16
|
-
"@caatinga/core": "^2.0
|
|
17
|
-
"@creit.tech/stellar-wallets-kit": "^
|
|
15
|
+
"@caatinga/client": "^2.2.0",
|
|
16
|
+
"@caatinga/core": "^2.2.0",
|
|
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,7 +23,7 @@
|
|
|
23
23
|
"vite": "^6.0.6"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@caatinga/cli": "^2.0
|
|
26
|
+
"@caatinga/cli": "^2.2.0",
|
|
27
27
|
"@types/react": "^18.3.18",
|
|
28
28
|
"@types/react-dom": "^18.3.5",
|
|
29
29
|
"typescript": "^5.7.2"
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
# pnpm
|
|
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. All dependency
|
|
3
|
+
# stubbing now happens at the build layer (see vite.config.ts), so no pnpm-only
|
|
4
|
+
# `overrides` are needed and this template installs identically on every PM.
|
|
2
5
|
allowBuilds:
|
|
3
6
|
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,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 {
|
|
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
|
-
{
|
|
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
|
-
|
|
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'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,17 +1,9 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
2
|
import { caatingaClient } from "../caatinga.js";
|
|
3
|
-
import {
|
|
4
|
-
import { useWallet } from "
|
|
3
|
+
import { formatCaatingaError } from "@caatinga/core/browser";
|
|
4
|
+
import { useWallet } from "@caatinga/client/react";
|
|
5
5
|
import { LoadingModal } from "./LoadingModal.js";
|
|
6
6
|
|
|
7
|
-
function formatCaatingaError(error: unknown): string {
|
|
8
|
-
if (error instanceof CaatingaError) {
|
|
9
|
-
return `[${error.code}] ${error.message}\n\n${error.hint}`;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
return error instanceof Error ? error.message : String(error);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
7
|
export function CounterCard() {
|
|
16
8
|
const { publicKey } = useWallet();
|
|
17
9
|
const [count, setCount] = useState<number | null>(null);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { useWallet } from "
|
|
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,
|
|
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={
|
|
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
|
-
{
|
|
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>
|