movementkit-cli 1.0.1 → 1.0.3

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 (36) hide show
  1. package/dist/index.js +11 -7
  2. package/kits/engineer/.claude/agents/devops.md +176 -0
  3. package/kits/engineer/.claude/agents/frontend.md +207 -0
  4. package/kits/engineer/.claude/agents/smart-contract.md +150 -0
  5. package/kits/engineer/.claude/agents/tester.md +174 -0
  6. package/kits/engineer/.claude/commands/cook/contracts.md +174 -0
  7. package/kits/engineer/.claude/commands/cook/frontend.md +325 -0
  8. package/kits/engineer/.claude/commands/cook.md +118 -0
  9. package/kits/engineer/.claude/commands/deploy-full.md +158 -0
  10. package/kits/engineer/.claude/commands/deploy-smart-contract.md +177 -0
  11. package/kits/engineer/.claude/commands/docs/generate.md +121 -0
  12. package/kits/engineer/.claude/commands/docs/init.md +132 -0
  13. package/kits/engineer/.claude/commands/plan.md +103 -0
  14. package/kits/engineer/.claude/commands/review.md +98 -0
  15. package/kits/engineer/.claude/commands/test.md +92 -0
  16. package/kits/engineer/.claude/commands/watzup.md +100 -0
  17. package/kits/engineer/.claude/workflows/development-rules.md +110 -0
  18. package/kits/engineer/.claude/workflows/primary-workflow.md +95 -0
  19. package/kits/engineer/CLAUDE.md +105 -0
  20. package/kits/engineer/contracts/Move.toml +13 -0
  21. package/kits/engineer/contracts/sources/counter.move +122 -0
  22. package/kits/engineer/contracts/tests/counter_tests.move +96 -0
  23. package/kits/engineer/docs/MOVE_LANGUAGE_REFERENCE.md +560 -0
  24. package/kits/engineer/frontend/.env.example +9 -0
  25. package/kits/engineer/frontend/index.html +14 -0
  26. package/kits/engineer/frontend/package.json +29 -0
  27. package/kits/engineer/frontend/src/App.tsx +41 -0
  28. package/kits/engineer/frontend/src/components/WalletConnect.tsx +54 -0
  29. package/kits/engineer/frontend/src/contexts/WalletContext.tsx +42 -0
  30. package/kits/engineer/frontend/src/hooks/useContract.ts +95 -0
  31. package/kits/engineer/frontend/src/index.css +76 -0
  32. package/kits/engineer/frontend/src/main.tsx +11 -0
  33. package/kits/engineer/frontend/tsconfig.json +22 -0
  34. package/kits/engineer/frontend/tsconfig.node.json +11 -0
  35. package/kits/engineer/frontend/vite.config.ts +17 -0
  36. package/package.json +3 -2
