This is a self-custodial Bitcoin wallet application built on the Internet Computer Protocol (ICP). The app uses Internet Identity for authentication and manages Bitcoin addresses for users.
- Type: Asset canister (static files)
- Technology: React + TypeScript + Vite
- Purpose: Serves the user interface (HTML, CSS, JavaScript)
- Location:
frontend/directory - Deployment: Static assets are uploaded to the canister
- Type: Motoko canister
- Technology: Motoko (ICP's native language)
- Purpose: Business logic and data storage
- Location:
backend/main.mo - Deployment: Compiled Motoko code runs on ICP
- Local:
http://localhost:4943/?canisterId=uzt4z-lp777-77774-qaabq-cai - Production:
https://id.ai/ - Purpose: User authentication and identity management
User's Browser
↓
Frontend Canister (serves React app)
↓
Internet Identity (authenticates user)
↓
HttpAgent (creates authenticated requests)
↓
Backend Canister (processes requests, stores data)
↓
Returns data to frontend
↓
User sees updated UI
-
Identity Authentication (
useInternetIdentity.tsx)- User logs in with Internet Identity
- Gets an authenticated identity (Principal)
- Identity is used to sign all backend requests
-
Actor Creation (
useActor.ts)- Creates an
HttpAgentwith the user's identity - Creates an
Actorusing the backend canister ID - The actor provides type-safe methods to call backend functions
- Creates an
-
Backend Communication (
useQueries.ts)- Uses React Query for data fetching
- Calls backend methods through the actor
- Handles loading states, errors, and caching
frontend/src/hooks/useInternetIdentity.tsx: Manages Internet Identity authenticationfrontend/src/hooks/useActor.ts: Creates the backend actor connectionfrontend/src/hooks/useQueries.ts: React Query hooks for backend databackend/main.mo: Motoko backend with all business logic
The backend canister (moto) provides these methods:
ensureWalletExists(): Creates or returns user's Bitcoin addressgetWalletInfo(): Returns user's wallet information (optional - returns null if wallet doesn't exist)getBalance(): Returns user's balancegetTransactionHistory(): Returns user's transaction historysendTransaction(): Sends a transactiongetBitcoinAddress(): Returns user's Bitcoin addresscompleteOnboarding(): Marks onboarding as completeisOnboardingComplete(): Checks if onboarding is complete
- Frontend:
http://localhost:5173(Vite dev server) - Backend:
http://localhost:4943(dfx local network) - Internet Identity: Local canister
- Environment:
VITE_DFX_NETWORK=local
- Frontend:
https://<canister-id>.icp0.io/ - Backend:
https://ic0.app(mainnet) - Internet Identity:
https://id.ai/ - Environment:
VITE_DFX_NETWORK=icor auto-detected from.icp0.ioURL
The app automatically detects the network:
- URL-based: If URL contains
.ic0.appor.icp0.io→ Production - Environment variable:
VITE_DFX_NETWORKcan override - Hostname:
localhost→ Local development
- Backend:
2en3s-2iaaa-aaaad-qhqja-cai(Stopped) - Frontend:
2rkk7-3aaaa-aaaad-qhqkq-cai(Stopped)
- Message Size Limit: Asset canister has a 3.1MB message reply limit
- Cycle Consumption: Failed deployments consumed cycles during retries
- Asset Deployment: Frontend deployment failed due to message size limits
- Local Development First: Test everything locally before deploying to mainnet
- Optimize Build: Reduce frontend bundle size to avoid message size limits
- Monitor Cycles: Check canister cycles regularly to avoid unexpected costs
- Incremental Deployment: Deploy backend first, test, then deploy frontend
- Test the app locally with
dfx startandnpm run dev - Verify the connection between frontend and backend
- Optimize the frontend build size
- Test deployment on a smaller scale first