feat(useragent): initial connection impl
This commit is contained in:
@@ -16,5 +16,5 @@ sources = ['protobufs/*.proto']
|
|||||||
outputs = ['useragent/lib/proto/*']
|
outputs = ['useragent/lib/proto/*']
|
||||||
run = '''
|
run = '''
|
||||||
dart pub global activate protoc_plugin && \
|
dart pub global activate protoc_plugin && \
|
||||||
protoc --dart_out=useragent/lib/proto --proto_path=protobufs/ protobufs/*.proto
|
protoc --dart_out=grpc:useragent/lib/proto --proto_path=protobufs/ protobufs/*.proto
|
||||||
'''
|
'''
|
||||||
@@ -1,3 +1,132 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:arbiter/features/connection/server_info_storage.dart';
|
||||||
|
import 'package:arbiter/features/identity/pk_manager.dart';
|
||||||
|
import 'package:arbiter/proto/arbiter.pbgrpc.dart';
|
||||||
|
import 'package:arbiter/proto/user_agent.pb.dart';
|
||||||
|
import 'package:grpc/grpc.dart';
|
||||||
|
import 'package:mtcore/markettakers.dart';
|
||||||
|
|
||||||
|
class Connection {
|
||||||
|
final ClientChannel channel;
|
||||||
|
final StreamController<UserAgentRequest> _tx;
|
||||||
|
final StreamIterator<UserAgentResponse> _rx;
|
||||||
|
|
||||||
|
Connection({
|
||||||
|
required this.channel,
|
||||||
|
required StreamController<UserAgentRequest> tx,
|
||||||
|
required ResponseStream<UserAgentResponse> rx,
|
||||||
|
}) : _tx = tx,
|
||||||
|
_rx = StreamIterator(rx);
|
||||||
|
|
||||||
|
Future<void> send(UserAgentRequest request) async {
|
||||||
|
_tx.add(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<UserAgentResponse> receive() async {
|
||||||
|
await _rx.moveNext();
|
||||||
|
return _rx.current;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> close() async {
|
||||||
|
await _tx.close();
|
||||||
|
await channel.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Connection> _connect(StoredServerInfo serverInfo) async {
|
||||||
|
final channel = ClientChannel(
|
||||||
|
serverInfo.address,
|
||||||
|
port: serverInfo.port,
|
||||||
|
options: ChannelOptions(
|
||||||
|
connectTimeout: const Duration(seconds: 10),
|
||||||
|
credentials: ChannelCredentials.secure(
|
||||||
|
onBadCertificate: (cert, host) {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final client = ArbiterServiceClient(channel);
|
||||||
|
final tx = StreamController<UserAgentRequest>();
|
||||||
|
|
||||||
|
final rx = client.userAgent(tx.stream);
|
||||||
|
|
||||||
|
return Connection(channel: channel, tx: tx, rx: rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<int> formatChallenge(AuthChallenge challenge, List<int> pubkey) {
|
||||||
|
final encodedPubkey = base64Encode(pubkey);
|
||||||
|
final payload = "${challenge.nonce}:$encodedPubkey";
|
||||||
|
return utf8.encode(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Connection> connectAndAuthorize(
|
||||||
|
StoredServerInfo serverInfo,
|
||||||
|
KeyHandle key, {
|
||||||
|
String? bootstrapToken,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
final connection = await _connect(serverInfo);
|
||||||
|
talker.info(
|
||||||
|
'Connected to server at ${serverInfo.address}:${serverInfo.port}',
|
||||||
|
);
|
||||||
|
final pubkey = await key.getPublicKey();
|
||||||
|
|
||||||
|
final req = AuthChallengeRequest(
|
||||||
|
pubkey: pubkey,
|
||||||
|
bootstrapToken: bootstrapToken,
|
||||||
|
keyType: switch (key.alg) {
|
||||||
|
KeyAlgorithm.rsa => KeyType.KEY_TYPE_RSA,
|
||||||
|
KeyAlgorithm.ecdsa => KeyType.KEY_TYPE_ECDSA_SECP256K1,
|
||||||
|
KeyAlgorithm.ed25519 => KeyType.KEY_TYPE_ED25519,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
await connection.send(UserAgentRequest(authChallengeRequest: req));
|
||||||
|
talker.info(
|
||||||
|
"Sent auth challenge request with pubkey ${base64Encode(pubkey)}",
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
class Connection {}
|
final response = await connection.receive();
|
||||||
|
|
||||||
|
talker.info(
|
||||||
|
'Received response from server, checking for auth challenge...',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.hasAuthChallenge()) {
|
||||||
|
throw Exception(
|
||||||
|
'Expected AuthChallengeResponse, got ${response.whichPayload()}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final challenge = formatChallenge(response.authChallenge, pubkey);
|
||||||
|
talker.info(
|
||||||
|
'Received auth challenge, signing with key ${base64Encode(pubkey)}',
|
||||||
|
);
|
||||||
|
|
||||||
|
final signature = await key.sign(challenge);
|
||||||
|
|
||||||
|
final solutionReq = AuthChallengeSolution(signature: signature);
|
||||||
|
await connection.send(UserAgentRequest(authChallengeSolution: solutionReq));
|
||||||
|
|
||||||
|
talker.info('Sent auth challenge solution, waiting for server response...');
|
||||||
|
|
||||||
|
final solutionResponse = await connection.receive();
|
||||||
|
|
||||||
|
if (!solutionResponse.hasAuthOk()) {
|
||||||
|
throw Exception(
|
||||||
|
'Expected AuthChallengeSolutionResponse, got ${solutionResponse.whichPayload()}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
talker.info('Authentication successful, connection established');
|
||||||
|
|
||||||
|
return connection;
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception('Failed to connect to server: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'server_info_storage.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class StoredServerInfo {
|
class StoredServerInfo {
|
||||||
const StoredServerInfo({
|
const StoredServerInfo({
|
||||||
required this.address,
|
required this.address,
|
||||||
@@ -13,19 +17,9 @@ class StoredServerInfo {
|
|||||||
final int port;
|
final int port;
|
||||||
final String caCertFingerprint;
|
final String caCertFingerprint;
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
factory StoredServerInfo.fromJson(Map<String, dynamic> json) => _$StoredServerInfoFromJson(json);
|
||||||
'address': address,
|
Map<String, dynamic> toJson() => _$StoredServerInfoToJson(this);
|
||||||
'port': port,
|
|
||||||
'caCertFingerprint': caCertFingerprint,
|
|
||||||
};
|
|
||||||
|
|
||||||
factory StoredServerInfo.fromJson(Map<String, dynamic> json) {
|
|
||||||
return StoredServerInfo(
|
|
||||||
address: json['address'] as String,
|
|
||||||
port: json['port'] as int,
|
|
||||||
caCertFingerprint: json['caCertFingerprint'] as String,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class ServerInfoStorage {
|
abstract class ServerInfoStorage {
|
||||||
21
useragent/lib/features/connection/server_info_storage.g.dart
Normal file
21
useragent/lib/features/connection/server_info_storage.g.dart
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'server_info_storage.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
StoredServerInfo _$StoredServerInfoFromJson(Map<String, dynamic> json) =>
|
||||||
|
StoredServerInfo(
|
||||||
|
address: json['address'] as String,
|
||||||
|
port: (json['port'] as num).toInt(),
|
||||||
|
caCertFingerprint: json['caCertFingerprint'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$StoredServerInfoToJson(StoredServerInfo instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'address': instance.address,
|
||||||
|
'port': instance.port,
|
||||||
|
'caCertFingerprint': instance.caCertFingerprint,
|
||||||
|
};
|
||||||
@@ -2,7 +2,7 @@ import 'dart:convert';
|
|||||||
|
|
||||||
import 'package:cryptography/cryptography.dart';
|
import 'package:cryptography/cryptography.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:arbiter/features/pk_manager.dart';
|
import 'package:arbiter/features/identity/pk_manager.dart';
|
||||||
|
|
||||||
final storage = FlutterSecureStorage(
|
final storage = FlutterSecureStorage(
|
||||||
aOptions: AndroidOptions.biometric(
|
aOptions: AndroidOptions.biometric(
|
||||||
@@ -16,7 +16,6 @@ final storage = FlutterSecureStorage(
|
|||||||
synchronizable: false,
|
synchronizable: false,
|
||||||
accessControlFlags: [
|
accessControlFlags: [
|
||||||
AccessControlFlag.userPresence,
|
AccessControlFlag.userPresence,
|
||||||
AccessControlFlag.privateKeyUsage,
|
|
||||||
],
|
],
|
||||||
usesDataProtectionKeychain: true,
|
usesDataProtectionKeychain: true,
|
||||||
),
|
),
|
||||||
@@ -10,14 +10,10 @@
|
|||||||
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
||||||
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
|
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
|
||||||
|
|
||||||
import 'dart:async' as $async;
|
|
||||||
import 'dart:core' as $core;
|
import 'dart:core' as $core;
|
||||||
|
|
||||||
import 'package:protobuf/protobuf.dart' as $pb;
|
import 'package:protobuf/protobuf.dart' as $pb;
|
||||||
|
|
||||||
import 'client.pb.dart' as $0;
|
|
||||||
import 'user_agent.pb.dart' as $1;
|
|
||||||
|
|
||||||
export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions;
|
export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions;
|
||||||
|
|
||||||
class ServerInfo extends $pb.GeneratedMessage {
|
class ServerInfo extends $pb.GeneratedMessage {
|
||||||
@@ -86,21 +82,6 @@ class ServerInfo extends $pb.GeneratedMessage {
|
|||||||
void clearCertPublicKey() => $_clearField(2);
|
void clearCertPublicKey() => $_clearField(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArbiterServiceApi {
|
|
||||||
final $pb.RpcClient _client;
|
|
||||||
|
|
||||||
ArbiterServiceApi(this._client);
|
|
||||||
|
|
||||||
$async.Future<$0.ClientResponse> client(
|
|
||||||
$pb.ClientContext? ctx, $0.ClientRequest request) =>
|
|
||||||
_client.invoke<$0.ClientResponse>(
|
|
||||||
ctx, 'ArbiterService', 'Client', request, $0.ClientResponse());
|
|
||||||
$async.Future<$1.UserAgentResponse> userAgent(
|
|
||||||
$pb.ClientContext? ctx, $1.UserAgentRequest request) =>
|
|
||||||
_client.invoke<$1.UserAgentResponse>(
|
|
||||||
ctx, 'ArbiterService', 'UserAgent', request, $1.UserAgentResponse());
|
|
||||||
}
|
|
||||||
|
|
||||||
const $core.bool _omitFieldNames =
|
const $core.bool _omitFieldNames =
|
||||||
$core.bool.fromEnvironment('protobuf.omit_field_names');
|
$core.bool.fromEnvironment('protobuf.omit_field_names');
|
||||||
const $core.bool _omitMessageNames =
|
const $core.bool _omitMessageNames =
|
||||||
|
|||||||
90
useragent/lib/proto/arbiter.pbgrpc.dart
Normal file
90
useragent/lib/proto/arbiter.pbgrpc.dart
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
// This is a generated file - do not edit.
|
||||||
|
//
|
||||||
|
// Generated from arbiter.proto.
|
||||||
|
|
||||||
|
// @dart = 3.3
|
||||||
|
|
||||||
|
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
// ignore_for_file: curly_braces_in_flow_control_structures
|
||||||
|
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
||||||
|
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
|
||||||
|
|
||||||
|
import 'dart:async' as $async;
|
||||||
|
import 'dart:core' as $core;
|
||||||
|
|
||||||
|
import 'package:grpc/service_api.dart' as $grpc;
|
||||||
|
import 'package:protobuf/protobuf.dart' as $pb;
|
||||||
|
|
||||||
|
import 'client.pb.dart' as $0;
|
||||||
|
import 'user_agent.pb.dart' as $1;
|
||||||
|
|
||||||
|
export 'arbiter.pb.dart';
|
||||||
|
|
||||||
|
@$pb.GrpcServiceName('arbiter.ArbiterService')
|
||||||
|
class ArbiterServiceClient extends $grpc.Client {
|
||||||
|
/// The hostname for this service.
|
||||||
|
static const $core.String defaultHost = '';
|
||||||
|
|
||||||
|
/// OAuth scopes needed for the client.
|
||||||
|
static const $core.List<$core.String> oauthScopes = [
|
||||||
|
'',
|
||||||
|
];
|
||||||
|
|
||||||
|
ArbiterServiceClient(super.channel, {super.options, super.interceptors});
|
||||||
|
|
||||||
|
$grpc.ResponseStream<$0.ClientResponse> client(
|
||||||
|
$async.Stream<$0.ClientRequest> request, {
|
||||||
|
$grpc.CallOptions? options,
|
||||||
|
}) {
|
||||||
|
return $createStreamingCall(_$client, request, options: options);
|
||||||
|
}
|
||||||
|
|
||||||
|
$grpc.ResponseStream<$1.UserAgentResponse> userAgent(
|
||||||
|
$async.Stream<$1.UserAgentRequest> request, {
|
||||||
|
$grpc.CallOptions? options,
|
||||||
|
}) {
|
||||||
|
return $createStreamingCall(_$userAgent, request, options: options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// method descriptors
|
||||||
|
|
||||||
|
static final _$client =
|
||||||
|
$grpc.ClientMethod<$0.ClientRequest, $0.ClientResponse>(
|
||||||
|
'/arbiter.ArbiterService/Client',
|
||||||
|
($0.ClientRequest value) => value.writeToBuffer(),
|
||||||
|
$0.ClientResponse.fromBuffer);
|
||||||
|
static final _$userAgent =
|
||||||
|
$grpc.ClientMethod<$1.UserAgentRequest, $1.UserAgentResponse>(
|
||||||
|
'/arbiter.ArbiterService/UserAgent',
|
||||||
|
($1.UserAgentRequest value) => value.writeToBuffer(),
|
||||||
|
$1.UserAgentResponse.fromBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@$pb.GrpcServiceName('arbiter.ArbiterService')
|
||||||
|
abstract class ArbiterServiceBase extends $grpc.Service {
|
||||||
|
$core.String get $name => 'arbiter.ArbiterService';
|
||||||
|
|
||||||
|
ArbiterServiceBase() {
|
||||||
|
$addMethod($grpc.ServiceMethod<$0.ClientRequest, $0.ClientResponse>(
|
||||||
|
'Client',
|
||||||
|
client,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
($core.List<$core.int> value) => $0.ClientRequest.fromBuffer(value),
|
||||||
|
($0.ClientResponse value) => value.writeToBuffer()));
|
||||||
|
$addMethod($grpc.ServiceMethod<$1.UserAgentRequest, $1.UserAgentResponse>(
|
||||||
|
'UserAgent',
|
||||||
|
userAgent,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
($core.List<$core.int> value) => $1.UserAgentRequest.fromBuffer(value),
|
||||||
|
($1.UserAgentResponse value) => value.writeToBuffer()));
|
||||||
|
}
|
||||||
|
|
||||||
|
$async.Stream<$0.ClientResponse> client(
|
||||||
|
$grpc.ServiceCall call, $async.Stream<$0.ClientRequest> request);
|
||||||
|
|
||||||
|
$async.Stream<$1.UserAgentResponse> userAgent(
|
||||||
|
$grpc.ServiceCall call, $async.Stream<$1.UserAgentRequest> request);
|
||||||
|
}
|
||||||
@@ -15,15 +15,6 @@ import 'dart:convert' as $convert;
|
|||||||
import 'dart:core' as $core;
|
import 'dart:core' as $core;
|
||||||
import 'dart:typed_data' as $typed_data;
|
import 'dart:typed_data' as $typed_data;
|
||||||
|
|
||||||
import 'package:protobuf/well_known_types/google/protobuf/empty.pbjson.dart'
|
|
||||||
as $3;
|
|
||||||
import 'package:protobuf/well_known_types/google/protobuf/timestamp.pbjson.dart'
|
|
||||||
as $4;
|
|
||||||
|
|
||||||
import 'client.pbjson.dart' as $0;
|
|
||||||
import 'evm.pbjson.dart' as $2;
|
|
||||||
import 'user_agent.pbjson.dart' as $1;
|
|
||||||
|
|
||||||
@$core.Deprecated('Use serverInfoDescriptor instead')
|
@$core.Deprecated('Use serverInfoDescriptor instead')
|
||||||
const ServerInfo$json = {
|
const ServerInfo$json = {
|
||||||
'1': 'ServerInfo',
|
'1': 'ServerInfo',
|
||||||
@@ -37,88 +28,3 @@ const ServerInfo$json = {
|
|||||||
final $typed_data.Uint8List serverInfoDescriptor = $convert.base64Decode(
|
final $typed_data.Uint8List serverInfoDescriptor = $convert.base64Decode(
|
||||||
'CgpTZXJ2ZXJJbmZvEhgKB3ZlcnNpb24YASABKAlSB3ZlcnNpb24SJgoPY2VydF9wdWJsaWNfa2'
|
'CgpTZXJ2ZXJJbmZvEhgKB3ZlcnNpb24YASABKAlSB3ZlcnNpb24SJgoPY2VydF9wdWJsaWNfa2'
|
||||||
'V5GAIgASgMUg1jZXJ0UHVibGljS2V5');
|
'V5GAIgASgMUg1jZXJ0UHVibGljS2V5');
|
||||||
|
|
||||||
const $core.Map<$core.String, $core.dynamic> ArbiterServiceBase$json = {
|
|
||||||
'1': 'ArbiterService',
|
|
||||||
'2': [
|
|
||||||
{
|
|
||||||
'1': 'Client',
|
|
||||||
'2': '.arbiter.client.ClientRequest',
|
|
||||||
'3': '.arbiter.client.ClientResponse',
|
|
||||||
'5': true,
|
|
||||||
'6': true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'1': 'UserAgent',
|
|
||||||
'2': '.arbiter.user_agent.UserAgentRequest',
|
|
||||||
'3': '.arbiter.user_agent.UserAgentResponse',
|
|
||||||
'5': true,
|
|
||||||
'6': true
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
@$core.Deprecated('Use arbiterServiceDescriptor instead')
|
|
||||||
const $core.Map<$core.String, $core.Map<$core.String, $core.dynamic>>
|
|
||||||
ArbiterServiceBase$messageJson = {
|
|
||||||
'.arbiter.client.ClientRequest': $0.ClientRequest$json,
|
|
||||||
'.arbiter.client.AuthChallengeRequest': $0.AuthChallengeRequest$json,
|
|
||||||
'.arbiter.client.AuthChallengeSolution': $0.AuthChallengeSolution$json,
|
|
||||||
'.arbiter.client.ClientResponse': $0.ClientResponse$json,
|
|
||||||
'.arbiter.client.AuthChallenge': $0.AuthChallenge$json,
|
|
||||||
'.arbiter.client.AuthOk': $0.AuthOk$json,
|
|
||||||
'.arbiter.evm.EvmSignTransactionResponse': $2.EvmSignTransactionResponse$json,
|
|
||||||
'.arbiter.evm.TransactionEvalError': $2.TransactionEvalError$json,
|
|
||||||
'.google.protobuf.Empty': $3.Empty$json,
|
|
||||||
'.arbiter.evm.NoMatchingGrantError': $2.NoMatchingGrantError$json,
|
|
||||||
'.arbiter.evm.SpecificMeaning': $2.SpecificMeaning$json,
|
|
||||||
'.arbiter.evm.EtherTransferMeaning': $2.EtherTransferMeaning$json,
|
|
||||||
'.arbiter.evm.TokenTransferMeaning': $2.TokenTransferMeaning$json,
|
|
||||||
'.arbiter.evm.TokenInfo': $2.TokenInfo$json,
|
|
||||||
'.arbiter.evm.PolicyViolationsError': $2.PolicyViolationsError$json,
|
|
||||||
'.arbiter.evm.EvalViolation': $2.EvalViolation$json,
|
|
||||||
'.arbiter.evm.GasLimitExceededViolation': $2.GasLimitExceededViolation$json,
|
|
||||||
'.arbiter.evm.EvmAnalyzeTransactionResponse':
|
|
||||||
$2.EvmAnalyzeTransactionResponse$json,
|
|
||||||
'.arbiter.client.ClientConnectError': $0.ClientConnectError$json,
|
|
||||||
'.arbiter.user_agent.UserAgentRequest': $1.UserAgentRequest$json,
|
|
||||||
'.arbiter.user_agent.AuthChallengeRequest': $1.AuthChallengeRequest$json,
|
|
||||||
'.arbiter.user_agent.AuthChallengeSolution': $1.AuthChallengeSolution$json,
|
|
||||||
'.arbiter.user_agent.UnsealStart': $1.UnsealStart$json,
|
|
||||||
'.arbiter.user_agent.UnsealEncryptedKey': $1.UnsealEncryptedKey$json,
|
|
||||||
'.arbiter.evm.EvmGrantCreateRequest': $2.EvmGrantCreateRequest$json,
|
|
||||||
'.arbiter.evm.SharedSettings': $2.SharedSettings$json,
|
|
||||||
'.google.protobuf.Timestamp': $4.Timestamp$json,
|
|
||||||
'.arbiter.evm.TransactionRateLimit': $2.TransactionRateLimit$json,
|
|
||||||
'.arbiter.evm.SpecificGrant': $2.SpecificGrant$json,
|
|
||||||
'.arbiter.evm.EtherTransferSettings': $2.EtherTransferSettings$json,
|
|
||||||
'.arbiter.evm.VolumeRateLimit': $2.VolumeRateLimit$json,
|
|
||||||
'.arbiter.evm.TokenTransferSettings': $2.TokenTransferSettings$json,
|
|
||||||
'.arbiter.evm.EvmGrantDeleteRequest': $2.EvmGrantDeleteRequest$json,
|
|
||||||
'.arbiter.evm.EvmGrantListRequest': $2.EvmGrantListRequest$json,
|
|
||||||
'.arbiter.user_agent.ClientConnectionResponse':
|
|
||||||
$1.ClientConnectionResponse$json,
|
|
||||||
'.arbiter.user_agent.UserAgentResponse': $1.UserAgentResponse$json,
|
|
||||||
'.arbiter.user_agent.AuthChallenge': $1.AuthChallenge$json,
|
|
||||||
'.arbiter.user_agent.AuthOk': $1.AuthOk$json,
|
|
||||||
'.arbiter.user_agent.UnsealStartResponse': $1.UnsealStartResponse$json,
|
|
||||||
'.arbiter.evm.WalletCreateResponse': $2.WalletCreateResponse$json,
|
|
||||||
'.arbiter.evm.WalletEntry': $2.WalletEntry$json,
|
|
||||||
'.arbiter.evm.WalletListResponse': $2.WalletListResponse$json,
|
|
||||||
'.arbiter.evm.WalletList': $2.WalletList$json,
|
|
||||||
'.arbiter.evm.EvmGrantCreateResponse': $2.EvmGrantCreateResponse$json,
|
|
||||||
'.arbiter.evm.EvmGrantDeleteResponse': $2.EvmGrantDeleteResponse$json,
|
|
||||||
'.arbiter.evm.EvmGrantListResponse': $2.EvmGrantListResponse$json,
|
|
||||||
'.arbiter.evm.EvmGrantList': $2.EvmGrantList$json,
|
|
||||||
'.arbiter.evm.GrantEntry': $2.GrantEntry$json,
|
|
||||||
'.arbiter.user_agent.ClientConnectionRequest':
|
|
||||||
$1.ClientConnectionRequest$json,
|
|
||||||
'.arbiter.user_agent.ClientConnectionCancel': $1.ClientConnectionCancel$json,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Descriptor for `ArbiterService`. Decode as a `google.protobuf.ServiceDescriptorProto`.
|
|
||||||
final $typed_data.Uint8List arbiterServiceDescriptor = $convert.base64Decode(
|
|
||||||
'Cg5BcmJpdGVyU2VydmljZRJLCgZDbGllbnQSHS5hcmJpdGVyLmNsaWVudC5DbGllbnRSZXF1ZX'
|
|
||||||
'N0Gh4uYXJiaXRlci5jbGllbnQuQ2xpZW50UmVzcG9uc2UoATABElwKCVVzZXJBZ2VudBIkLmFy'
|
|
||||||
'Yml0ZXIudXNlcl9hZ2VudC5Vc2VyQWdlbnRSZXF1ZXN0GiUuYXJiaXRlci51c2VyX2FnZW50Ll'
|
|
||||||
'VzZXJBZ2VudFJlc3BvbnNlKAEwAQ==');
|
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
// This is a generated file - do not edit.
|
|
||||||
//
|
|
||||||
// Generated from arbiter.proto.
|
|
||||||
|
|
||||||
// @dart = 3.3
|
|
||||||
|
|
||||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
|
||||||
// ignore_for_file: constant_identifier_names
|
|
||||||
// ignore_for_file: curly_braces_in_flow_control_structures
|
|
||||||
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
|
||||||
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
|
|
||||||
|
|
||||||
import 'dart:async' as $async;
|
|
||||||
import 'dart:core' as $core;
|
|
||||||
|
|
||||||
import 'package:protobuf/protobuf.dart' as $pb;
|
|
||||||
|
|
||||||
import 'arbiter.pbjson.dart';
|
|
||||||
import 'client.pb.dart' as $0;
|
|
||||||
import 'user_agent.pb.dart' as $1;
|
|
||||||
|
|
||||||
export 'arbiter.pb.dart';
|
|
||||||
|
|
||||||
abstract class ArbiterServiceBase extends $pb.GeneratedService {
|
|
||||||
$async.Future<$0.ClientResponse> client(
|
|
||||||
$pb.ServerContext ctx, $0.ClientRequest request);
|
|
||||||
$async.Future<$1.UserAgentResponse> userAgent(
|
|
||||||
$pb.ServerContext ctx, $1.UserAgentRequest request);
|
|
||||||
|
|
||||||
$pb.GeneratedMessage createRequest($core.String methodName) {
|
|
||||||
switch (methodName) {
|
|
||||||
case 'Client':
|
|
||||||
return $0.ClientRequest();
|
|
||||||
case 'UserAgent':
|
|
||||||
return $1.UserAgentRequest();
|
|
||||||
default:
|
|
||||||
throw $core.ArgumentError('Unknown method: $methodName');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$async.Future<$pb.GeneratedMessage> handleCall($pb.ServerContext ctx,
|
|
||||||
$core.String methodName, $pb.GeneratedMessage request) {
|
|
||||||
switch (methodName) {
|
|
||||||
case 'Client':
|
|
||||||
return client(ctx, request as $0.ClientRequest);
|
|
||||||
case 'UserAgent':
|
|
||||||
return userAgent(ctx, request as $1.UserAgentRequest);
|
|
||||||
default:
|
|
||||||
throw $core.ArgumentError('Unknown method: $methodName');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$core.Map<$core.String, $core.dynamic> get $json => ArbiterServiceBase$json;
|
|
||||||
$core.Map<$core.String, $core.Map<$core.String, $core.dynamic>>
|
|
||||||
get $messageJson => ArbiterServiceBase$messageJson;
|
|
||||||
}
|
|
||||||
@@ -26,10 +26,12 @@ class AuthChallengeRequest extends $pb.GeneratedMessage {
|
|||||||
factory AuthChallengeRequest({
|
factory AuthChallengeRequest({
|
||||||
$core.List<$core.int>? pubkey,
|
$core.List<$core.int>? pubkey,
|
||||||
$core.String? bootstrapToken,
|
$core.String? bootstrapToken,
|
||||||
|
KeyType? keyType,
|
||||||
}) {
|
}) {
|
||||||
final result = create();
|
final result = create();
|
||||||
if (pubkey != null) result.pubkey = pubkey;
|
if (pubkey != null) result.pubkey = pubkey;
|
||||||
if (bootstrapToken != null) result.bootstrapToken = bootstrapToken;
|
if (bootstrapToken != null) result.bootstrapToken = bootstrapToken;
|
||||||
|
if (keyType != null) result.keyType = keyType;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,6 +52,8 @@ class AuthChallengeRequest extends $pb.GeneratedMessage {
|
|||||||
..a<$core.List<$core.int>>(
|
..a<$core.List<$core.int>>(
|
||||||
1, _omitFieldNames ? '' : 'pubkey', $pb.PbFieldType.OY)
|
1, _omitFieldNames ? '' : 'pubkey', $pb.PbFieldType.OY)
|
||||||
..aOS(2, _omitFieldNames ? '' : 'bootstrapToken')
|
..aOS(2, _omitFieldNames ? '' : 'bootstrapToken')
|
||||||
|
..aE<KeyType>(3, _omitFieldNames ? '' : 'keyType',
|
||||||
|
enumValues: KeyType.values)
|
||||||
..hasRequiredFields = false;
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
@@ -88,6 +92,15 @@ class AuthChallengeRequest extends $pb.GeneratedMessage {
|
|||||||
$core.bool hasBootstrapToken() => $_has(1);
|
$core.bool hasBootstrapToken() => $_has(1);
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
void clearBootstrapToken() => $_clearField(2);
|
void clearBootstrapToken() => $_clearField(2);
|
||||||
|
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
KeyType get keyType => $_getN(2);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
set keyType(KeyType value) => $_setField(3, value);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
$core.bool hasKeyType() => $_has(2);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
void clearKeyType() => $_clearField(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
class AuthChallenge extends $pb.GeneratedMessage {
|
class AuthChallenge extends $pb.GeneratedMessage {
|
||||||
|
|||||||
@@ -14,6 +14,31 @@ import 'dart:core' as $core;
|
|||||||
|
|
||||||
import 'package:protobuf/protobuf.dart' as $pb;
|
import 'package:protobuf/protobuf.dart' as $pb;
|
||||||
|
|
||||||
|
class KeyType extends $pb.ProtobufEnum {
|
||||||
|
static const KeyType KEY_TYPE_UNSPECIFIED =
|
||||||
|
KeyType._(0, _omitEnumNames ? '' : 'KEY_TYPE_UNSPECIFIED');
|
||||||
|
static const KeyType KEY_TYPE_ED25519 =
|
||||||
|
KeyType._(1, _omitEnumNames ? '' : 'KEY_TYPE_ED25519');
|
||||||
|
static const KeyType KEY_TYPE_ECDSA_SECP256K1 =
|
||||||
|
KeyType._(2, _omitEnumNames ? '' : 'KEY_TYPE_ECDSA_SECP256K1');
|
||||||
|
static const KeyType KEY_TYPE_RSA =
|
||||||
|
KeyType._(3, _omitEnumNames ? '' : 'KEY_TYPE_RSA');
|
||||||
|
|
||||||
|
static const $core.List<KeyType> values = <KeyType>[
|
||||||
|
KEY_TYPE_UNSPECIFIED,
|
||||||
|
KEY_TYPE_ED25519,
|
||||||
|
KEY_TYPE_ECDSA_SECP256K1,
|
||||||
|
KEY_TYPE_RSA,
|
||||||
|
];
|
||||||
|
|
||||||
|
static final $core.List<KeyType?> _byValue =
|
||||||
|
$pb.ProtobufEnum.$_initByValueList(values, 3);
|
||||||
|
static KeyType? valueOf($core.int value) =>
|
||||||
|
value < 0 || value >= _byValue.length ? null : _byValue[value];
|
||||||
|
|
||||||
|
const KeyType._(super.value, super.name);
|
||||||
|
}
|
||||||
|
|
||||||
class UnsealResult extends $pb.ProtobufEnum {
|
class UnsealResult extends $pb.ProtobufEnum {
|
||||||
static const UnsealResult UNSEAL_RESULT_UNSPECIFIED =
|
static const UnsealResult UNSEAL_RESULT_UNSPECIFIED =
|
||||||
UnsealResult._(0, _omitEnumNames ? '' : 'UNSEAL_RESULT_UNSPECIFIED');
|
UnsealResult._(0, _omitEnumNames ? '' : 'UNSEAL_RESULT_UNSPECIFIED');
|
||||||
|
|||||||
@@ -15,6 +15,22 @@ import 'dart:convert' as $convert;
|
|||||||
import 'dart:core' as $core;
|
import 'dart:core' as $core;
|
||||||
import 'dart:typed_data' as $typed_data;
|
import 'dart:typed_data' as $typed_data;
|
||||||
|
|
||||||
|
@$core.Deprecated('Use keyTypeDescriptor instead')
|
||||||
|
const KeyType$json = {
|
||||||
|
'1': 'KeyType',
|
||||||
|
'2': [
|
||||||
|
{'1': 'KEY_TYPE_UNSPECIFIED', '2': 0},
|
||||||
|
{'1': 'KEY_TYPE_ED25519', '2': 1},
|
||||||
|
{'1': 'KEY_TYPE_ECDSA_SECP256K1', '2': 2},
|
||||||
|
{'1': 'KEY_TYPE_RSA', '2': 3},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Descriptor for `KeyType`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||||
|
final $typed_data.Uint8List keyTypeDescriptor = $convert.base64Decode(
|
||||||
|
'CgdLZXlUeXBlEhgKFEtFWV9UWVBFX1VOU1BFQ0lGSUVEEAASFAoQS0VZX1RZUEVfRUQyNTUxOR'
|
||||||
|
'ABEhwKGEtFWV9UWVBFX0VDRFNBX1NFQ1AyNTZLMRACEhAKDEtFWV9UWVBFX1JTQRAD');
|
||||||
|
|
||||||
@$core.Deprecated('Use unsealResultDescriptor instead')
|
@$core.Deprecated('Use unsealResultDescriptor instead')
|
||||||
const UnsealResult$json = {
|
const UnsealResult$json = {
|
||||||
'1': 'UnsealResult',
|
'1': 'UnsealResult',
|
||||||
@@ -64,6 +80,14 @@ const AuthChallengeRequest$json = {
|
|||||||
'10': 'bootstrapToken',
|
'10': 'bootstrapToken',
|
||||||
'17': true
|
'17': true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'1': 'key_type',
|
||||||
|
'3': 3,
|
||||||
|
'4': 1,
|
||||||
|
'5': 14,
|
||||||
|
'6': '.arbiter.user_agent.KeyType',
|
||||||
|
'10': 'keyType'
|
||||||
|
},
|
||||||
],
|
],
|
||||||
'8': [
|
'8': [
|
||||||
{'1': '_bootstrap_token'},
|
{'1': '_bootstrap_token'},
|
||||||
@@ -73,8 +97,9 @@ const AuthChallengeRequest$json = {
|
|||||||
/// Descriptor for `AuthChallengeRequest`. Decode as a `google.protobuf.DescriptorProto`.
|
/// Descriptor for `AuthChallengeRequest`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
final $typed_data.Uint8List authChallengeRequestDescriptor = $convert.base64Decode(
|
final $typed_data.Uint8List authChallengeRequestDescriptor = $convert.base64Decode(
|
||||||
'ChRBdXRoQ2hhbGxlbmdlUmVxdWVzdBIWCgZwdWJrZXkYASABKAxSBnB1YmtleRIsCg9ib290c3'
|
'ChRBdXRoQ2hhbGxlbmdlUmVxdWVzdBIWCgZwdWJrZXkYASABKAxSBnB1YmtleRIsCg9ib290c3'
|
||||||
'RyYXBfdG9rZW4YAiABKAlIAFIOYm9vdHN0cmFwVG9rZW6IAQFCEgoQX2Jvb3RzdHJhcF90b2tl'
|
'RyYXBfdG9rZW4YAiABKAlIAFIOYm9vdHN0cmFwVG9rZW6IAQESNgoIa2V5X3R5cGUYAyABKA4y'
|
||||||
'bg==');
|
'Gy5hcmJpdGVyLnVzZXJfYWdlbnQuS2V5VHlwZVIHa2V5VHlwZUISChBfYm9vdHN0cmFwX3Rva2'
|
||||||
|
'Vu');
|
||||||
|
|
||||||
@$core.Deprecated('Use authChallengeDescriptor instead')
|
@$core.Deprecated('Use authChallengeDescriptor instead')
|
||||||
const AuthChallenge$json = {
|
const AuthChallenge$json = {
|
||||||
|
|||||||
22
useragent/lib/providers/connection/bootstrap_token.dart
Normal file
22
useragent/lib/providers/connection/bootstrap_token.dart
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
|
part 'bootstrap_token.g.dart';
|
||||||
|
|
||||||
|
@Riverpod(keepAlive: true)
|
||||||
|
class BootstrapToken extends _$BootstrapToken {
|
||||||
|
String? build() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(String token) {
|
||||||
|
state = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? take() {
|
||||||
|
final token = state;
|
||||||
|
state = null;
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
||||||
62
useragent/lib/providers/connection/bootstrap_token.g.dart
Normal file
62
useragent/lib/providers/connection/bootstrap_token.g.dart
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'bootstrap_token.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// RiverpodGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint, type=warning
|
||||||
|
|
||||||
|
@ProviderFor(BootstrapToken)
|
||||||
|
final bootstrapTokenProvider = BootstrapTokenProvider._();
|
||||||
|
|
||||||
|
final class BootstrapTokenProvider
|
||||||
|
extends $NotifierProvider<BootstrapToken, String?> {
|
||||||
|
BootstrapTokenProvider._()
|
||||||
|
: super(
|
||||||
|
from: null,
|
||||||
|
argument: null,
|
||||||
|
retry: null,
|
||||||
|
name: r'bootstrapTokenProvider',
|
||||||
|
isAutoDispose: false,
|
||||||
|
dependencies: null,
|
||||||
|
$allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String debugGetCreateSourceHash() => _$bootstrapTokenHash();
|
||||||
|
|
||||||
|
@$internal
|
||||||
|
@override
|
||||||
|
BootstrapToken create() => BootstrapToken();
|
||||||
|
|
||||||
|
/// {@macro riverpod.override_with_value}
|
||||||
|
Override overrideWithValue(String? value) {
|
||||||
|
return $ProviderOverride(
|
||||||
|
origin: this,
|
||||||
|
providerOverride: $SyncValueProvider<String?>(value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _$bootstrapTokenHash() => r'a59e679ab0561ed2ab4148660499891571d439db';
|
||||||
|
|
||||||
|
abstract class _$BootstrapToken extends $Notifier<String?> {
|
||||||
|
String? build();
|
||||||
|
@$mustCallSuper
|
||||||
|
@override
|
||||||
|
void runBuild() {
|
||||||
|
final ref = this.ref as $Ref<String?, String?>;
|
||||||
|
final element =
|
||||||
|
ref.element
|
||||||
|
as $ClassProviderElement<
|
||||||
|
AnyNotifier<String?, String?>,
|
||||||
|
String?,
|
||||||
|
Object?,
|
||||||
|
Object?
|
||||||
|
>;
|
||||||
|
element.handleCreate(ref, build);
|
||||||
|
}
|
||||||
|
}
|
||||||
39
useragent/lib/providers/connection/connection_manager.dart
Normal file
39
useragent/lib/providers/connection/connection_manager.dart
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import 'package:arbiter/features/connection/connection.dart';
|
||||||
|
import 'package:arbiter/providers/connection/bootstrap_token.dart';
|
||||||
|
import 'package:arbiter/providers/key.dart';
|
||||||
|
import 'package:arbiter/providers/server_info.dart';
|
||||||
|
import 'package:mtcore/markettakers.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
|
part 'connection_manager.g.dart';
|
||||||
|
|
||||||
|
@Riverpod(keepAlive: true)
|
||||||
|
class ConnectionManager extends _$ConnectionManager {
|
||||||
|
@override
|
||||||
|
Future<Connection?> build() async {
|
||||||
|
final serverInfo = await ref.watch(serverInfoProvider.future);
|
||||||
|
final key = await ref.watch(keyProvider.future);
|
||||||
|
final token = ref.watch(bootstrapTokenProvider);
|
||||||
|
|
||||||
|
if (serverInfo == null || key == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final Connection connection;
|
||||||
|
try {
|
||||||
|
connection = await connectAndAuthorize(serverInfo, key, bootstrapToken: token);
|
||||||
|
} catch (e) {
|
||||||
|
talker.handle(e);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ref.onDispose(() {
|
||||||
|
final connection = state.asData?.value;
|
||||||
|
if (connection != null) {
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
}
|
||||||
54
useragent/lib/providers/connection/connection_manager.g.dart
Normal file
54
useragent/lib/providers/connection/connection_manager.g.dart
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'connection_manager.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// RiverpodGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint, type=warning
|
||||||
|
|
||||||
|
@ProviderFor(ConnectionManager)
|
||||||
|
final connectionManagerProvider = ConnectionManagerProvider._();
|
||||||
|
|
||||||
|
final class ConnectionManagerProvider
|
||||||
|
extends $AsyncNotifierProvider<ConnectionManager, Connection?> {
|
||||||
|
ConnectionManagerProvider._()
|
||||||
|
: super(
|
||||||
|
from: null,
|
||||||
|
argument: null,
|
||||||
|
retry: null,
|
||||||
|
name: r'connectionManagerProvider',
|
||||||
|
isAutoDispose: false,
|
||||||
|
dependencies: null,
|
||||||
|
$allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String debugGetCreateSourceHash() => _$connectionManagerHash();
|
||||||
|
|
||||||
|
@$internal
|
||||||
|
@override
|
||||||
|
ConnectionManager create() => ConnectionManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
String _$connectionManagerHash() => r'8923346dff75a9a06127c71a0a39ca65d9733d8c';
|
||||||
|
|
||||||
|
abstract class _$ConnectionManager extends $AsyncNotifier<Connection?> {
|
||||||
|
FutureOr<Connection?> build();
|
||||||
|
@$mustCallSuper
|
||||||
|
@override
|
||||||
|
void runBuild() {
|
||||||
|
final ref = this.ref as $Ref<AsyncValue<Connection?>, Connection?>;
|
||||||
|
final element =
|
||||||
|
ref.element
|
||||||
|
as $ClassProviderElement<
|
||||||
|
AnyNotifier<AsyncValue<Connection?>, Connection?>,
|
||||||
|
AsyncValue<Connection?>,
|
||||||
|
Object?,
|
||||||
|
Object?
|
||||||
|
>;
|
||||||
|
element.handleCreate(ref, build);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:mtcore/markettakers.dart';
|
import 'package:mtcore/markettakers.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:arbiter/features/pk_manager.dart';
|
import 'package:arbiter/features/identity/pk_manager.dart';
|
||||||
import 'package:arbiter/features/simple_ed25519.dart';
|
import 'package:arbiter/features/identity/simple_ed25519.dart';
|
||||||
|
|
||||||
part 'key.g.dart';
|
part 'key.g.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:arbiter/features/server_info_storage.dart';
|
import 'package:arbiter/features/connection/server_info_storage.dart';
|
||||||
import 'package:cryptography/cryptography.dart';
|
import 'package:cryptography/cryptography.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class Router extends RootStackRouter {
|
|||||||
List<AutoRoute> get routes => [
|
List<AutoRoute> get routes => [
|
||||||
AutoRoute(page: Bootstrap.page, path: '/bootstrap', initial: true),
|
AutoRoute(page: Bootstrap.page, path: '/bootstrap', initial: true),
|
||||||
AutoRoute(page: ServerInfoSetupRoute.page, path: '/server-info'),
|
AutoRoute(page: ServerInfoSetupRoute.page, path: '/server-info'),
|
||||||
|
AutoRoute(page: ServerConnectionRoute.page, path: '/server-connection'),
|
||||||
|
|
||||||
AutoRoute(
|
AutoRoute(
|
||||||
page: DashboardRouter.page,
|
page: DashboardRouter.page,
|
||||||
|
|||||||
@@ -13,18 +13,20 @@ import 'package:arbiter/screens/bootstrap.dart' as _i2;
|
|||||||
import 'package:arbiter/screens/dashboard.dart' as _i4;
|
import 'package:arbiter/screens/dashboard.dart' as _i4;
|
||||||
import 'package:arbiter/screens/dashboard/about.dart' as _i1;
|
import 'package:arbiter/screens/dashboard/about.dart' as _i1;
|
||||||
import 'package:arbiter/screens/dashboard/calc.dart' as _i3;
|
import 'package:arbiter/screens/dashboard/calc.dart' as _i3;
|
||||||
import 'package:arbiter/screens/server_info_setup.dart' as _i5;
|
import 'package:arbiter/screens/server_connection.dart' as _i5;
|
||||||
import 'package:auto_route/auto_route.dart' as _i6;
|
import 'package:arbiter/screens/server_info_setup.dart' as _i6;
|
||||||
|
import 'package:auto_route/auto_route.dart' as _i7;
|
||||||
|
import 'package:flutter/material.dart' as _i8;
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i1.AboutScreen]
|
/// [_i1.AboutScreen]
|
||||||
class AboutRoute extends _i6.PageRouteInfo<void> {
|
class AboutRoute extends _i7.PageRouteInfo<void> {
|
||||||
const AboutRoute({List<_i6.PageRouteInfo>? children})
|
const AboutRoute({List<_i7.PageRouteInfo>? children})
|
||||||
: super(AboutRoute.name, initialChildren: children);
|
: super(AboutRoute.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'AboutRoute';
|
static const String name = 'AboutRoute';
|
||||||
|
|
||||||
static _i6.PageInfo page = _i6.PageInfo(
|
static _i7.PageInfo page = _i7.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i1.AboutScreen();
|
return const _i1.AboutScreen();
|
||||||
@@ -34,13 +36,13 @@ class AboutRoute extends _i6.PageRouteInfo<void> {
|
|||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i2.Bootstrap]
|
/// [_i2.Bootstrap]
|
||||||
class Bootstrap extends _i6.PageRouteInfo<void> {
|
class Bootstrap extends _i7.PageRouteInfo<void> {
|
||||||
const Bootstrap({List<_i6.PageRouteInfo>? children})
|
const Bootstrap({List<_i7.PageRouteInfo>? children})
|
||||||
: super(Bootstrap.name, initialChildren: children);
|
: super(Bootstrap.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'Bootstrap';
|
static const String name = 'Bootstrap';
|
||||||
|
|
||||||
static _i6.PageInfo page = _i6.PageInfo(
|
static _i7.PageInfo page = _i7.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i2.Bootstrap();
|
return const _i2.Bootstrap();
|
||||||
@@ -50,13 +52,13 @@ class Bootstrap extends _i6.PageRouteInfo<void> {
|
|||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i3.CalcScreen]
|
/// [_i3.CalcScreen]
|
||||||
class CalcRoute extends _i6.PageRouteInfo<void> {
|
class CalcRoute extends _i7.PageRouteInfo<void> {
|
||||||
const CalcRoute({List<_i6.PageRouteInfo>? children})
|
const CalcRoute({List<_i7.PageRouteInfo>? children})
|
||||||
: super(CalcRoute.name, initialChildren: children);
|
: super(CalcRoute.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'CalcRoute';
|
static const String name = 'CalcRoute';
|
||||||
|
|
||||||
static _i6.PageInfo page = _i6.PageInfo(
|
static _i7.PageInfo page = _i7.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i3.CalcScreen();
|
return const _i3.CalcScreen();
|
||||||
@@ -66,13 +68,13 @@ class CalcRoute extends _i6.PageRouteInfo<void> {
|
|||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i4.DashboardRouter]
|
/// [_i4.DashboardRouter]
|
||||||
class DashboardRouter extends _i6.PageRouteInfo<void> {
|
class DashboardRouter extends _i7.PageRouteInfo<void> {
|
||||||
const DashboardRouter({List<_i6.PageRouteInfo>? children})
|
const DashboardRouter({List<_i7.PageRouteInfo>? children})
|
||||||
: super(DashboardRouter.name, initialChildren: children);
|
: super(DashboardRouter.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'DashboardRouter';
|
static const String name = 'DashboardRouter';
|
||||||
|
|
||||||
static _i6.PageInfo page = _i6.PageInfo(
|
static _i7.PageInfo page = _i7.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i4.DashboardRouter();
|
return const _i4.DashboardRouter();
|
||||||
@@ -81,17 +83,70 @@ class DashboardRouter extends _i6.PageRouteInfo<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i5.ServerInfoSetupScreen]
|
/// [_i5.ServerConnectionScreen]
|
||||||
class ServerInfoSetupRoute extends _i6.PageRouteInfo<void> {
|
class ServerConnectionRoute
|
||||||
const ServerInfoSetupRoute({List<_i6.PageRouteInfo>? children})
|
extends _i7.PageRouteInfo<ServerConnectionRouteArgs> {
|
||||||
|
ServerConnectionRoute({
|
||||||
|
_i8.Key? key,
|
||||||
|
String? arbiterUrl,
|
||||||
|
List<_i7.PageRouteInfo>? children,
|
||||||
|
}) : super(
|
||||||
|
ServerConnectionRoute.name,
|
||||||
|
args: ServerConnectionRouteArgs(key: key, arbiterUrl: arbiterUrl),
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'ServerConnectionRoute';
|
||||||
|
|
||||||
|
static _i7.PageInfo page = _i7.PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
final args = data.argsAs<ServerConnectionRouteArgs>(
|
||||||
|
orElse: () => const ServerConnectionRouteArgs(),
|
||||||
|
);
|
||||||
|
return _i5.ServerConnectionScreen(
|
||||||
|
key: args.key,
|
||||||
|
arbiterUrl: args.arbiterUrl,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServerConnectionRouteArgs {
|
||||||
|
const ServerConnectionRouteArgs({this.key, this.arbiterUrl});
|
||||||
|
|
||||||
|
final _i8.Key? key;
|
||||||
|
|
||||||
|
final String? arbiterUrl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'ServerConnectionRouteArgs{key: $key, arbiterUrl: $arbiterUrl}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
if (other is! ServerConnectionRouteArgs) return false;
|
||||||
|
return key == other.key && arbiterUrl == other.arbiterUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => key.hashCode ^ arbiterUrl.hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [_i6.ServerInfoSetupScreen]
|
||||||
|
class ServerInfoSetupRoute extends _i7.PageRouteInfo<void> {
|
||||||
|
const ServerInfoSetupRoute({List<_i7.PageRouteInfo>? children})
|
||||||
: super(ServerInfoSetupRoute.name, initialChildren: children);
|
: super(ServerInfoSetupRoute.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'ServerInfoSetupRoute';
|
static const String name = 'ServerInfoSetupRoute';
|
||||||
|
|
||||||
static _i6.PageInfo page = _i6.PageInfo(
|
static _i7.PageInfo page = _i7.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i5.ServerInfoSetupScreen();
|
return const _i6.ServerInfoSetupScreen();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
60
useragent/lib/screens/server_connection.dart
Normal file
60
useragent/lib/screens/server_connection.dart
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import 'package:arbiter/providers/connection/connection_manager.dart';
|
||||||
|
import 'package:arbiter/router.gr.dart';
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:sizer/sizer.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class ServerConnectionScreen extends HookConsumerWidget {
|
||||||
|
const ServerConnectionScreen({super.key, this.arbiterUrl});
|
||||||
|
|
||||||
|
final String? arbiterUrl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final connectionState = ref.watch(connectionManagerProvider);
|
||||||
|
|
||||||
|
if (connectionState.value != null) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
context.router.replace(const DashboardRouter());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
final body = switch (connectionState) {
|
||||||
|
AsyncLoading() => const CircularProgressIndicator(),
|
||||||
|
AsyncError(:final error) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Connection failed',
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
SizedBox(height: 1.5.h),
|
||||||
|
Text('$error', textAlign: TextAlign.center),
|
||||||
|
SizedBox(height: 2.h),
|
||||||
|
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
context.router.replace(const ServerInfoSetupRoute());
|
||||||
|
},
|
||||||
|
child: const Text('Back to server setup'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
_ => const CircularProgressIndicator(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(title: const Text('Connecting')),
|
||||||
|
body: Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 8.w),
|
||||||
|
child: body,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:arbiter/features/arbiter_url.dart';
|
import 'package:arbiter/features/connection/arbiter_url.dart';
|
||||||
import 'package:arbiter/features/server_info_storage.dart';
|
import 'package:arbiter/features/connection/server_info_storage.dart';
|
||||||
|
import 'package:arbiter/providers/connection/bootstrap_token.dart';
|
||||||
import 'package:arbiter/providers/server_info.dart';
|
import 'package:arbiter/providers/server_info.dart';
|
||||||
import 'package:arbiter/router.gr.dart';
|
import 'package:arbiter/router.gr.dart';
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
@@ -25,7 +26,7 @@ class ServerInfoSetupScreen extends HookConsumerWidget {
|
|||||||
if (serverInfo != null) {
|
if (serverInfo != null) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
context.router.replace(const DashboardRouter());
|
context.router.replace(ServerConnectionRoute());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -38,6 +39,14 @@ class ServerInfoSetupScreen extends HookConsumerWidget {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final arbiterUrl = ArbiterUrl.parse(controller.text.trim());
|
final arbiterUrl = ArbiterUrl.parse(controller.text.trim());
|
||||||
|
|
||||||
|
// set token before triggering reconnection by updating server info
|
||||||
|
if (arbiterUrl.bootstrapToken != null) {
|
||||||
|
ref
|
||||||
|
.read(bootstrapTokenProvider.notifier)
|
||||||
|
.set(arbiterUrl.bootstrapToken!);
|
||||||
|
}
|
||||||
|
|
||||||
await ref
|
await ref
|
||||||
.read(serverInfoProvider.notifier)
|
.read(serverInfoProvider.notifier)
|
||||||
.save(
|
.save(
|
||||||
@@ -47,7 +56,9 @@ class ServerInfoSetupScreen extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
context.router.replace(const DashboardRouter());
|
context.router.replace(
|
||||||
|
ServerConnectionRoute(arbiterUrl: controller.text.trim()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} on FormatException catch (error) {
|
} on FormatException catch (error) {
|
||||||
errorText.value = error.message;
|
errorText.value = error.message;
|
||||||
@@ -94,13 +105,15 @@ class ServerInfoSetupScreen extends HookConsumerWidget {
|
|||||||
final options = [
|
final options = [
|
||||||
const _OptionCard(
|
const _OptionCard(
|
||||||
title: 'Local',
|
title: 'Local',
|
||||||
subtitle: 'Will start and connect to a local service in a future update.',
|
subtitle:
|
||||||
|
'Will start and connect to a local service in a future update.',
|
||||||
enabled: false,
|
enabled: false,
|
||||||
child: SizedBox.shrink(),
|
child: SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
_OptionCard(
|
_OptionCard(
|
||||||
title: 'Remote',
|
title: 'Remote',
|
||||||
subtitle: 'Paste an Arbiter URL to store the server address, port, and CA fingerprint.',
|
subtitle:
|
||||||
|
'Paste an Arbiter URL to store the server address, port, and CA fingerprint.',
|
||||||
child: _RemoteConnectionForm(
|
child: _RemoteConnectionForm(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
errorText: errorText.value,
|
errorText: errorText.value,
|
||||||
@@ -271,10 +284,7 @@ class _OptionCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
SizedBox(height: 1.h),
|
SizedBox(height: 1.h),
|
||||||
Text(subtitle, style: theme.textTheme.bodyMedium),
|
Text(subtitle, style: theme.textTheme.bodyMedium),
|
||||||
if (enabled) ...[
|
if (enabled) ...[SizedBox(height: 2.h), child],
|
||||||
SizedBox(height: 2.h),
|
|
||||||
child,
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -4,5 +4,7 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>keychain-access-groups</key>
|
<key>keychain-access-groups</key>
|
||||||
<array/>
|
<array/>
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -6,5 +6,7 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>keychain-access-groups</key>
|
<key>keychain-access-groups</key>
|
||||||
<array/>
|
<array/>
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.5+2"
|
version: "0.3.5+2"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: crypto
|
name: crypto
|
||||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||||
@@ -401,8 +401,16 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
freezed:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: freezed
|
||||||
|
sha256: "13065f10e135263a4f5a4391b79a8efc5fb8106f8dd555a9e49b750b45393d77"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.3"
|
||||||
freezed_annotation:
|
freezed_annotation:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: freezed_annotation
|
name: freezed_annotation
|
||||||
sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8"
|
sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8"
|
||||||
@@ -538,13 +546,21 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.2"
|
version: "0.7.2"
|
||||||
json_annotation:
|
json_annotation:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: json_annotation
|
name: json_annotation
|
||||||
sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8
|
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.11.0"
|
version: "4.9.0"
|
||||||
|
json_serializable:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: json_serializable
|
||||||
|
sha256: c5b2ee75210a0f263c6c7b9eeea80553dbae96ea1bf57f02484e806a3ffdffa3
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.11.2"
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -918,6 +934,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.1"
|
version: "4.2.1"
|
||||||
|
source_helper:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_helper
|
||||||
|
sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.8"
|
||||||
source_map_stack_trace:
|
source_map_stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ dependencies:
|
|||||||
hosted: https://git.markettakers.org/api/packages/MarketTakers/pub/
|
hosted: https://git.markettakers.org/api/packages/MarketTakers/pub/
|
||||||
version: ^1.0.6
|
version: ^1.0.6
|
||||||
cryptography: ^2.9.0
|
cryptography: ^2.9.0
|
||||||
|
crypto: ^3.0.6
|
||||||
flutter_secure_storage: ^10.0.0
|
flutter_secure_storage: ^10.0.0
|
||||||
cryptography_flutter: ^2.3.4
|
cryptography_flutter: ^2.3.4
|
||||||
riverpod_annotation: ^4.0.0
|
riverpod_annotation: ^4.0.0
|
||||||
@@ -30,6 +31,8 @@ dependencies:
|
|||||||
flutter_hooks: ^0.21.3+1
|
flutter_hooks: ^0.21.3+1
|
||||||
auto_route: ^11.1.0
|
auto_route: ^11.1.0
|
||||||
protobuf: ^6.0.0
|
protobuf: ^6.0.0
|
||||||
|
freezed_annotation: ^3.1.0
|
||||||
|
json_annotation: ^4.9.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@@ -39,6 +42,8 @@ dev_dependencies:
|
|||||||
riverpod_generator: ^4.0.0+1
|
riverpod_generator: ^4.0.0+1
|
||||||
build_runner: ^2.12.2
|
build_runner: ^2.12.2
|
||||||
auto_route_generator: ^10.4.0
|
auto_route_generator: ^10.4.0
|
||||||
|
freezed: ^3.2.3
|
||||||
|
json_serializable: ^6.11.2
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|||||||
Reference in New Issue
Block a user