@@ -0,0 +1,560 @@
1
+ # Move Language Reference for Movement Blockchain
2
+
3
+ This document provides a comprehensive reference for writing Move smart contracts on the Movement blockchain.
4
+
5
+ ## Movement Network Configuration
6
+
7
+ ### Network Endpoints
8
+ ```
9
+ Mainnet: https://full.mainnet.movementinfra.xyz/v1 (Chain ID: 126)
10
+ Testnet: https://full.testnet.movementinfra.xyz/v1 (Chain ID: 250)
11
+ Faucet: https://faucet.movementnetwork.xyz/
12
+ Explorer: https://explorer.movementnetwork.xyz/
13
+ ```
14
+
15
+ ### CLI Commands
16
+ ```bash
17
+ # Initialize a new Move project
18
+ movement move init --name project_name
19
+
20
+ # Compile contracts
21
+ movement move compile
22
+
23
+ # Run tests
24
+ movement move test
25
+
26
+ # Publish to testnet
27
+ movement move publish --url https://full.testnet.movementinfra.xyz/v1 --named-addresses module_addr=default
28
+
29
+ # Publish to mainnet
30
+ movement move publish --url https://full.mainnet.movementinfra.xyz/v1 --named-addresses module_addr=default
31
+ ```
32
+
33
+ ## Move.toml Configuration
34
+
35
+ ```toml
36
+ [package]
37
+ name = "project_name"
38
+ version = "1.0.0"
39
+ authors = []
40
+
41
+ [addresses]
42
+ module_addr = "_"
43
+
44
+ [dependencies]
45
+ AptosFramework = { git = "https://github.com/aptos-labs/aptos-core.git", subdir = "aptos-move/framework/aptos-framework", rev = "mainnet" }
46
+ AptosStdlib = { git = "https://github.com/aptos-labs/aptos-core.git", subdir = "aptos-move/framework/aptos-stdlib", rev = "mainnet" }
47
+ MoveStdlib = { git = "https://github.com/aptos-labs/aptos-core.git", subdir = "aptos-move/framework/move-stdlib", rev = "mainnet" }
48
+ ```
49
+
50
+ ## Module Structure
51
+
52
+ ```move
53
+ module module_addr::module_name {
54
+ // Imports
55
+ use std::signer;
56
+ use std::string::String;
57
+ use std::vector;
58
+ use std::option::{Self, Option};
59
+ use aptos_framework::event;
60
+ use aptos_framework::account;
61
+ use aptos_framework::timestamp;
62
+ use aptos_framework::coin;
63
+ use aptos_framework::aptos_coin::AptosCoin;
64
+
65
+ // Error codes (constants)
66
+ const E_NOT_AUTHORIZED: u64 = 1;
67
+ const E_ALREADY_EXISTS: u64 = 2;
68
+ const E_NOT_FOUND: u64 = 3;
69
+ const E_INSUFFICIENT_BALANCE: u64 = 4;
70
+
71
+ // Resources (structs with 'key' ability)
72
+ struct ResourceName has key, store {
73
+ field1: u64,
74
+ field2: String,
75
+ field3: vector<u8>,
76
+ }
77
+
78
+ // Events
79
+ #[event]
80
+ struct EventName has drop, store {
81
+ actor: address,
82
+ value: u64,
83
+ timestamp: u64,
84
+ }
85
+
86
+ // Entry functions (callable from transactions)
87
+ public entry fun function_name(
88
+ account: &signer,
89
+ param1: u64,
90
+ ) acquires ResourceName {
91
+ // Implementation
92
+ }
93
+
94
+ // View functions (read-only, callable without transaction)
95
+ #[view]
96
+ public fun get_value(addr: address): u64 acquires ResourceName {
97
+ borrow_global<ResourceName>(addr).field1
98
+ }
99
+
100
+ // Internal functions
101
+ fun internal_helper(): u64 {
102
+ 42
103
+ }
104
+ }
105
+ ```
106
+
107
+ ## Primitive Types
108
+
109
+ | Type | Description | Example |
110
+ |------|-------------|---------|
111
+ | `u8` | 8-bit unsigned integer | `let x: u8 = 255;` |
112
+ | `u16` | 16-bit unsigned integer | `let x: u16 = 65535;` |
113
+ | `u32` | 32-bit unsigned integer | `let x: u32 = 100;` |
114
+ | `u64` | 64-bit unsigned integer | `let x: u64 = 1000000;` |
115
+ | `u128` | 128-bit unsigned integer | `let x: u128 = 1000000;` |
116
+ | `u256` | 256-bit unsigned integer | `let x: u256 = 1000000;` |
117
+ | `bool` | Boolean | `let b: bool = true;` |
118
+ | `address` | 32-byte address | `let a: address = @0x1;` |
119
+ | `vector<T>` | Dynamic array | `let v: vector<u8> = vector[];` |
120
+ | `String` | UTF-8 string | `use std::string::String;` |
121
+
122
+ ## Abilities
123
+
124
+ | Ability | Description |
125
+ |---------|-------------|
126
+ | `copy` | Value can be copied |
127
+ | `drop` | Value can be dropped (destroyed) |
128
+ | `store` | Value can be stored in global storage |
129
+ | `key` | Value can be used as a key in global storage |
130
+
131
+ ```move
132
+ // Resource that can be stored globally
133
+ struct MyResource has key, store {
134
+ value: u64,
135
+ }
136
+
137
+ // Struct that can be copied and dropped
138
+ struct MyData has copy, drop, store {
139
+ value: u64,
140
+ }
141
+
142
+ // Event struct (must have drop and store)
143
+ #[event]
144
+ struct MyEvent has drop, store {
145
+ value: u64,
146
+ }
147
+ ```
148
+
149
+ ## Global Storage Operations
150
+
151
+ ```move
152
+ // Store a resource at an address
153
+ move_to<ResourceName>(account, ResourceName { field1: 0, field2: string::utf8(b"hello") });
154
+
155
+ // Check if resource exists
156
+ exists<ResourceName>(addr)
157
+
158
+ // Borrow immutable reference
159
+ let resource_ref = borrow_global<ResourceName>(addr);
160
+
161
+ // Borrow mutable reference
162
+ let resource_mut = borrow_global_mut<ResourceName>(addr);
163
+
164
+ // Remove resource from storage
165
+ let resource = move_from<ResourceName>(addr);
166
+ ```
167
+
168
+ ## Signer Operations
169
+
170
+ ```move
171
+ use std::signer;
172
+
173
+ public entry fun my_function(account: &signer) {
174
+ // Get address from signer
175
+ let addr = signer::address_of(account);
176
+
177
+ // Validate caller
178
+ assert!(addr == @admin_address, E_NOT_AUTHORIZED);
179
+ }
180
+ ```
181
+
182
+ ## Events
183
+
184
+ ```move
185
+ use aptos_framework::event;
186
+
187
+ // Define event struct
188
+ #[event]
189
+ struct TransferEvent has drop, store {
190
+ from: address,
191
+ to: address,
192
+ amount: u64,
193
+ }
194
+
195
+ // Emit event
196
+ event::emit(TransferEvent {
197
+ from: sender_addr,
198
+ to: recipient_addr,
199
+ amount: 100,
200
+ });
201
+ ```
202
+
203
+ ## Vectors
204
+
205
+ ```move
206
+ use std::vector;
207
+
208
+ // Create empty vector
209
+ let v: vector<u64> = vector[];
210
+
211
+ // Create with initial values
212
+ let v = vector[1, 2, 3];
213
+
214
+ // Push element
215
+ vector::push_back(&mut v, 4);
216
+
217
+ // Pop element
218
+ let last = vector::pop_back(&mut v);
219
+
220
+ // Get length
221
+ let len = vector::length(&v);
222
+
223
+ // Check if empty
224
+ let is_empty = vector::is_empty(&v);
225
+
226
+ // Get element by index (immutable)
227
+ let elem = vector::borrow(&v, 0);
228
+
229
+ // Get element by index (mutable)
230
+ let elem_mut = vector::borrow_mut(&mut v, 0);
231
+
232
+ // Check if contains
233
+ let contains = vector::contains(&v, &42);
234
+ ```
235
+
236
+ ## Strings
237
+
238
+ ```move
239
+ use std::string::{Self, String};
240
+
241
+ // Create from bytes
242
+ let s: String = string::utf8(b"Hello, World!");
243
+
244
+ // Get length
245
+ let len = string::length(&s);
246
+
247
+ // Check if empty
248
+ let is_empty = string::is_empty(&s);
249
+
250
+ // Append
251
+ string::append(&mut s, string::utf8(b" More text"));
252
+
253
+ // Convert to bytes
254
+ let bytes: vector<u8> = *string::bytes(&s);
255
+ ```
256
+
257
+ ## Options
258
+
259
+ ```move
260
+ use std::option::{Self, Option};
261
+
262
+ // Create Some
263
+ let opt: Option<u64> = option::some(42);
264
+
265
+ // Create None
266
+ let none: Option<u64> = option::none();
267
+
268
+ // Check if some
269
+ let is_some = option::is_some(&opt);
270
+
271
+ // Check if none
272
+ let is_none = option::is_none(&none);
273
+
274
+ // Extract value (aborts if none)
275
+ let value = option::extract(&mut opt);
276
+
277
+ // Get with default
278
+ let value = option::get_with_default(&opt, 0);
279
+
280
+ // Borrow value
281
+ let value_ref = option::borrow(&opt);
282
+ ```
283
+
284
+ ## Coin Operations
285
+
286
+ ```move
287
+ use aptos_framework::coin;
288
+ use aptos_framework::aptos_coin::AptosCoin;
289
+
290
+ // Register coin store for account
291
+ coin::register<AptosCoin>(account);
292
+
293
+ // Get balance
294
+ let balance = coin::balance<AptosCoin>(addr);
295
+
296
+ // Transfer coins
297
+ coin::transfer<AptosCoin>(from, to_addr, amount);
298
+
299
+ // Withdraw coins
300
+ let coins = coin::withdraw<AptosCoin>(account, amount);
301
+
302
+ // Deposit coins
303
+ coin::deposit(to_addr, coins);
304
+ ```
305
+
306
+ ## Timestamp
307
+
308
+ ```move
309
+ use aptos_framework::timestamp;
310
+
311
+ // Get current timestamp in seconds
312
+ let now_seconds = timestamp::now_seconds();
313
+
314
+ // Get current timestamp in microseconds
315
+ let now_microseconds = timestamp::now_microseconds();
316
+ ```
317
+
318
+ ## Testing
319
+
320
+ ### Basic Test
321
+ ```move
322
+ #[test_only]
323
+ module module_addr::module_name_tests {
324
+ use std::signer;
325
+ use module_addr::module_name;
326
+
327
+ #[test(account = @0x1)]
328
+ fun test_basic_function(account: &signer) {
329
+ // Setup
330
+ let addr = signer::address_of(account);
331
+
332
+ // Action
333
+ module_name::initialize(account);
334
+
335
+ // Assert
336
+ assert!(module_name::get_value(addr) == 0, 0);
337
+ }
338
+ }
339
+ ```
340
+
341
+ ### Test with Expected Failure
342
+ ```move
343
+ #[test]
344
+ #[expected_failure(abort_code = module_name::E_NOT_AUTHORIZED)]
345
+ fun test_unauthorized_access() {
346
+ // This should fail with E_NOT_AUTHORIZED
347
+ module_name::admin_only_function();
348
+ }
349
+ ```
350
+
351
+ ### Test with Multiple Signers
352
+ ```move
353
+ #[test(admin = @0x1, user = @0x2)]
354
+ fun test_with_multiple_accounts(admin: &signer, user: &signer) {
355
+ // Setup admin
356
+ module_name::initialize(admin);
357
+
358
+ // User interacts
359
+ module_name::user_action(user);
360
+ }
361
+ ```
362
+
363
+ ### Test with Framework
364
+ ```move
365
+ #[test(aptos_framework = @aptos_framework, account = @0x1)]
366
+ fun test_with_framework(aptos_framework: &signer, account: &signer) {
367
+ // Setup timestamp for testing
368
+ timestamp::set_time_has_started_for_testing(aptos_framework);
369
+
370
+ // Now timestamp::now_seconds() works in tests
371
+ module_name::time_dependent_function(account);
372
+ }
373
+ ```
374
+
375
+ ## Common Patterns
376
+
377
+ ### Initialization Pattern
378
+ ```move
379
+ struct Config has key {
380
+ admin: address,
381
+ is_initialized: bool,
382
+ }
383
+
384
+ public entry fun initialize(admin: &signer) {
385
+ let admin_addr = signer::address_of(admin);
386
+ assert!(!exists<Config>(admin_addr), E_ALREADY_EXISTS);
387
+
388
+ move_to(admin, Config {
389
+ admin: admin_addr,
390
+ is_initialized: true,
391
+ });
392
+ }
393
+ ```
394
+
395
+ ### Admin-Only Pattern
396
+ ```move
397
+ public entry fun admin_function(admin: &signer) acquires Config {
398
+ let admin_addr = signer::address_of(admin);
399
+ let config = borrow_global<Config>(@module_addr);
400
+ assert!(config.admin == admin_addr, E_NOT_AUTHORIZED);
401
+
402
+ // Admin-only logic
403
+ }
404
+ ```
405
+
406
+ ### Counter Pattern
407
+ ```move
408
+ struct Counter has key {
409
+ value: u64,
410
+ }
411
+
412
+ public entry fun increment(account: &signer) acquires Counter {
413
+ let addr = signer::address_of(account);
414
+
415
+ if (!exists<Counter>(addr)) {
416
+ move_to(account, Counter { value: 0 });
417
+ };
418
+
419
+ let counter = borrow_global_mut<Counter>(addr);
420
+ counter.value = counter.value + 1;
421
+
422
+ event::emit(CounterIncremented {
423
+ account: addr,
424
+ new_value: counter.value
425
+ });
426
+ }
427
+
428
+ #[view]
429
+ public fun get_count(addr: address): u64 acquires Counter {
430
+ if (!exists<Counter>(addr)) {
431
+ return 0
432
+ };
433
+ borrow_global<Counter>(addr).value
434
+ }
435
+ ```
436
+
437
+ ### Token/NFT Collection Pattern
438
+ ```move
439
+ struct Collection has key {
440
+ items: vector<Item>,
441
+ next_id: u64,
442
+ }
443
+
444
+ struct Item has store, drop {
445
+ id: u64,
446
+ name: String,
447
+ owner: address,
448
+ }
449
+
450
+ public entry fun mint(account: &signer, name: String) acquires Collection {
451
+ let addr = signer::address_of(account);
452
+ let collection = borrow_global_mut<Collection>(@module_addr);
453
+
454
+ let item = Item {
455
+ id: collection.next_id,
456
+ name,
457
+ owner: addr,
458
+ };
459
+
460
+ vector::push_back(&mut collection.items, item);
461
+ collection.next_id = collection.next_id + 1;
462
+
463
+ event::emit(ItemMinted { id: collection.next_id - 1, owner: addr });
464
+ }
465
+ ```
466
+
467
+ ## Security Best Practices
468
+
469
+ 1. **Always validate signers** - Check that the caller has permission
470
+ 2. **Use assert! with error codes** - Provide clear error messages
471
+ 3. **Emit events for state changes** - Enable off-chain tracking
472
+ 4. **Check for resource existence** - Use `exists<T>(addr)` before accessing
473
+ 5. **Avoid unbounded loops** - Can cause out-of-gas errors
474
+ 6. **Use acquires annotation** - Declare all resources accessed
475
+ 7. **Initialize before use** - Ensure resources exist before borrowing
476
+
477
+ ## Complete Example: Simple Token
478
+
479
+ ```move
480
+ module module_addr::simple_token {
481
+ use std::signer;
482
+ use std::string::String;
483
+ use aptos_framework::event;
484
+
485
+ // Errors
486
+ const E_NOT_INITIALIZED: u64 = 1;
487
+ const E_INSUFFICIENT_BALANCE: u64 = 2;
488
+
489
+ // Resources
490
+ struct TokenInfo has key {
491
+ name: String,
492
+ symbol: String,
493
+ total_supply: u64,
494
+ }
495
+
496
+ struct Balance has key {
497
+ value: u64,
498
+ }
499
+
500
+ // Events
501
+ #[event]
502
+ struct Transfer has drop, store {
503
+ from: address,
504
+ to: address,
505
+ amount: u64,
506
+ }
507
+
508
+ // Initialize token
509
+ public entry fun initialize(
510
+ admin: &signer,
511
+ name: String,
512
+ symbol: String,
513
+ initial_supply: u64,
514
+ ) {
515
+ let admin_addr = signer::address_of(admin);
516
+
517
+ move_to(admin, TokenInfo {
518
+ name,
519
+ symbol,
520
+ total_supply: initial_supply,
521
+ });
522
+
523
+ move_to(admin, Balance { value: initial_supply });
524
+ }
525
+
526
+ // Transfer tokens
527
+ public entry fun transfer(
528
+ from: &signer,
529
+ to: address,
530
+ amount: u64,
531
+ ) acquires Balance {
532
+ let from_addr = signer::address_of(from);
533
+
534
+ // Deduct from sender
535
+ let from_balance = borrow_global_mut<Balance>(from_addr);
536
+ assert!(from_balance.value >= amount, E_INSUFFICIENT_BALANCE);
537
+ from_balance.value = from_balance.value - amount;
538
+
539
+ // Add to recipient
540
+ if (!exists<Balance>(to)) {
541
+ // Create balance for new recipient (requires signer, simplified here)
542
+ // In practice, recipient would need to register first
543
+ };
544
+ let to_balance = borrow_global_mut<Balance>(to);
545
+ to_balance.value = to_balance.value + amount;
546
+
547
+ event::emit(Transfer { from: from_addr, to, amount });
548
+ }
549
+
550
+ // View balance
551
+ #[view]
552
+ public fun balance_of(addr: address): u64 acquires Balance {
553
+ if (!exists<Balance>(addr)) {
554
+ return 0
555
+ };
556
+ borrow_global<Balance>(addr).value
557
+ }
558
+ }
559
+ ```
560
+
@@ -0,0 +1,9 @@
1
+ # Movement Network Configuration
2
+ VITE_NETWORK=testnet
3
+ VITE_MODULE_ADDRESS=0x1
4
+ VITE_MODULE_NAME=module_name
5
+ VITE_API_URL=http://localhost:3001
6
+
7
+ # Optional: Aptos API Key for wallet adapter
8
+ # VITE_APTOS_API_KEY=your_api_key_here
9
+
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Movement dApp</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
14
+
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "movement-dapp-frontend",
3
+ "version": "1.0.0",
4
+ "description": "Frontend for Movement blockchain dApp",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc && vite build",
9
+ "preview": "vite preview",
10
+ "test": "vitest",
11
+ "test:coverage": "vitest --coverage"
12
+ },
13
+ "dependencies": {
14
+ "@aptos-labs/ts-sdk": "^1.33.1",
15
+ "@aptos-labs/wallet-adapter-react": "^3.0.0",
16
+ "@radix-ui/react-dialog": "^1.0.5",
17
+ "react": "^18.2.0",
18
+ "react-dom": "^18.2.0"
19
+ },
20
+ "devDependencies": {
21
+ "@types/react": "^18.2.0",
22
+ "@types/react-dom": "^18.2.0",
23
+ "@vitejs/plugin-react": "^4.2.0",
24
+ "typescript": "^5.3.0",
25
+ "vite": "^5.0.0",
26
+ "vitest": "^1.0.0"
27
+ }
28
+ }
29
+
@@ -0,0 +1,41 @@
1
+ import { WalletProvider } from "./contexts/WalletContext";
2
+ import { WalletConnect } from "./components/WalletConnect";
3
+
4
+ function App() {
5
+ return (
6
+ <WalletProvider>
7
+ <div className="app">
8
+ <header>
9
+ <h1>🚀 Movement dApp</h1>
10
+ <p>Built with Movement Kit</p>
11
+ </header>
12
+
13
+ <main>
14
+ <div className="card">
15
+ <WalletConnect />
16
+ </div>
17
+
18
+ <div className="card">
19
+ <h2>Getting Started</h2>
20
+ <p>Connect your wallet to interact with the Movement blockchain.</p>
21
+ </div>
22
+ </main>
23
+
24
+ <footer>
25
+ <p>
26
+ <a href="https://explorer.movementnetwork.xyz/" target="_blank" rel="noopener noreferrer">
27
+ Movement Explorer
28
+ </a>
29
+ {" | "}
30
+ <a href="https://faucet.movementnetwork.xyz/" target="_blank" rel="noopener noreferrer">
31
+ Faucet
32
+ </a>
33
+ </p>
34
+ </footer>
35
+ </div>
36
+ </WalletProvider>
37
+ );
38
+ }
39
+
40
+ export default App;
41
+
@@ -0,0 +1,54 @@
1
+ import { useWallet } from "@aptos-labs/wallet-adapter-react";
2
+
3
+ export function WalletConnect() {
4
+ const {
5
+ connect,
6
+ disconnect,
7
+ account,
8
+ connected,
9
+ wallets,
10
+ isLoading,
11
+ } = useWallet();
12
+
13
+ // Format address for display
14
+ const formatAddress = (address: string) => {
15
+ return `${address.slice(0, 6)}...${address.slice(-4)}`;
16
+ };
17
+
18
+ if (isLoading) {
19
+ return <div>Loading wallet...</div>;
20
+ }
21
+
22
+ if (connected && account) {
23
+ return (
24
+ <div className="wallet-connected">
25
+ <p>
26
+ <strong>Connected:</strong> {formatAddress(account.address.toString())}
27
+ </p>
28
+ <button onClick={disconnect}>
29
+ Disconnect Wallet
30
+ </button>
31
+ </div>
32
+ );
33
+ }
34
+
35
+ return (
36
+ <div className="wallet-connect">
37
+ <h3>Connect Wallet</h3>
38
+ <div className="wallet-options">
39
+ {wallets?.filter(wallet => wallet.readyState === "Installed").map((wallet) => (
40
+ <button
41
+ key={wallet.name}
42
+ onClick={() => connect(wallet.name)}
43
+ >
44
+ Connect {wallet.name}
45
+ </button>
46
+ ))}
47
+ {wallets?.filter(wallet => wallet.readyState === "Installed").length === 0 && (
48
+ <p>No wallets detected. Please install a wallet extension.</p>
49
+ )}
50
+ </div>
51
+ </div>
52
+ );
53
+ }
54
+