spaps-mcp 0.1.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.
@@ -0,0 +1,368 @@
1
+ # SPAPS Integration Step 5/12: Wallet Authentication
2
+
3
+ ## Prerequisites Check
4
+
5
+ Before starting, confirm you have completed:
6
+ - ✅ Step 1-4: Project setup through email authentication
7
+ - ✅ TokenManager working with email auth
8
+ - ✅ Error handling implemented
9
+
10
+ ## Required TodoWrite List
11
+
12
+ Create a TodoWrite with EXACTLY these items:
13
+
14
+ ```javascript
15
+ TodoWrite({
16
+ todos: [
17
+ { content: "Add wallet connection UI with shadcn/ui", status: "pending", activeForm: "Adding wallet connection UI" },
18
+ { content: "Implement authenticateWallet method (NOT signInWithWallet)", status: "pending", activeForm: "Implementing authenticateWallet" },
19
+ { content: "Create complete signature callback function", status: "pending", activeForm: "Creating signature callback" },
20
+ { content: "Use WalletUtils.detectChainType for chain detection", status: "pending", activeForm: "Using WalletUtils for chain detection" },
21
+ { content: "Test with address 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb8", status: "pending", activeForm: "Testing wallet authentication" },
22
+ { content: "Store tokens and setup auto-refresh", status: "pending", activeForm: "Setting up token storage" },
23
+ { content: "Handle wallet-specific errors", status: "pending", activeForm: "Handling wallet errors" },
24
+ { content: "Request step 6 of SPAPS integration wizard", status: "pending", activeForm: "Requesting next step" }
25
+ ]
26
+ })
27
+ ```
28
+
29
+ ## Critical Method Name Warning
30
+
31
+ ⚠️ **EXTREMELY IMPORTANT - READ THIS CAREFULLY** ⚠️
32
+
33
+ The correct method is `authenticateWallet` NOT `signInWithWallet`:
34
+
35
+ ```typescript
36
+ // ✅ CORRECT - This is the ONLY method that exists
37
+ const auth = await sdk.auth.authenticateWallet(
38
+ walletAddress,
39
+ signatureCallback,
40
+ chainType
41
+ )
42
+
43
+ // ❌ WRONG - These methods DO NOT EXIST
44
+ sdk.auth.signInWithWallet(...) // NO! This doesn't exist!
45
+ sdk.auth.walletLogin(...) // NO! This doesn't exist!
46
+ sdk.auth.walletSignIn(...) // NO! This doesn't exist!
47
+ sdk.auth.connectWallet(...) // NO! This doesn't exist!
48
+ ```
49
+
50
+ ## Implementation Guide
51
+
52
+ ### 1. Import Requirements
53
+ ```typescript
54
+ import { SweetPotatoSDK, TokenManager, WalletUtils, SweetPotatoAPIError } from 'spaps-sdk'
55
+ import { sdk } from '@/lib/spaps'
56
+ import { ethers } from 'ethers' // For Ethereum wallet interaction
57
+
58
+ // UI Components
59
+ import { Button } from "@/components/ui/button"
60
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
61
+ import { toast } from "sonner"
62
+ ```
63
+
64
+ ### 2. Wallet Authentication Implementation
65
+
66
+ The `authenticateWallet` method has this EXACT signature:
67
+
68
+ ```typescript
69
+ authenticateWallet(
70
+ walletAddress: string,
71
+ signatureFunction: (message: string) => Promise<string> | string,
72
+ chainType?: 'ethereum' | 'solana' | 'bitcoin' | 'base',
73
+ username?: string
74
+ ): Promise<AuthResponse>
75
+ ```
76
+
77
+ **Bitcoin scope note:** the server verifies BIP-137 signed messages
78
+ (Bitcoin Core / Electrum / Trezor style) for mainnet P2PKH, P2WPKH (`bc1q`),
79
+ and P2SH-P2WPKH addresses. Taproot (`bc1p`) addresses require BIP-322 and are
80
+ rejected with a clear error — do not offer Taproot wallets for sign-in.
81
+ Testnet addresses are rejected.
82
+
83
+ ### 3. Complete Implementation Example
84
+
85
+ ```typescript
86
+ 'use client'
87
+
88
+ import { useState } from 'react'
89
+ import { Button } from "@/components/ui/button"
90
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
91
+ import { toast } from "sonner"
92
+ import { sdk } from '@/lib/spaps'
93
+ import { SweetPotatoAPIError, TokenManager, WalletUtils } from 'spaps-sdk'
94
+ import { ethers } from 'ethers'
95
+
96
+ export function WalletAuthForm() {
97
+ const [isLoading, setIsLoading] = useState(false)
98
+ const [walletAddress, setWalletAddress] = useState('')
99
+
100
+ const handleWalletConnect = async () => {
101
+ setIsLoading(true)
102
+
103
+ try {
104
+ // Step 1: Check for MetaMask
105
+ if (!window.ethereum) {
106
+ throw new Error('Please install MetaMask to use wallet authentication')
107
+ }
108
+
109
+ // Step 2: Request account access
110
+ const accounts = await window.ethereum.request({
111
+ method: 'eth_requestAccounts'
112
+ }) as string[]
113
+
114
+ if (!accounts || accounts.length === 0) {
115
+ throw new Error('No accounts found in wallet')
116
+ }
117
+
118
+ const address = accounts[0]
119
+ setWalletAddress(address)
120
+
121
+ // Step 3: Detect chain type (optional, will auto-detect if not provided)
122
+ const chainType = WalletUtils.detectChainType(address)
123
+ console.log('Detected chain type:', chainType)
124
+
125
+ // Step 4: Create signature callback
126
+ // ⚠️ CRITICAL: This callback receives the message to sign
127
+ const signatureCallback = async (message: string): Promise<string> => {
128
+ console.log('Message to sign:', message)
129
+
130
+ // Create ethers provider and signer
131
+ const provider = new ethers.BrowserProvider(window.ethereum)
132
+ const signer = await provider.getSigner()
133
+
134
+ // Sign the message
135
+ const signature = await signer.signMessage(message)
136
+ console.log('Signature:', signature)
137
+
138
+ return signature
139
+ }
140
+
141
+ // Step 5: Authenticate with SPAPS
142
+ // ✅ CORRECT METHOD NAME: authenticateWallet
143
+ const authResponse = await sdk.auth.authenticateWallet(
144
+ address,
145
+ signatureCallback,
146
+ 'ethereum' // or chainType from detection
147
+ )
148
+
149
+ // Step 6: Store tokens
150
+ TokenManager.storeTokens(authResponse)
151
+
152
+ // Step 7: Setup auto-refresh
153
+ TokenManager.autoRefreshToken(sdk)
154
+
155
+ toast.success('Wallet authenticated successfully!')
156
+ console.log('Auth response:', authResponse)
157
+
158
+ } catch (error) {
159
+ handleWalletError(error)
160
+ } finally {
161
+ setIsLoading(false)
162
+ }
163
+ }
164
+
165
+ const handleWalletError = (error: any) => {
166
+ if (error instanceof SweetPotatoAPIError) {
167
+ switch (error.code) {
168
+ case 'INVALID_SIGNATURE':
169
+ toast.error('Invalid wallet signature')
170
+ break
171
+ case 'WALLET_NOT_FOUND':
172
+ toast.error('Wallet not registered')
173
+ break
174
+ case 'RATE_LIMITED':
175
+ toast.error('Too many attempts. Please wait.')
176
+ break
177
+ default:
178
+ toast.error(error.message)
179
+ }
180
+ } else if (error?.code === 4001) {
181
+ // User rejected the signature request in MetaMask
182
+ toast.error('Signature request rejected')
183
+ } else if (error?.code === -32002) {
184
+ // Request already pending in MetaMask
185
+ toast.error('Please check MetaMask for pending request')
186
+ } else {
187
+ toast.error(error.message || 'Wallet authentication failed')
188
+ }
189
+ console.error('Wallet auth error:', error)
190
+ }
191
+
192
+ // Test wallet button
193
+ const fillTestWallet = () => {
194
+ setWalletAddress('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb8')
195
+ }
196
+
197
+ return (
198
+ <Card className="w-[400px]">
199
+ <CardHeader>
200
+ <CardTitle>Wallet Authentication</CardTitle>
201
+ <CardDescription>
202
+ Connect your Ethereum wallet to authenticate
203
+ </CardDescription>
204
+ </CardHeader>
205
+ <CardContent className="space-y-4">
206
+ <div className="p-4 bg-muted rounded-lg">
207
+ <p className="text-sm font-medium">Current Address:</p>
208
+ <p className="text-xs font-mono mt-1">
209
+ {walletAddress || 'Not connected'}
210
+ </p>
211
+ </div>
212
+
213
+ <Button
214
+ onClick={handleWalletConnect}
215
+ className="w-full"
216
+ disabled={isLoading}
217
+ >
218
+ {isLoading ? 'Connecting...' : 'Connect Ethereum Wallet'}
219
+ </Button>
220
+
221
+ <Button
222
+ variant="outline"
223
+ className="w-full"
224
+ onClick={fillTestWallet}
225
+ >
226
+ Use Test Wallet Address
227
+ </Button>
228
+
229
+ <p className="text-xs text-muted-foreground">
230
+ Test wallet: 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb8
231
+ </p>
232
+ </CardContent>
233
+ </Card>
234
+ )
235
+ }
236
+
237
+ // TypeScript declarations for MetaMask
238
+ declare global {
239
+ interface Window {
240
+ ethereum?: {
241
+ request: (args: { method: string; params?: any[] }) => Promise<any>
242
+ on: (event: string, handler: (...args: any[]) => void) => void
243
+ removeListener: (event: string, handler: (...args: any[]) => void) => void
244
+ }
245
+ }
246
+ }
247
+ ```
248
+
249
+ ## How authenticateWallet Works
250
+
251
+ The SDK handles these steps automatically:
252
+ 1. **Gets nonce** from server for the wallet address
253
+ 2. **Creates message** to sign with nonce
254
+ 3. **Calls your callback** with the message
255
+ 4. **Receives signature** from your callback
256
+ 5. **Verifies signature** on server
257
+ 6. **Returns auth tokens** if valid
258
+
259
+ You only need to provide the signature callback!
260
+
261
+ ## Testing Without Real Wallet
262
+
263
+ For testing without MetaMask:
264
+
265
+ ```typescript
266
+ const mockSignatureCallback = async (message: string): Promise<string> => {
267
+ console.log('Would sign message:', message)
268
+ // Return mock signature for testing
269
+ return '0xmocked_signature_for_testing_purposes_only'
270
+ }
271
+
272
+ // Use in test mode
273
+ const auth = await sdk.auth.authenticateWallet(
274
+ '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb8',
275
+ mockSignatureCallback,
276
+ 'ethereum'
277
+ )
278
+ ```
279
+
280
+ ## Validation Checklist
281
+
282
+ ✅ **Method Name**:
283
+ - Uses `authenticateWallet` (NOT signInWithWallet)
284
+ - Three parameters: address, callback, chainType
285
+
286
+ ✅ **Signature Callback**:
287
+ - Receives message as parameter
288
+ - Returns signature as string or Promise<string>
289
+ - Properly signs with wallet
290
+
291
+ ✅ **Chain Detection**:
292
+ - Uses `WalletUtils.detectChainType(address)`
293
+ - Passes correct chain type ('ethereum', 'solana', etc.)
294
+
295
+ ✅ **Token Storage**:
296
+ - Calls `TokenManager.storeTokens(authResponse)`
297
+ - Sets up `TokenManager.autoRefreshToken(sdk)`
298
+
299
+ ✅ **Error Handling**:
300
+ - Handles MetaMask not installed
301
+ - Handles signature rejection (code 4001)
302
+ - Handles SweetPotatoAPIError codes
303
+
304
+ ## Common Mistakes to Avoid
305
+
306
+ ❌ **Wrong method name**:
307
+ ```typescript
308
+ // WRONG - This method doesn't exist!
309
+ sdk.auth.signInWithWallet(address, signature, chain)
310
+ ```
311
+
312
+ ❌ **Not providing callback**:
313
+ ```typescript
314
+ // WRONG - Missing signature callback
315
+ sdk.auth.authenticateWallet(address)
316
+ ```
317
+
318
+ ❌ **Signing outside of callback**:
319
+ ```typescript
320
+ // WRONG - Don't pre-sign
321
+ const signature = await signer.signMessage('some message')
322
+ sdk.auth.authenticateWallet(address, signature) // NO!
323
+ ```
324
+
325
+ ✅ **CORRECT - Provide callback**:
326
+ ```typescript
327
+ sdk.auth.authenticateWallet(
328
+ address,
329
+ async (message) => await signer.signMessage(message),
330
+ 'ethereum'
331
+ )
332
+ ```
333
+
334
+ ## Testing Requirements
335
+
336
+ 1. **With MetaMask**:
337
+ - Install MetaMask extension
338
+ - Connect to any network
339
+ - Click "Connect Wallet"
340
+ - Sign the message when prompted
341
+ - Should authenticate successfully
342
+
343
+ 2. **Test Address**:
344
+ - Address: `0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb8`
345
+ - Should work with mock signature in local mode
346
+
347
+ 3. **Error Cases**:
348
+ - Reject signature → Should show "rejected" error
349
+ - No MetaMask → Should show "install MetaMask" error
350
+
351
+ ## Next Step
352
+
353
+ When ALL todos are ✅ complete and wallet auth works:
354
+
355
+ ```javascript
356
+ mcp__product-manager__get_agent_instructions({
357
+ category: "spaps-integration",
358
+ project: "spaps-demo",
359
+ step: 6
360
+ })
361
+ ```
362
+
363
+ ## Remember
364
+
365
+ 🔴 **CRITICAL**: The method is `authenticateWallet` NOT `signInWithWallet`!
366
+ - This is the #1 mistake that causes integration failures
367
+ - Double-check your code uses the correct method name
368
+ - The signature must be provided via callback, not directly