@caatinga/cli 0.2.4 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/README.md +6 -7
  2. package/dist/index.js +198 -150
  3. package/package.json +3 -2
  4. package/templates/marketplace-with-token/caatinga.template.json +1 -1
  5. package/templates/marketplace-with-token/contracts/marketplace/test_snapshots/test/stores_token_contract_id_in_constructor.1.json +86 -0
  6. package/templates/marketplace-with-token/package.json +3 -3
  7. package/templates/react-vite-counter/README.md +18 -2
  8. package/templates/react-vite-counter/caatinga.template.json +1 -1
  9. package/templates/react-vite-counter/contracts/counter/src/lib.rs +70 -6
  10. package/templates/react-vite-counter/contracts/counter/test_snapshots/test/get_returns_zero_before_increment.1.json +76 -0
  11. package/templates/react-vite-counter/contracts/counter/test_snapshots/test/increment_returns_overflow_error.1.json +91 -0
  12. package/templates/react-vite-counter/contracts/counter/test_snapshots/test/increments_counter.1.json +91 -0
  13. package/templates/react-vite-counter/contracts/counter/test_snapshots/test/repeated_increments_preserve_state.1.json +92 -0
  14. package/templates/react-vite-counter/package.json +4 -5
  15. package/templates/react-vite-counter/pnpm-workspace.yaml +12 -0
  16. package/templates/react-vite-counter/src/App.tsx +26 -2
  17. package/templates/react-vite-counter/src/components/CounterCard.tsx +31 -6
  18. package/templates/react-vite-counter/src/components/LoadingModal.tsx +14 -0
  19. package/templates/react-vite-counter/src/components/WalletButton.tsx +3 -39
  20. package/templates/react-vite-counter/src/context/WalletContext.tsx +64 -0
  21. package/templates/react-vite-counter/src/contracts/generated/counter.ts +1 -1
  22. package/templates/react-vite-counter/src/stubs/hot-wallet.ts +14 -0
  23. package/templates/react-vite-counter/src/styles.css +51 -0
  24. package/templates/react-vite-counter/src/wallet.ts +3 -3
  25. package/templates/react-vite-counter/vite.config.ts +5 -6
  26. package/templates/react-vite-counter/src/hooks/useStellarWallet.ts +0 -70
@@ -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.1",
16
+ "@caatinga/core": "^2.0.1",
17
17
  "@vitejs/plugin-react": "^4.3.4",
18
18
  "react": "^18.3.1",
19
19
  "react-dom": "^18.3.1",
20
20
  "vite": "^6.0.6"
21
21
  },
22
22
  "devDependencies": {
23
- "@caatinga/cli": "^0.2.4",
23
+ "@caatinga/cli": "^2.0.1",
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.1",
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,17 +12,16 @@
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.1",
16
+ "@caatinga/core": "^2.0.1",
17
+ "@creit.tech/stellar-wallets-kit": "^1.9.5",
18
18
  "@vitejs/plugin-react": "^4.3.4",
19
19
  "react": "^18.3.1",
20
20
  "react-dom": "^18.3.1",
21
- "stellar-wallets-kit": "github:Creit-Tech/Stellar-Wallets-Kit#v0.0.7",
22
21
  "vite": "^6.0.6"
23
22
  },
24
23
  "devDependencies": {
25
- "@caatinga/cli": "^0.2.4",
24
+ "@caatinga/cli": "^2.0.1",
26
25
  "@types/react": "^18.3.18",
27
26
  "@types/react-dom": "^18.3.5",
28
27
  "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,7 +1,10 @@
1
1
  import { CounterCard } from "./components/CounterCard";
2
2
  import { WalletButton } from "./components/WalletButton";
3
+ import { WalletProvider, useWallet } from "./context/WalletContext";
4
+
5
+ function AppBody() {
6
+ const { publicKey } = useWallet();
3
7
 
4
- export default function App() {
5
8
  return (
6
9
  <main className="app-shell">
7
10
  <header className="topbar">
@@ -12,7 +15,28 @@ export default function App() {
12
15
  <WalletButton />
13
16
  </header>
14
17
 
15
- <CounterCard />
18
+ {publicKey ? (
19
+ <CounterCard />
20
+ ) : (
21
+ <section className="counter-panel" aria-labelledby="connect-title">
22
+ <div className="counter-panel__header">
23
+ <div>
24
+ <p className="eyebrow">Get started</p>
25
+ <h2 id="connect-title">Connect your wallet</h2>
26
+ </div>
27
+ <span className="network-pill">testnet</span>
28
+ </div>
29
+ <p>Connect a Stellar wallet to read and update the counter contract.</p>
30
+ </section>
31
+ )}
16
32
  </main>
17
33
  );
18
34
  }
35
+
36
+ export default function App() {
37
+ return (
38
+ <WalletProvider>
39
+ <AppBody />
40
+ </WalletProvider>
41
+ );
42
+ }