How to build a mobile wallet for Starknet
In this tutorial we will build a mobile wallet app.
Pre-requisites
- Create a new flutter project
flutter create wallet_app
Run it locally with flutter run
to make sure it is properly configured.
-
Install and run
starknet-devnet
-
Add necessary dependencies
flutter pub add wallet_kit hive_flutter hooks_riverpod flutter_dotenv
- Create a
.env
file in the root of your wallet_app project
ACCOUNT_CLASS_HASH="0x061dac032f228abef9c6626f995015233097ae253a7f72d68552db02f2971b8f"
RPC="http://127.0.0.1:5050/rpc"
Please note that
ACCOUNT_CLASS_HASH
must match the one used by your version ofstarknet-devnet
, it's displayed at startup. Here is the value forstarknet-devnet 0.2.0
Predeployed accounts using class with hash: 0x061dac032f228abef9c6626f995015233097ae253a7f72d68552db02f2971b8f
If you are running on another device than the host running
starknet-devnet
, you should use the external IP of your host running and startstarknet-devnet
with--host 0.0.0.0
argument
- Add
.env
file in yourpubspec.yaml
assets:
- .env
- Update Android minimun SDK version
secure_store
package used by wallet_kit
require Android minimum SDK version set to at least 23, you need to modify android/app/build.gradle
:
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.wallet_app"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = 23
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
- Biometric support (optional)
In order to use Biometric
on Android, your MainActivity
must inherit from FlutterFragmentActivity
instead of FlutterActity
.
You need to modify your MainActivity.kt
with:
import io.flutter.embedding.android.FlutterFragmentActivity
class MainActivity: FlutterFragmentActivity()
Let's write some code
Let's start with a simple main
function in your 'main.dart' file.
import 'package:flutter/material.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
}
We will need to load our environment variables using flutter_dotenv
package
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await dotenv.load();
}
Now let's intialize wallet_kit
and hive
in our main
function
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:wallet_kit/wallet_kit.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await dotenv.load();
await WalletKit().init(
accountClassHash: dotenv.env['ACCOUNT_CLASS_HASH'] as String,
rpc: dotenv.env['RPC'] as String,
);
await Hive.initFlutter();
}
Let's also setup device orientation and system ui overlay
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.edgeToEdge,
);
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
systemNavigationBarColor: Colors.transparent,
systemNavigationBarDividerColor: Colors.transparent,
systemNavigationBarIconBrightness: Brightness.dark,
));
Finally we can create an App widget and run our app:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:wallet_kit/wallet_kit.dart';
import './screens/home_screen.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await dotenv.load();
WalletKit().init(
accountClassHash: dotenv.env['ACCOUNT_CLASS_HASH'] as String,
rpc: dotenv.env['RPC'] as String,
);
await Hive.initFlutter();
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.edgeToEdge,
);
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
systemNavigationBarColor: Colors.transparent,
systemNavigationBarDividerColor: Colors.transparent,
systemNavigationBarIconBrightness: Brightness.dark,
));
runApp(const ProviderScope(child: WalletApp()));
}
class WalletApp extends HookConsumerWidget {
const WalletApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return MaterialApp(
title: 'Starknet Wallet',
home: const Placeholder(),
theme: walletThemeData,
debugShowCheckedModeBanner: false,
);
}
}
Create a screens/
folder and add home_screen.dart
file with a pre-built layout from wallet_kit
, as well as WalletSelector
, AccountAddress
, WalletBody
and SendEthButton
:
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:wallet_kit/wallet_kit.dart';
class HomeScreen extends HookConsumerWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return const Scaffold(
body: Layout2(
children: [
SizedBox(height: 32),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
WalletSelector(),
AccountAddress(),
DeployAccountButton(),
],
),
SizedBox(height: 32),
WalletBody(),
SendEthButton(),
],
),
);
}
}
Now replace home: const Placeholder()
with home: const HomeScreen()
in main.dart
. Your WalletApp should now look like this:
import './screens/home_screen.dart';
class WalletApp extends HookConsumerWidget {
const WalletApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return MaterialApp(
title: 'Starknet Wallet',
home: const HomeScreen(),
theme: walletThemeData,
debugShowCheckedModeBanner: false,
);
}
}
Now you can run your app with flutter run
and see your wallet in action! 💸
Deploying an account requires some ETH to pay transaction fees.
With starknet-devnet
, you can mint some ETH to your account address with the following command:
curl --silent -H 'Content-type: application/json' \
-X POST http://localhost:5050/mint \
-d '{"address": ""<YOUR_ACCOUNT_ADDRESS>"", "amount": 20000000000000000000, "unit": "WEI"}'
{"new_balance":"20000000000000000000","unit":"WEI","tx_hash":"0x9d2d26cef777c50b64475592e0df6e6c6012014e660f97bb37aaf5138aff54"}