diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml
index 3fce297..d7fd112 100644
--- a/.idea/libraries/Dart_Packages.xml
+++ b/.idea/libraries/Dart_Packages.xml
@@ -30,6 +30,13 @@
+
+
+
+
+
+
+
@@ -44,6 +51,13 @@
+
+
+
+
+
+
+
@@ -86,6 +100,13 @@
+
+
+
+
+
+
+
@@ -93,6 +114,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -142,6 +177,13 @@
+
+
+
+
+
+
+
@@ -156,6 +198,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -184,6 +240,13 @@
+
+
+
+
+
+
+
@@ -254,6 +317,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -289,6 +366,13 @@
+
+
+
+
+
+
+
@@ -324,6 +408,13 @@
+
+
+
+
+
+
+
@@ -450,26 +541,35 @@
+
+
+
+
+
+
+
+
+
@@ -480,15 +580,19 @@
+
+
+
+
diff --git a/lib/data/base/text_value.dart b/lib/data/base/text_value.dart
index f725eb2..019ae13 100644
--- a/lib/data/base/text_value.dart
+++ b/lib/data/base/text_value.dart
@@ -2,7 +2,7 @@ import '../error/input_error.dart';
class TextValue {
TextValue({
- required this.text,
+ this.text = "",
this.error,
});
diff --git a/lib/data/error/input_error.dart b/lib/data/error/input_error.dart
index d038c98..b552079 100644
--- a/lib/data/error/input_error.dart
+++ b/lib/data/error/input_error.dart
@@ -16,3 +16,13 @@ class RequiredAmount extends InputError {
@override
String text = "Field is required";
}
+
+class KeyNotBase58Encoded extends InputError {
+ @override
+ String text = "Invalid value, must be base58 encoded";
+}
+
+class KeyLengthInvalid extends InputError {
+ @override
+ String text = "Key value must be between 32 and 44 characters";
+}
diff --git a/lib/di/blocs_providers.dart b/lib/di/blocs_providers.dart
index be279c6..bc486b8 100644
--- a/lib/di/blocs_providers.dart
+++ b/lib/di/blocs_providers.dart
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:sol_pay_gen/domain/generate_transfer_request_qr_use_case.dart';
import 'package:sol_pay_gen/feature/qr/bloc/qr_generator_cubit.dart';
+import 'package:sol_pay_gen/validator/keys_validator.dart';
import 'package:sol_pay_gen/validator/number_validator.dart';
import '../data/transfer/transfer_request_repository.dart';
@@ -11,8 +12,10 @@ MultiBlocProvider getBlocProviders({required StatelessWidget child}) {
return MultiBlocProvider(
providers: [
BlocProvider(
- create: (BuildContext context) =>
- ParametersInputCubit(context.read()),
+ create: (BuildContext context) => ParametersInputCubit(
+ context.read(),
+ context.read(),
+ ),
),
BlocProvider(
create: (BuildContext context) => QrGeneratorCubit(
diff --git a/lib/di/repositories_providers.dart b/lib/di/repositories_providers.dart
index d49c911..9de3dcd 100644
--- a/lib/di/repositories_providers.dart
+++ b/lib/di/repositories_providers.dart
@@ -1,5 +1,6 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:sol_pay_gen/data/transfer/transfer_request_repository.dart';
+import 'package:sol_pay_gen/validator/keys_validator.dart';
import 'package:sol_pay_gen/validator/number_validator.dart';
MultiRepositoryProvider getRepositoryProviders({
@@ -8,10 +9,13 @@ MultiRepositoryProvider getRepositoryProviders({
return MultiRepositoryProvider(
providers: [
RepositoryProvider(
- create: (context) => DefaultTransferRequestRepository(),
+ create: (_) => DefaultTransferRequestRepository(),
),
RepositoryProvider(
- create: (context) => DefaultNumberValidator(),
+ create: (_) => DefaultNumberValidator(),
+ ),
+ RepositoryProvider(
+ create: (_) => DefaultKeysValidator(),
)
],
child: blocProviders,
diff --git a/lib/feature/input/bloc/parameters_input_cubit.dart b/lib/feature/input/bloc/parameters_input_cubit.dart
index c2e6cbf..e798ad4 100644
--- a/lib/feature/input/bloc/parameters_input_cubit.dart
+++ b/lib/feature/input/bloc/parameters_input_cubit.dart
@@ -2,18 +2,22 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:sol_pay_gen/data/base/text_value.dart';
import 'package:sol_pay_gen/feature/input/bloc/parameters_input_state.dart';
+import '../../../data/error/input_error.dart';
+import '../../../validator/keys_validator.dart';
import '../../../validator/number_validator.dart';
class ParametersInputCubit extends Cubit {
final NumberValidator _numberValidator;
+ final KeysValidator _keysValidator;
ParametersInputCubit(
this._numberValidator,
+ this._keysValidator,
) : super(
ParametersInputState(
- address: TextValue(text: ""),
- amount: TextValue(text: ""),
- reference: null,
+ address: TextValue(),
+ amount: TextValue(),
+ reference: TextValue(),
memo: null,
message: null,
label: null,
@@ -22,9 +26,12 @@ class ParametersInputCubit extends Cubit {
);
void onAddressChange(String address) {
- emit(
- state.copyWith(address: TextValue(text: address)),
- );
+ emit(state.copyWith(
+ address: TextValue(
+ text: address,
+ error: _validateAddress(address),
+ ),
+ ));
}
void onAmountChange(String amount) {
@@ -45,7 +52,12 @@ class ParametersInputCubit extends Cubit {
}
void onReferenceChange(String reference) {
- emit(state.copyWith(reference: reference));
+ emit(state.copyWith(
+ reference: TextValue(
+ text: reference,
+ error: reference.isEmpty ? null : _keysValidator.validateKey(reference),
+ ),
+ ));
}
void onSplTokenChange(String token) {
@@ -57,6 +69,16 @@ class ParametersInputCubit extends Cubit {
}
void onValidate() {
- emit(state.validate());
+ emit(state.copyWith(
+ address: state.address.copyWith(
+ error: _validateAddress(state.address.text),
+ ),
+ ));
+ }
+
+ InputError? _validateAddress(String address) {
+ return address.isEmpty
+ ? RequiredAmount()
+ : _keysValidator.validateKey(address);
}
}
diff --git a/lib/feature/input/bloc/parameters_input_state.dart b/lib/feature/input/bloc/parameters_input_state.dart
index fe99384..d9d5b7c 100644
--- a/lib/feature/input/bloc/parameters_input_state.dart
+++ b/lib/feature/input/bloc/parameters_input_state.dart
@@ -1,11 +1,10 @@
import 'package:equatable/equatable.dart';
import 'package:sol_pay_gen/data/base/text_value.dart';
-import 'package:sol_pay_gen/data/error/input_error.dart';
class ParametersInputState extends Equatable {
final TextValue address;
final TextValue amount;
- final String? reference;
+ final TextValue reference;
final String? label;
final String? message;
final String? memo;
@@ -36,7 +35,7 @@ class ParametersInputState extends Equatable {
TextValue? amount,
String? label,
String? message,
- String? reference,
+ TextValue? reference,
String? memo,
String? splTokenAddress,
}) =>
@@ -50,13 +49,6 @@ class ParametersInputState extends Equatable {
splTokenAddress: splTokenAddress ?? this.splTokenAddress,
);
- ParametersInputState validate() {
- return copyWith(
- address: address.copyWith(
- error: address.text.isEmpty ? RequiredAmount() : null,
- ),
- );
- }
-
- bool isValid() => amount.isValid() && address.isValid();
+ bool isValid() =>
+ amount.isValid() && address.isValid() && reference.isValid();
}
diff --git a/lib/feature/input/parameters_input_screen.dart b/lib/feature/input/parameters_input_screen.dart
index 16b95ce..55978f4 100644
--- a/lib/feature/input/parameters_input_screen.dart
+++ b/lib/feature/input/parameters_input_screen.dart
@@ -79,6 +79,7 @@ class InputBody extends StatelessWidget {
const Padding(padding: EdgeInsets.only(top: 16.0)),
BaseInput(
labelText: 'Reference',
+ error: state.reference.error?.text,
onChanged: (address) => context
.read()
.onReferenceChange(address),
diff --git a/lib/feature/qr/bloc/qr_generator_cubit.dart b/lib/feature/qr/bloc/qr_generator_cubit.dart
index f81b66e..b6d84a9 100644
--- a/lib/feature/qr/bloc/qr_generator_cubit.dart
+++ b/lib/feature/qr/bloc/qr_generator_cubit.dart
@@ -26,7 +26,7 @@ class QrGeneratorCubit extends Cubit {
label: inputState.label,
message: inputState.message,
amount: inputState.amount.text,
- reference: inputState.reference,
+ reference: inputState.reference.text,
memo: inputState.memo,
splToken: inputState.splTokenAddress,
);
diff --git a/lib/validator/keys_validator.dart b/lib/validator/keys_validator.dart
new file mode 100644
index 0000000..ae7cc5f
--- /dev/null
+++ b/lib/validator/keys_validator.dart
@@ -0,0 +1,27 @@
+import 'package:solana/base58.dart';
+
+import '../data/error/input_error.dart';
+
+abstract class KeysValidator {
+ InputError? validateKey(String key);
+}
+
+class DefaultKeysValidator extends KeysValidator {
+ // https://docs.solana.com/cli/transfer-tokens#:~:text=The%20public%20key%20is%20a,from%2032%20to%2044%20characters.
+ static const minSolanaKeyLength = 32;
+ static const maxSolanaKeyLength = 44;
+
+ @override
+ InputError? validateKey(String key) {
+ if (key.length < minSolanaKeyLength || key.length > maxSolanaKeyLength) {
+ return KeyLengthInvalid();
+ } else {
+ try {
+ base58decode(key);
+ return null;
+ } on FormatException catch (_) {
+ return KeyNotBase58Encoded();
+ }
+ }
+ }
+}
diff --git a/pubspec.lock b/pubspec.lock
index 903bafd..b0a9240 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -33,6 +33,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.11.0"
+ bip39:
+ dependency: transitive
+ description:
+ name: bip39
+ sha256: de1ee27ebe7d96b84bb3a04a4132a0a3007dcdd5ad27dd14aa87a29d97c45edc
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.6"
bloc:
dependency: transitive
description:
@@ -49,6 +57,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
+ borsh_annotation:
+ dependency: transitive
+ description:
+ name: borsh_annotation
+ sha256: "8c2cc353cb99a12b6c4f9c69e3640d2e18f5127628391658b9fceb96d4fec4d6"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.3.1+4"
characters:
dependency: transitive
description:
@@ -97,6 +113,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.3"
+ cryptography:
+ dependency: transitive
+ description:
+ name: cryptography
+ sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.5.0"
cupertino_icons:
dependency: "direct main"
description:
@@ -105,6 +129,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.5"
+ decimal:
+ dependency: transitive
+ description:
+ name: decimal
+ sha256: "24a261d5d5c87e86c7651c417a5dbdf8bcd7080dd592533910e8d0505a279f21"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.3.3"
+ ed25519_hd_key:
+ dependency: transitive
+ description:
+ name: ed25519_hd_key
+ sha256: c5c9f11a03f5789bf9dcd9ae88d641571c802640851f1cacdb13123f171b3a26
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.1"
equatable:
dependency: "direct main"
description:
@@ -155,6 +195,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
+ freezed_annotation:
+ dependency: transitive
+ description:
+ name: freezed_annotation
+ sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.1"
frontend_server_client:
dependency: transitive
description:
@@ -171,6 +219,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
+ hex:
+ dependency: transitive
+ description:
+ name: hex
+ sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.2.0"
+ http:
+ dependency: transitive
+ description:
+ name: http
+ sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.13.6"
http_multi_server:
dependency: transitive
description:
@@ -203,6 +267,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.6.7"
+ json_annotation:
+ dependency: transitive
+ description:
+ name: json_annotation
+ sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.8.1"
lints:
dependency: transitive
description:
@@ -283,6 +355,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.8.3"
+ pinenacl:
+ dependency: transitive
+ description:
+ name: pinenacl
+ sha256: "3a5503637587d635647c93ea9a8fecf48a420cc7deebe6f1fc85c2a5637ab327"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.5.1"
+ pointycastle:
+ dependency: transitive
+ description:
+ name: pointycastle
+ sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.7.3"
pool:
dependency: transitive
description:
@@ -323,6 +411,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.1"
+ rational:
+ dependency: transitive
+ description:
+ name: rational
+ sha256: ba58e9e18df9abde280e8b10051e4bce85091e41e8e7e411b6cde2e738d357cf
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.2"
shelf:
dependency: transitive
description:
@@ -360,6 +456,14 @@ packages:
description: flutter
source: sdk
version: "0.0.99"
+ solana:
+ dependency: "direct main"
+ description:
+ name: solana
+ sha256: "24f6e87a28035ce7ce54a488f7476432fc5f9ffccd70547f514a2ec9c6251c87"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.30.0"
source_map_stack_trace:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 97068b4..5567ddc 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -17,6 +17,7 @@ dependencies:
equatable: ^2.0.5
cupertino_icons: ^1.0.2
pretty_qr_code: ^2.0.3
+ solana: ^0.30.0
dev_dependencies:
flutter_test: