A Flutter mobile application demonstrating authentication with Privy and smart contract interaction on Starknet. This template is designed to help developers bootstrap mobile dApps on Starknet with a production-ready integration of authentication and blockchain state management.
This template provides a complete mobile dApp implementation featuring:
- π Email OTP Authentication via Privy Flutter SDK
- βοΈ Starknet Integration with on-chain counter contract
- π¨ Modern UI with dark theme and smooth animations
- π± iOS Support (iOS 16.0+)
- π State Management using Riverpod
- π Production-Ready architecture and error handling
- β‘ Demo Mode with optimistic updates for smooth UX
β οΈ Important Note on V3 Transactions
This template usesstarknet.dart 0.2.0which defaults to V3 transactions. However, most public RPC providers (including Alchemy, Infura) do not yet support V3 transactions on Sepolia (UNSUPPORTED_TX_VERSIONerror).Current Behavior: The app is configured in Demo Mode with optimistic UI updates - the counter increments/decrements immediately in the UI even though transactions fail on the RPC level. This provides a smooth demonstration experience.
For Production:
- Use a local devnet (
starknet-devnet) which supports V3 transactions- Wait for RPC providers to add V3 support
- Or modify the account to use V1/V2 transactions (older spec)
starknet-mobile-counter-dapp/
βββ lib/
β βββ config/
β β βββ constants.dart # Contract address, RPC URL, Privy config
β β βββ theme.dart # App theme configuration
β βββ features/
β β βββ auth/
β β β βββ providers/ # Authentication state management
β β β βββ screens/ # Login screen with OTP flow
β β β βββ services/ # Privy service wrapper
β β βββ counter/
β β βββ providers/ # Counter state management
β β βββ screens/ # Counter display and interaction
β β βββ services/ # Starknet service for contract calls
β β βββ widgets/ # Reusable UI components
β βββ main.dart # App entry point
βββ contracts/
β βββ src/lib.cairo # Counter smart contract
β βββ Counter_ABI.json # Contract ABI
β βββ Scarb.toml # Cairo project config
βββ .env # Environment variables (GitIgnored)
βββ ios/ # iOS-specific configuration
- PrivyService: Handles email OTP authentication flow.
- StarknetService: Manages account derivation and contract interactions.
- AuthNotifier: Manages authentication state with Riverpod.
- CounterNotifier: Manages counter state and contract calls.
- Flutter SDK: 3.24.0 or higher
- Dart: 3.0.0 or higher
- iOS: Xcode 15+ (for iOS development)
- Privy Account: Sign up at dashboard.privy.io
# Clone the repository
git clone <your-repo-url>
cd starknet-mobile-counter-dapp
# Install Flutter dependencies
flutter pub get
# Install iOS dependencies (macOS only)
cd ios && pod install && cd ..-
Create a
.envfile in the root directory:touch .env
-
Add the following variables to
.env:# Starknet RPC URL (Alchemy, Infura, or public node) RPC_URL=https://starknet-sepolia.public.blastapi.io/rpc/v0_7 # Deployer Account (Fallback for Demo) # NOTE: In production, users should deploy their own accounts. # WARNING: Never commit real private keys to version control! DEPLOYER_PRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE DEPLOYER_ADDRESS=0xYOUR_ACCOUNT_ADDRESS_HERE
-
Create a Privy App:
- Go to dashboard.privy.io
- Create a new app
- Copy your App ID and Client ID
-
Enable Email Authentication:
- Navigate to User Management β Authentication
- Toggle Email authentication ON
-
Register iOS Bundle Identifier:
- Go to Configuration β App Settings β Clients
- Add iOS Bundle Identifier:
com.example.starknetMobileCounter
-
Update Configuration:
- Open
lib/config/constants.dart - Replace
privyAppIdandprivyAppClientIdwith your credentials
- Open
# Run on iOS simulator
flutter runThe app uses Privy Flutter SDK for passwordless authentication via email OTP.
- Initialization: Privy is initialized in
main.dartbefore the app starts. - Login Flow:
- User enters email β
PrivyService.sendCode(email) - User enters OTP β
PrivyService.loginWithCode(code, email) - On success, the user is authenticated and redirected to the Counter screen.
- User enters email β
The Counter contract is written in Cairo and deployed on Starknet Sepolia.
Contract Address: 0x02d2a4804f83c34227314dba41d5c2f8a546a500d34e30bb5078fd36b5af2d77
Demo Limitation: In a real-world dApp, each user must deploy their own Starknet account (Abstract Account) before sending transactions. This requires funding the address with ETH for gas.
This Template's Approach (Demo Mode):
- Account Fallback: If the user's derived account is not deployed, the app falls back to a pre-funded Deployer Account (configured in
.env) to allow immediate interaction without friction. - Optimistic Updates: To provide a snappy user experience even if the network is congested or if there are RPC issues, the app uses optimistic updates. When you click "Increment", the UI updates immediately while the transaction is processed in the background.
- Reading:
get_counteris called usingprovider.call. - Writing:
increase_counteranddecrease_counterare called usingaccount.execute(Invoke Transaction V3).
This section walks through the complete user journey in the app, from authentication to interacting with the on-chain counter.
What happens:
- User opens the app and sees the login screen
- User enters their email address
- User receives a one-time password (OTP) code via email
- User enters the OTP code to authenticate
Technical details:
LoginScreenusesPrivyService.sendCode(email)to trigger the OTP email- After code entry,
PrivyService.loginWithCode(code, email)authenticates the user - On success,
AuthNotifierupdates the authentication state with thePrivyUserobject - The app automatically navigates to the Counter screen
Code reference:
- UI:
lib/features/auth/screens/login_screen.dart - Service:
lib/features/auth/services/privy_service.dart - State:
lib/features/auth/providers/auth_provider.dart
What happens:
- Once authenticated, the app initializes a Starknet account for the user
- The wallet address is displayed in the app bar
Technical details:
walletProviderwatches the authentication state- When a user is authenticated,
StarknetService.initAccount(userId)is called - Demo Mode: The app uses a pre-funded deployer account (from
.env) as a fallback to enable immediate interaction without requiring account deployment or funding - In production, you would derive a unique account from the user's Privy wallet and deploy it on-chain
Code reference:
- Service:
lib/features/counter/services/starknet_service.dart - Provider:
lib/features/counter/providers/counter_provider.dart
What happens:
- The counter screen displays the current count value
- The UI shows the user's wallet address in the app bar
- Two action buttons (+ and -) are available for interaction
Technical details:
CounterNotifierautomatically fetches the counter value on initialization- The counter is read using
StarknetService.getCounterValue(walletAddress) - This calls the
get_counter(user)function on the smart contract usingprovider.call - The counter value updates in real-time when transactions complete
Code reference:
- UI:
lib/features/counter/screens/counter_screen.dart - Service:
lib/features/counter/services/starknet_service.dart - Provider:
lib/features/counter/providers/counter_provider.dart
What happens:
- User taps the + button
- The counter value increments immediately in the UI (optimistic update)
- A transaction is sent to the Starknet network in the background
- If successful, the new value persists on-chain
- If the transaction fails, the UI reverts to the previous value
Technical details:
CounterNotifier.increment()performs an optimistic update first- Then calls
StarknetService.increaseCounter() - This executes an invoke transaction using
account.execute()with theincrease_counterfunction selector - Demo Mode: If the transaction fails (e.g., due to V3 transaction incompatibility), the optimistic update remains for demonstration purposes
- In production with V3 support, the transaction would be confirmed on-chain
Code reference:
- Provider:
lib/features/counter/providers/counter_provider.dart - Service:
lib/features/counter/services/starknet_service.dart
What happens:
- User taps the - button
- The counter value decrements immediately in the UI (optimistic update)
- A transaction is sent to the Starknet network in the background
- Similar behavior to the increase flow
Technical details:
CounterNotifier.decrement()performs an optimistic update- Calls
StarknetService.decreaseCounter() - Uses the same transaction flow as increment but with the
decrease_counterfunction selector
Code reference:
- Provider:
lib/features/counter/providers/counter_provider.dart
What happens:
- User taps the logout button in the app bar
- The app clears the authentication state and returns to the login screen
Technical details:
AuthNotifier.logout()callsPrivyService.logout()- The authentication state is reset to
null - The app automatically navigates back to the login screen
β
Passwordless Authentication - Email OTP via Privy
β
Starknet Account Management - Account initialization and address derivation
β
Smart Contract Reads - Fetching on-chain state with provider.call
β
Smart Contract Writes - Sending transactions with account.execute
β
Optimistic UI Updates - Immediate feedback for better UX
β
Error Handling - Graceful fallbacks and user-friendly error messages
β
State Management - Reactive UI with Riverpod providers
- Run the app:
flutter run - Login: Use any email address (Privy will send a real OTP)
- View Counter: See the current count for your wallet address
- Increment: Tap + and watch the optimistic update
- Decrement: Tap - to decrease the counter
- Logout: Tap the logout icon to end the session
π‘ Tip: In Demo Mode, the counter updates immediately even if the blockchain transaction fails. For production with V3 transaction support, transactions will be confirmed on-chain before the UI updates permanently.
Update contractAddress in lib/config/constants.dart.
- Update
Counter_ABI.json(optional, for reference). - Add the function selector in
lib/config/constants.dart(if using constants for selectors). - Implement the method in
StarknetService.
MIT License - feel free to use this template for your projects!
Contributions are welcome! Please feel free to submit a Pull Request.
- Starknet Dart SDK: https://github.com/starknet-ecosystem/starknet.dart
- Privy Flutter SDK: https://docs.privy.io
- Starknet Docs: https://docs.starknet.io