@caatinga/cli 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -419,12 +419,19 @@ import path from "path";
419
419
  import { fileURLToPath } from "url";
420
420
  import { CaatingaError as CaatingaError3, CaatingaErrorCode as CaatingaErrorCode3 } from "@caatinga/core";
421
421
  async function resolveTemplateDir(templateName) {
422
- const envTemplatesDir = process.env.CAATINGA_TEMPLATES_DIR;
423
- const candidates = [
424
- envTemplatesDir ? path.join(envTemplatesDir, templateName) : void 0,
425
- path.resolve(process.cwd(), "packages", "templates", templateName),
426
- ...candidatePathsFromModule(templateName)
427
- ].filter((candidate) => Boolean(candidate));
422
+ const candidates = buildTemplateCandidates(templateName);
423
+ if (process.env.CAATINGA_DEBUG_TEMPLATE_RESOLUTION === "1") {
424
+ const envValue = process.env.CAATINGA_TEMPLATES_DIR ?? "<unset>";
425
+ const cwd = process.cwd();
426
+ process.stderr.write(
427
+ `caatinga: template resolution candidates for "${templateName}": env=${envValue} cwd=${cwd}
428
+ `
429
+ );
430
+ for (const candidate of candidates) {
431
+ process.stderr.write(`caatinga: candidate ${candidate}
432
+ `);
433
+ }
434
+ }
428
435
  for (const candidate of candidates) {
429
436
  try {
430
437
  await access2(candidate);
@@ -435,9 +442,17 @@ async function resolveTemplateDir(templateName) {
435
442
  throw new CaatingaError3(
436
443
  `Template "${templateName}" was not found.`,
437
444
  CaatingaErrorCode3.TEMPLATE_NOT_FOUND,
438
- "Set CAATINGA_TEMPLATES_DIR or run from a Caatinga checkout that includes packages/templates."
445
+ "Set CAATINGA_TEMPLATES_DIR, run `pnpm build` before `pnpm pack`, or run from a Caatinga checkout that includes packages/templates."
439
446
  );
440
447
  }
448
+ function buildTemplateCandidates(templateName) {
449
+ const envTemplatesDir = process.env.CAATINGA_TEMPLATES_DIR;
450
+ return [
451
+ envTemplatesDir ? path.join(envTemplatesDir, templateName) : void 0,
452
+ path.resolve(process.cwd(), "packages", "templates", templateName),
453
+ ...candidatePathsFromModule(templateName)
454
+ ].filter((candidate) => Boolean(candidate));
455
+ }
441
456
  function candidatePathsFromModule(templateName) {
442
457
  const currentFile = fileURLToPath(import.meta.url);
443
458
  const start = path.dirname(currentFile);
@@ -506,7 +521,7 @@ function registerInvokeCommand(program2) {
506
521
  }
507
522
 
508
523
  // src/version.ts
509
- var CAATINGA_CLI_VERSION = "0.2.2";
524
+ var CAATINGA_CLI_VERSION = "0.2.4";
510
525
 
511
526
  // src/program.ts
512
527
  function createProgram() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caatinga/cli",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
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.2",
46
+ "@caatinga/core": "^0.2.4",
47
47
  "chalk": "^5.4.1",
48
48
  "commander": "^12.1.0"
49
49
  },
@@ -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.2",
6
+ "compatibleCore": "^0.2.4",
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": "^0.2.2",
16
- "@caatinga/core": "^0.2.2",
15
+ "@caatinga/client": "^0.2.4",
16
+ "@caatinga/core": "^0.2.4",
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.2",
23
+ "@caatinga/cli": "^0.2.4",
24
24
  "@types/react": "^18.3.18",
25
25
  "@types/react-dom": "^18.3.5",
26
26
  "typescript": "^5.7.2"
@@ -0,0 +1,5 @@
1
+ VITE_WALLETCONNECT_PROJECT_ID=your_walletconnect_project_id
2
+ VITE_APP_NAME=__PROJECT_NAME__
3
+ VITE_APP_DESCRIPTION=Caatinga counter dApp
4
+ VITE_APP_URL=https://your-dapp-url.com
5
+ VITE_APP_ICON_URL=https://your-dapp-url.com/icon.png
@@ -21,10 +21,12 @@ After `caatinga generate`, wire generated bindings to the client:
21
21
 
22
22
  ```ts
23
23
  import { createCaatingaClient } from "@caatinga/client";
24
- import { freighterWalletAdapter } from "@caatinga/client/freighter";
24
+ import { createStellarWalletsKitAdapter } from "@caatinga/client/stellar-wallets-kit";
25
25
  import * as Counter from "./contracts/generated/counter";
26
26
  import artifacts from "../caatinga.artifacts.json";
27
27
 
28
+ const wallet = createStellarWalletsKitAdapter();
29
+
28
30
  export const caatingaClient = createCaatingaClient({
29
31
  network: {
30
32
  name: "testnet",
@@ -32,7 +34,7 @@ export const caatingaClient = createCaatingaClient({
32
34
  networkPassphrase: "Test SDF Network ; September 2015"
33
35
  },
34
36
  artifacts,
35
- wallet: freighterWalletAdapter,
37
+ wallet,
36
38
  contracts: {
37
39
  counter: {
38
40
  binding: Counter
@@ -48,7 +50,14 @@ const tx = await caatingaClient.contract("counter").buildXdr("increment");
48
50
  console.log(tx.preparedXdr);
49
51
  ```
50
52
 
51
- Invoke through Freighter:
53
+ Read the on-chain counter through simulation:
54
+
55
+ ```ts
56
+ const count = await caatingaClient.contract("counter").read<number>("get");
57
+ console.log(count);
58
+ ```
59
+
60
+ Invoke through a connected wallet:
52
61
 
53
62
  ```ts
54
63
  const result = await caatingaClient.contract("counter").invoke("increment", {
@@ -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.2",
6
+ "compatibleCore": "^0.2.4",
7
7
  "templateVersion": 1
8
8
  },
9
9
  "frontend": {
@@ -12,16 +12,17 @@
12
12
  "caatinga:generate": "caatinga generate counter"
13
13
  },
14
14
  "dependencies": {
15
- "@caatinga/client": "^0.2.2",
16
- "@caatinga/core": "^0.2.2",
17
- "@stellar/freighter-api": "^4.0.0",
15
+ "@caatinga/client": "^0.2.4",
16
+ "@caatinga/core": "^0.2.4",
17
+ "@creit.tech/xbull-wallet-connect": "github:Creit-Tech/xBull-Wallet-Connect",
18
18
  "@vitejs/plugin-react": "^4.3.4",
19
- "vite": "^6.0.6",
20
19
  "react": "^18.3.1",
21
- "react-dom": "^18.3.1"
20
+ "react-dom": "^18.3.1",
21
+ "stellar-wallets-kit": "github:Creit-Tech/Stellar-Wallets-Kit#v0.0.7",
22
+ "vite": "^6.0.6"
22
23
  },
23
24
  "devDependencies": {
24
- "@caatinga/cli": "^0.2.2",
25
+ "@caatinga/cli": "^0.2.4",
25
26
  "@types/react": "^18.3.18",
26
27
  "@types/react-dom": "^18.3.5",
27
28
  "typescript": "^5.7.2"
@@ -1,8 +1,8 @@
1
1
  import { createCaatingaClient } from "@caatinga/client";
2
- import { freighterWalletAdapter } from "@caatinga/client/freighter";
3
2
  import type { CaatingaArtifacts } from "@caatinga/core/browser";
4
3
  import artifactsJson from "../caatinga.artifacts.json";
5
4
  import * as Counter from "./contracts/generated/counter.js";
5
+ import { stellarWalletAdapter } from "./wallet.js";
6
6
 
7
7
  const artifacts = artifactsJson as CaatingaArtifacts;
8
8
 
@@ -13,7 +13,7 @@ export const caatingaClient = createCaatingaClient({
13
13
  networkPassphrase: "Test SDF Network ; September 2015"
14
14
  },
15
15
  artifacts,
16
- wallet: freighterWalletAdapter,
16
+ wallet: stellarWalletAdapter,
17
17
  contracts: {
18
18
  counter: { binding: Counter }
19
19
  }
@@ -1,4 +1,4 @@
1
- import { useMemo, useState } from "react";
1
+ import { useCallback, useMemo, useState } from "react";
2
2
  import { caatingaClient } from "../caatinga.js";
3
3
  import { CaatingaError } from "@caatinga/core/browser";
4
4
 
@@ -11,28 +11,44 @@ function formatCaatingaError(error: unknown): string {
11
11
  }
12
12
 
13
13
  export function CounterCard() {
14
- const [count, setCount] = useState(0);
14
+ const [count, setCount] = useState<number | null>(null);
15
15
  const [loading, setLoading] = useState(false);
16
16
  const [error, setError] = useState<string | null>(null);
17
- const formattedCount = useMemo(() => new Intl.NumberFormat().format(count), [count]);
17
+ const formattedCount = useMemo(
18
+ () => (count === null ? "Unknown" : new Intl.NumberFormat().format(count)),
19
+ [count]
20
+ );
18
21
 
19
- async function increment() {
22
+ const refresh = useCallback(async () => {
20
23
  setLoading(true);
21
24
  setError(null);
22
25
 
23
26
  try {
24
- await caatingaClient.contract("counter").invoke("increment");
25
- setCount((value) => value + 1);
27
+ const nextCount = await caatingaClient.contract("counter").read<number>("get");
28
+ setCount(nextCount);
26
29
  } catch (caught) {
27
30
  setError(formatCaatingaError(caught));
28
31
  } finally {
29
32
  setLoading(false);
30
33
  }
31
- }
34
+ }, []);
32
35
 
33
- function reset() {
34
- setCount(0);
36
+ async function increment() {
37
+ setLoading(true);
35
38
  setError(null);
39
+
40
+ try {
41
+ const result = await caatingaClient.contract("counter").invoke<number>("increment");
42
+ if (typeof result.result === "number") {
43
+ setCount(result.result);
44
+ } else {
45
+ await refresh();
46
+ }
47
+ } catch (caught) {
48
+ setError(formatCaatingaError(caught));
49
+ } finally {
50
+ setLoading(false);
51
+ }
36
52
  }
37
53
 
38
54
  return (
@@ -51,8 +67,8 @@ export function CounterCard() {
51
67
  <button type="button" onClick={increment} disabled={loading}>
52
68
  {loading ? "Incrementing…" : "Increment"}
53
69
  </button>
54
- <button className="secondary-button" type="button" onClick={reset} disabled={loading}>
55
- Reset
70
+ <button className="secondary-button" type="button" onClick={refresh} disabled={loading}>
71
+ Refresh
56
72
  </button>
57
73
  </div>
58
74
 
@@ -1,14 +1,5 @@
1
- import { freighterWalletAdapter } from "@caatinga/client/freighter";
2
- import { useState } from "react";
3
- import { CaatingaError } from "@caatinga/core/browser";
4
-
5
- function formatWalletError(error: unknown): string {
6
- if (error instanceof CaatingaError) {
7
- return `[${error.code}] ${error.message}`;
8
- }
9
-
10
- return error instanceof Error ? error.message : String(error);
11
- }
1
+ import { WalletNetwork, WalletType } from "../wallet.js";
2
+ import { useStellarWallet } from "../hooks/useStellarWallet.js";
12
3
 
13
4
  function shortenAddress(address: string): string {
14
5
  if (address.length <= 12) {
@@ -19,41 +10,54 @@ function shortenAddress(address: string): string {
19
10
  }
20
11
 
21
12
  export function WalletButton() {
22
- const [publicKey, setPublicKey] = useState<string | null>(null);
23
- const [error, setError] = useState<string | null>(null);
24
- const [loading, setLoading] = useState(false);
25
-
26
- async function connect() {
27
- setLoading(true);
28
- setError(null);
29
-
30
- try {
31
- const key = await freighterWalletAdapter.getPublicKey();
32
- setPublicKey(key);
33
- } catch (caught) {
34
- setPublicKey(null);
35
- setError(formatWalletError(caught));
36
- } finally {
37
- setLoading(false);
38
- }
39
- }
40
-
41
- function disconnect() {
42
- setPublicKey(null);
43
- setError(null);
44
- }
13
+ const {
14
+ publicKey,
15
+ selectedWallet,
16
+ network,
17
+ loading,
18
+ error,
19
+ selectWallet,
20
+ selectNetwork,
21
+ connect,
22
+ disconnect
23
+ } = useStellarWallet();
45
24
 
46
25
  return (
47
26
  <div className="wallet-shell">
27
+ <div className="wallet-controls">
28
+ <label>
29
+ <span>Wallet</span>
30
+ <select
31
+ value={selectedWallet}
32
+ onChange={(event) => void selectWallet(event.target.value as WalletType)}
33
+ >
34
+ <option value={WalletType.XBULL}>xBull</option>
35
+ <option value={WalletType.FREIGHTER}>Freighter</option>
36
+ <option value={WalletType.ALBEDO}>Albedo</option>
37
+ <option value={WalletType.RABET}>Rabet</option>
38
+ <option value={WalletType.WALLET_CONNECT}>WalletConnect</option>
39
+ </select>
40
+ </label>
41
+ <label>
42
+ <span>Network</span>
43
+ <select
44
+ value={network}
45
+ onChange={(event) => void selectNetwork(event.target.value as WalletNetwork)}
46
+ >
47
+ <option value={WalletNetwork.TESTNET}>Testnet</option>
48
+ <option value={WalletNetwork.PUBLIC}>Public</option>
49
+ </select>
50
+ </label>
51
+ </div>
48
52
  <button
49
53
  className="wallet-button"
50
54
  type="button"
51
- onClick={publicKey ? disconnect : connect}
55
+ onClick={publicKey ? disconnect : () => void connect()}
52
56
  disabled={loading}
53
57
  aria-live="polite"
54
58
  >
55
59
  <span className={publicKey ? "status-dot status-dot--on" : "status-dot"} />
56
- {loading ? "Connecting" : publicKey ? shortenAddress(publicKey) : "Connect"}
60
+ {loading ? "Connecting..." : publicKey ? shortenAddress(publicKey) : "Connect"}
57
61
  </button>
58
62
  {error ? (
59
63
  <p className="wallet-error" role="alert">
@@ -3,6 +3,11 @@ type TransactionResult = {
3
3
  result?: unknown;
4
4
  };
5
5
 
6
+ type SignTransaction = (
7
+ xdr: string,
8
+ opts?: { networkPassphrase?: string; address?: string }
9
+ ) => Promise<{ signedTxXdr: string }> | { signedTxXdr: string };
10
+
6
11
  class ExampleTransaction {
7
12
  constructor(
8
13
  private readonly method: string,
@@ -17,9 +22,13 @@ class ExampleTransaction {
17
22
  return this;
18
23
  }
19
24
 
20
- async signAndSend(): Promise<TransactionResult> {
25
+ async signAndSend(input?: { signTransaction?: SignTransaction }): Promise<TransactionResult> {
26
+ const signed = input?.signTransaction
27
+ ? await input.signTransaction(this.toXDR())
28
+ : { signedTxXdr: this.toXDR() };
29
+
21
30
  return {
22
- txHash: "example-transaction-hash",
31
+ txHash: `example-transaction-hash:${signed.signedTxXdr}`,
23
32
  result: this.result
24
33
  };
25
34
  }
@@ -0,0 +1,70 @@
1
+ import { useCallback, useEffect, useState } from "react";
2
+ import { stellarWalletAdapter, WalletNetwork, WalletType } from "../wallet.js";
3
+
4
+ export function useStellarWallet() {
5
+ const [publicKey, setPublicKey] = useState<string | null>(null);
6
+ const [selectedWallet, setSelectedWallet] = useState<WalletType>(WalletType.XBULL);
7
+ const [network, setNetwork] = useState<WalletNetwork>(WalletNetwork.TESTNET);
8
+ const [loading, setLoading] = useState(false);
9
+ const [error, setError] = useState<string | null>(null);
10
+
11
+ const selectWallet = useCallback(async (wallet: WalletType) => {
12
+ setError(null);
13
+ await stellarWalletAdapter.setWallet(wallet);
14
+ setSelectedWallet(wallet);
15
+ setPublicKey(null);
16
+ }, []);
17
+
18
+ const selectNetwork = useCallback(async (nextNetwork: WalletNetwork) => {
19
+ setError(null);
20
+ await stellarWalletAdapter.setNetwork(nextNetwork);
21
+ setNetwork(nextNetwork);
22
+ setPublicKey(null);
23
+ }, []);
24
+
25
+ const connect = useCallback(async () => {
26
+ setLoading(true);
27
+ setError(null);
28
+
29
+ try {
30
+ if (selectedWallet === WalletType.WALLET_CONNECT) {
31
+ await stellarWalletAdapter.startWalletConnect();
32
+ await stellarWalletAdapter.connectWalletConnect();
33
+ }
34
+
35
+ const key = await stellarWalletAdapter.getPublicKey();
36
+ setPublicKey(key);
37
+ return key;
38
+ } catch (caught) {
39
+ const message = caught instanceof Error ? caught.message : String(caught);
40
+ setPublicKey(null);
41
+ setError(message);
42
+ throw caught;
43
+ } finally {
44
+ setLoading(false);
45
+ }
46
+ }, [selectedWallet]);
47
+
48
+ const disconnect = useCallback(() => {
49
+ setPublicKey(null);
50
+ setError(null);
51
+ }, []);
52
+
53
+ useEffect(() => {
54
+ stellarWalletAdapter.onWalletConnectSessionDeleted(() => {
55
+ setPublicKey(null);
56
+ });
57
+ }, []);
58
+
59
+ return {
60
+ publicKey,
61
+ selectedWallet,
62
+ network,
63
+ loading,
64
+ error,
65
+ selectWallet,
66
+ selectNetwork,
67
+ connect,
68
+ disconnect
69
+ };
70
+ }
@@ -79,6 +79,33 @@ button:hover {
79
79
  gap: 8px;
80
80
  }
81
81
 
82
+ .wallet-controls {
83
+ display: flex;
84
+ flex-wrap: wrap;
85
+ justify-content: flex-end;
86
+ gap: 8px;
87
+ }
88
+
89
+ .wallet-controls label {
90
+ display: grid;
91
+ gap: 4px;
92
+ color: #45515a;
93
+ font-size: 0.72rem;
94
+ font-weight: 800;
95
+ text-transform: uppercase;
96
+ }
97
+
98
+ .wallet-controls select {
99
+ min-height: 36px;
100
+ border: 1px solid #d9d5ca;
101
+ border-radius: 8px;
102
+ background: #ffffff;
103
+ color: #20232a;
104
+ font: inherit;
105
+ font-size: 0.86rem;
106
+ text-transform: none;
107
+ }
108
+
82
109
  .wallet-error,
83
110
  .counter-error {
84
111
  margin: 0;
@@ -169,6 +196,9 @@ button:hover {
169
196
  }
170
197
 
171
198
  .wallet-button,
199
+ .wallet-controls,
200
+ .wallet-controls label,
201
+ .wallet-controls select,
172
202
  .counter-actions button {
173
203
  width: 100%;
174
204
  justify-content: center;
@@ -0,0 +1,28 @@
1
+ import {
2
+ createStellarWalletsKitAdapter,
3
+ type StellarWalletsKitMetadata
4
+ } from "@caatinga/client/stellar-wallets-kit";
5
+ import { WalletNetwork, WalletType } from "stellar-wallets-kit";
6
+
7
+ export const stellarWalletAdapter = createStellarWalletsKitAdapter({
8
+ network: WalletNetwork.TESTNET,
9
+ selectedWallet: WalletType.XBULL,
10
+ walletConnectMetadata: getWalletConnectMetadata()
11
+ });
12
+
13
+ export { WalletNetwork, WalletType };
14
+
15
+ function getWalletConnectMetadata(): StellarWalletsKitMetadata | undefined {
16
+ const projectId = import.meta.env.VITE_WALLETCONNECT_PROJECT_ID as string | undefined;
17
+ if (!projectId) {
18
+ return undefined;
19
+ }
20
+
21
+ return {
22
+ projectId,
23
+ name: import.meta.env.VITE_APP_NAME ?? "__PROJECT_NAME__",
24
+ description: import.meta.env.VITE_APP_DESCRIPTION ?? "Caatinga counter dApp",
25
+ url: import.meta.env.VITE_APP_URL ?? window.location.origin,
26
+ icons: [import.meta.env.VITE_APP_ICON_URL ?? `${window.location.origin}/icon.png`]
27
+ };
28
+ }
@@ -14,7 +14,8 @@
14
14
  "resolveJsonModule": true,
15
15
  "isolatedModules": true,
16
16
  "noEmit": true,
17
- "jsx": "react-jsx"
17
+ "jsx": "react-jsx",
18
+ "types": ["vite/client"]
18
19
  },
19
20
  "include": ["src"],
20
21
  "references": []
@@ -1,6 +1,16 @@
1
1
  import { defineConfig } from "vite";
2
2
  import react from "@vitejs/plugin-react";
3
+ import { createRequire } from "node:module";
4
+ import { dirname, join } from "node:path";
5
+
6
+ const require = createRequire(import.meta.url);
7
+ const xbullWalletConnectRoot = dirname(require.resolve("@creit.tech/xbull-wallet-connect/package.json"));
3
8
 
4
9
  export default defineConfig({
5
- plugins: [react()]
10
+ plugins: [react()],
11
+ resolve: {
12
+ alias: {
13
+ "@creit-tech/xbull-wallet-connect": join(xbullWalletConnectRoot, "src/index.ts")
14
+ }
15
+ }
6
16
  });