Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Add wallet app tutorial #421

Merged
merged 5 commits into from
Dec 1, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 192 additions & 18 deletions docs/examples/mobile-wallet.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# How to build a mobile wallet for Starknet

In this tutorial we will build a mobile wallet app.

## Pre-requisites

1. Create a new flutter project

```
Expand All @@ -8,30 +12,200 @@ flutter create wallet_app

Run it locally with `flutter run` to make sure it is properly configured.

2. Create a `contracts` folder and setup a scarb project.
2. Install and run [`starknet-devnet`](https://0xspaceshard.github.io/starknet-devnet-rs/docs/running/install)

3. Add necessary dependnecies

```bash
flutter pub add wallet_kit hive_flutter hooks_riverpod flutter_dotenv
```

4. Create a `.env` file in the root of your wallet_app project

```bash
ACCOUNT_CLASS_HASH="0x05400e90f7e0ae78bd02c77cd75527280470e2fe19c54970dd79dc37a9d3645c"
RPC="http://127.0.0.1:5050/rpc"
```

> If you are building for Android, use `RPC="http://10.0.2.2:5050/rpc"` instead.

## Let's write some code

Let's start with a simple `main` function in your 'main.dart' file.

```dart
import 'package:flutter/material.dart';

- Install Rust
```
rustup override set stable && rustup update
```
- [Install scarb](https://docs.swmansion.com/scarb/docs/install) and run `scarb --version` to make sure it is working properly
- Run `scarb new contracts` in `wallet_app/`
- Run `scarb build` in `contracts/`
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
}
```

We will need to load our environment variables using `flutter_dotenv` package

```dart
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';

Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();

await dotenv.load();
}
```

3. Run starknet locally with devnet
Now let's intialize `wallet_kit` and `hive` in our `main` function

- `poetry new devnet`
- `cd devnet`
- `poetry add starknet-devnet`
- `poetry run starknet-devnet`
```dart
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';

4. Add `deps`
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();

- `flutter pub add starknet_flutter`
- ```
flutter pub add hooks_riverpod dev:custom_lint dev:riverpod_lint riverpod_annotation dev:build_runner dev:riverpod_generator
```
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

```dart
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:

```dart
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,
);
Comment on lines +117 to +120
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add await to WalletKit initialization

The WalletKit initialization in the tutorial is missing the await keyword, unlike the actual implementation in main.dart.

-  WalletKit().init(
+  await WalletKit().init(
     accountClassHash: dotenv.env['ACCOUNT_CLASS_HASH'] as String,
     rpc: dotenv.env['RPC'] as String,
   );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
WalletKit().init(
accountClassHash: dotenv.env['ACCOUNT_CLASS_HASH'] as String,
rpc: dotenv.env['RPC'] as String,
);
await 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`:

```dart
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 Layout2(
children: [
SizedBox(height: 32),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
WalletSelector(),
AccountAddress(),
],
),
SizedBox(height: 32),
WalletBody(),
SendEthButton(),
],
);
}
}
```

Now replace `home: const Placeholder()` with `home: const HomeScreen()` in `main.dart`. Your WalletApp should now look like this:

```dart
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! 💸
2 changes: 1 addition & 1 deletion examples/wallet_app/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.7.10'
ext.kotlin_version = '2.0.21'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

⚠️ Potential issue

Kotlin 2.0.21 is incompatible with Android Gradle Plugin 7.3.0

The verification confirms that the Android Gradle Plugin version 7.3.0 is being used in the project. Kotlin 2.0.x is not compatible with this AGP version, as it requires a more recent version of the Android Gradle Plugin. Additionally, other examples in the codebase are using Kotlin 1.7.10, which indicates this is the established version across the project.

Recommendations:

  • Either downgrade Kotlin to 1.9.x (preferably 1.9.22) for compatibility with AGP 7.3.0
  • Or upgrade both AGP and Kotlin versions together following the official compatibility matrix
🔗 Analysis chain

Consider using a more stable Kotlin version for the tutorial

The update to Kotlin 2.0.21 introduces potential risks:

  1. Kotlin 2.0 is a very recent major release that might have compatibility issues with existing Android tooling
  2. For a tutorial, using such a new version might cause problems for users following along

Consider using the latest stable 1.x version (e.g., 1.9.22) which is more widely tested with Android tooling.

Let's verify the compatibility with Android Gradle Plugin:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check Kotlin versions recommended for Android Gradle Plugin 7.3.0

# Search for any Kotlin version recommendations in the Android documentation
rg --type md "Kotlin.*version.*7\.3\.0" 

# Look for any version constraints in build files
rg "kotlin.*version.*7\.3\.0"

Length of output: 73


Script:

#!/bin/bash
# Let's check the current Android Gradle Plugin version and other dependencies

# Check build.gradle files for Android Gradle Plugin version
rg "com\.android\.tools\.build:gradle" -A 2

# Check other Kotlin-related dependencies
rg "org\.jetbrains\.kotlin" -A 2

# Look for any other build configuration that might give context
rg "compileSdk|minSdk|targetSdk|buildToolsVersion" 

Length of output: 4246

repositories {
google()
mavenCentral()
Expand Down
1 change: 0 additions & 1 deletion examples/wallet_app/contracts
Submodule contracts deleted from 6ca81b
Empty file.
Empty file.
Loading
Loading