fix(useragent): unsafe, but working implementation of ml-dsa
This commit is contained in:
@@ -28,8 +28,7 @@ class SdkConnectCallout extends StatelessWidget {
|
||||
|
||||
final hasDescription =
|
||||
clientInfo.hasDescription() && clientInfo.description.isNotEmpty;
|
||||
final hasVersion =
|
||||
clientInfo.hasVersion() && clientInfo.version.isNotEmpty;
|
||||
final hasVersion = clientInfo.hasVersion() && clientInfo.version.isNotEmpty;
|
||||
final showInfoCard = hasDescription || hasVersion;
|
||||
|
||||
return CreamFrame(
|
||||
@@ -74,10 +73,7 @@ class SdkConnectCallout extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
border: Border.all(color: Palette.line),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 1.6.w,
|
||||
vertical: 1.2.h,
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 1.6.w, vertical: 1.2.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 0.6.h,
|
||||
|
||||
@@ -78,7 +78,7 @@ class DashboardRouter extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _CalloutBell extends ConsumerWidget {
|
||||
const _CalloutBell({super.key});
|
||||
const _CalloutBell();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import 'package:arbiter/proto/user_agent/sdk_client.pb.dart' as ua_sdk;
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -13,5 +12,4 @@ class ClientDetails extends ConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,30 +12,24 @@ class ClientSummaryCard extends StatelessWidget {
|
||||
return CreamFrame(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
client.info.name,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(client.info.description),
|
||||
const SizedBox(height: 16),
|
||||
Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 16,
|
||||
children: [
|
||||
_Fact(label: 'Client ID', value: '${client.id}'),
|
||||
_Fact(label: 'Version', value: client.info.version),
|
||||
_Fact(
|
||||
label: 'Registered',
|
||||
value: _formatDate(client.createdAt),
|
||||
),
|
||||
_Fact(label: 'Pubkey', value: _shortPubkey(client.pubkey)),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(client.info.name, style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: 8),
|
||||
Text(client.info.description),
|
||||
const SizedBox(height: 16),
|
||||
Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 16,
|
||||
children: [
|
||||
_Fact(label: 'Client ID', value: '${client.id}'),
|
||||
_Fact(label: 'Version', value: client.info.version),
|
||||
_Fact(label: 'Registered', value: _formatDate(client.createdAt)),
|
||||
_Fact(label: 'Pubkey', value: _shortPubkey(client.pubkey)),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,27 +28,27 @@ class WalletAccessSaveBar extends StatelessWidget {
|
||||
return CreamFrame(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (errorText != null) ...[
|
||||
Text(errorText, style: TextStyle(color: Palette.coral)),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
Row(
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: state.hasChanges && !isPending ? onDiscard : null,
|
||||
child: const Text('Reset'),
|
||||
),
|
||||
const Spacer(),
|
||||
FilledButton(
|
||||
onPressed: state.hasChanges && !isPending ? onSave : null,
|
||||
child: Text(isPending ? 'Saving...' : 'Save changes'),
|
||||
),
|
||||
],
|
||||
),
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (errorText != null) ...[
|
||||
Text(errorText, style: TextStyle(color: Palette.coral)),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: state.hasChanges && !isPending ? onDiscard : null,
|
||||
child: const Text('Reset'),
|
||||
),
|
||||
const Spacer(),
|
||||
FilledButton(
|
||||
onPressed: state.hasChanges && !isPending ? onSave : null,
|
||||
child: Text(isPending ? 'Saving...' : 'Save changes'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,26 +30,23 @@ class WalletAccessSection extends ConsumerWidget {
|
||||
return CreamFrame(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Wallet access',
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text('Choose which managed wallets this client can see.'),
|
||||
const SizedBox(height: 16),
|
||||
_WalletAccessBody(
|
||||
clientId: clientId,
|
||||
state: state,
|
||||
accessSelectionAsync: accessSelectionAsync,
|
||||
isSavePending: isSavePending,
|
||||
optionsAsync: optionsAsync,
|
||||
onSearchChanged: onSearchChanged,
|
||||
onToggleWallet: onToggleWallet,
|
||||
),
|
||||
],
|
||||
),
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Wallet access', style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: 8),
|
||||
Text('Choose which managed wallets this client can see.'),
|
||||
const SizedBox(height: 16),
|
||||
_WalletAccessBody(
|
||||
clientId: clientId,
|
||||
state: state,
|
||||
accessSelectionAsync: accessSelectionAsync,
|
||||
isSavePending: isSavePending,
|
||||
optionsAsync: optionsAsync,
|
||||
onSearchChanged: onSearchChanged,
|
||||
onToggleWallet: onToggleWallet,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,48 +378,48 @@ class _ClientTable extends StatelessWidget {
|
||||
builder: (context, constraints) {
|
||||
final tableWidth = math.max(_tableMinWidth, constraints.maxWidth);
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Registered clients',
|
||||
style: theme.textTheme.titleLarge?.copyWith(
|
||||
color: Palette.ink,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Registered clients',
|
||||
style: theme.textTheme.titleLarge?.copyWith(
|
||||
color: Palette.ink,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
SizedBox(height: 0.6.h),
|
||||
Text(
|
||||
'Every entry here has authenticated with Arbiter at least once.',
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: Palette.ink.withValues(alpha: 0.70),
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 0.6.h),
|
||||
Text(
|
||||
'Every entry here has authenticated with Arbiter at least once.',
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: Palette.ink.withValues(alpha: 0.70),
|
||||
height: 1.4,
|
||||
),
|
||||
SizedBox(height: 1.6.h),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SizedBox(
|
||||
width: tableWidth,
|
||||
child: Column(
|
||||
children: [
|
||||
const _ClientTableHeader(),
|
||||
SizedBox(height: 1.h),
|
||||
for (var i = 0; i < clients.length; i++)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: i == clients.length - 1 ? 0 : 1.h,
|
||||
),
|
||||
child: _ClientTableRow(client: clients[i]),
|
||||
),
|
||||
SizedBox(height: 1.6.h),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SizedBox(
|
||||
width: tableWidth,
|
||||
child: Column(
|
||||
children: [
|
||||
const _ClientTableHeader(),
|
||||
SizedBox(height: 1.h),
|
||||
for (var i = 0; i < clients.length; i++)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: i == clients.length - 1 ? 0 : 1.h,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: _ClientTableRow(client: clients[i]),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,9 @@ class ClientPickerField extends ConsumerWidget {
|
||||
? null
|
||||
: (value) {
|
||||
ref.read(grantCreationProvider.notifier).setClientId(value);
|
||||
FormBuilder.of(context)?.fields['walletAccessId']?.didChange(null);
|
||||
FormBuilder.of(
|
||||
context,
|
||||
)?.fields['walletAccessId']?.didChange(null);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,46 +16,48 @@ class FormBuilderDateTimeField extends FormBuilderField<DateTime?> {
|
||||
super.onChanged,
|
||||
super.validator,
|
||||
}) : super(
|
||||
builder: (FormFieldState<DateTime?> field) {
|
||||
final value = field.value;
|
||||
return OutlinedButton(
|
||||
onPressed: () async {
|
||||
final ctx = field.context;
|
||||
final now = DateTime.now();
|
||||
final date = await showDatePicker(
|
||||
context: ctx,
|
||||
firstDate: DateTime(now.year - 5),
|
||||
lastDate: DateTime(now.year + 10),
|
||||
initialDate: value ?? now,
|
||||
);
|
||||
if (date == null) return;
|
||||
if (!ctx.mounted) return;
|
||||
final time = await showTimePicker(
|
||||
context: ctx,
|
||||
initialTime: TimeOfDay.fromDateTime(value ?? now),
|
||||
);
|
||||
if (time == null) return;
|
||||
field.didChange(DateTime(
|
||||
date.year,
|
||||
date.month,
|
||||
date.day,
|
||||
time.hour,
|
||||
time.minute,
|
||||
));
|
||||
},
|
||||
onLongPress: value == null ? null : () => field.didChange(null),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 1.8.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(label),
|
||||
SizedBox(height: 0.6.h),
|
||||
Text(value?.toLocal().toString() ?? 'Not set'),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
builder: (FormFieldState<DateTime?> field) {
|
||||
final value = field.value;
|
||||
return OutlinedButton(
|
||||
onPressed: () async {
|
||||
final ctx = field.context;
|
||||
final now = DateTime.now();
|
||||
final date = await showDatePicker(
|
||||
context: ctx,
|
||||
firstDate: DateTime(now.year - 5),
|
||||
lastDate: DateTime(now.year + 10),
|
||||
initialDate: value ?? now,
|
||||
);
|
||||
if (date == null) return;
|
||||
if (!ctx.mounted) return;
|
||||
final time = await showTimePicker(
|
||||
context: ctx,
|
||||
initialTime: TimeOfDay.fromDateTime(value ?? now),
|
||||
);
|
||||
if (time == null) return;
|
||||
field.didChange(
|
||||
DateTime(
|
||||
date.year,
|
||||
date.month,
|
||||
date.day,
|
||||
time.hour,
|
||||
time.minute,
|
||||
),
|
||||
);
|
||||
},
|
||||
onLongPress: value == null ? null : () => field.didChange(null),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 1.8.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(label),
|
||||
SizedBox(height: 0.6.h),
|
||||
Text(value?.toLocal().toString() ?? 'Not set'),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ class WalletAccessPickerField extends ConsumerWidget {
|
||||
helperText: state.selectedClientId == null
|
||||
? 'Select a client first'
|
||||
: accesses.isEmpty
|
||||
? 'No wallet accesses for this client'
|
||||
: null,
|
||||
? 'No wallet accesses for this client'
|
||||
: null,
|
||||
border: const OutlineInputBorder(),
|
||||
),
|
||||
items: [
|
||||
|
||||
@@ -93,9 +93,9 @@ class _EtherTransferForm extends ConsumerWidget {
|
||||
SizedBox(height: 1.6.h),
|
||||
Text(
|
||||
'Ether volume limit',
|
||||
style: Theme.of(context).textTheme.labelLarge?.copyWith(
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.labelLarge?.copyWith(fontWeight: FontWeight.w800),
|
||||
),
|
||||
SizedBox(height: 0.8.h),
|
||||
Row(
|
||||
@@ -157,9 +157,9 @@ class _EtherTargetsField extends StatelessWidget {
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Ether targets',
|
||||
style: Theme.of(context).textTheme.labelLarge?.copyWith(
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.labelLarge?.copyWith(fontWeight: FontWeight.w800),
|
||||
),
|
||||
),
|
||||
TextButton.icon(
|
||||
|
||||
@@ -13,7 +13,11 @@ import 'package:sizer/sizer.dart';
|
||||
part 'token_transfer_grant.g.dart';
|
||||
|
||||
class VolumeLimitEntry {
|
||||
VolumeLimitEntry({required this.id, this.amount = '', this.windowSeconds = ''});
|
||||
VolumeLimitEntry({
|
||||
required this.id,
|
||||
this.amount = '',
|
||||
this.windowSeconds = '',
|
||||
});
|
||||
|
||||
final int id;
|
||||
final String amount;
|
||||
@@ -27,7 +31,6 @@ class VolumeLimitEntry {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@riverpod
|
||||
class TokenGrantLimits extends _$TokenGrantLimits {
|
||||
int _nextId = 0;
|
||||
@@ -47,7 +50,6 @@ class TokenGrantLimits extends _$TokenGrantLimits {
|
||||
void remove(int index) => state = [...state]..removeAt(index);
|
||||
}
|
||||
|
||||
|
||||
class TokenTransferGrantHandler implements GrantFormHandler {
|
||||
const TokenTransferGrantHandler();
|
||||
|
||||
@@ -65,11 +67,16 @@ class TokenTransferGrantHandler implements GrantFormHandler {
|
||||
|
||||
return SpecificGrant(
|
||||
tokenTransfer: TokenTransferSettings(
|
||||
tokenContract:
|
||||
parseHexAddress(formValues['tokenContract'] as String? ?? ''),
|
||||
tokenContract: parseHexAddress(
|
||||
formValues['tokenContract'] as String? ?? '',
|
||||
),
|
||||
target: targetText.trim().isEmpty ? null : parseHexAddress(targetText),
|
||||
volumeLimits: limits
|
||||
.where((e) => e.amount.trim().isNotEmpty && e.windowSeconds.trim().isNotEmpty)
|
||||
.where(
|
||||
(e) =>
|
||||
e.amount.trim().isNotEmpty &&
|
||||
e.windowSeconds.trim().isNotEmpty,
|
||||
)
|
||||
.map(
|
||||
(e) => VolumeRateLimit(
|
||||
maxVolume: parseBigIntBytes(e.amount),
|
||||
@@ -153,9 +160,9 @@ class _TokenVolumeLimitsField extends StatelessWidget {
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Token volume limits',
|
||||
style: Theme.of(context).textTheme.labelLarge?.copyWith(
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.labelLarge?.copyWith(fontWeight: FontWeight.w800),
|
||||
),
|
||||
),
|
||||
TextButton.icon(
|
||||
@@ -196,7 +203,9 @@ class _TokenVolumeLimitRow extends HookWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final amountController = useTextEditingController(text: value.amount);
|
||||
final windowController = useTextEditingController(text: value.windowSeconds);
|
||||
final windowController = useTextEditingController(
|
||||
text: value.windowSeconds,
|
||||
);
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
@@ -214,8 +223,7 @@ class _TokenVolumeLimitRow extends HookWidget {
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: windowController,
|
||||
onChanged: (next) =>
|
||||
onChanged(value.copyWith(windowSeconds: next)),
|
||||
onChanged: (next) => onChanged(value.copyWith(windowSeconds: next)),
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Window (seconds)',
|
||||
border: OutlineInputBorder(),
|
||||
|
||||
@@ -25,10 +25,10 @@ const _etherHandler = EtherTransferGrantHandler();
|
||||
const _tokenHandler = TokenTransferGrantHandler();
|
||||
|
||||
GrantFormHandler _handlerFor(SpecificGrant_Grant type) => switch (type) {
|
||||
SpecificGrant_Grant.etherTransfer => _etherHandler,
|
||||
SpecificGrant_Grant.tokenTransfer => _tokenHandler,
|
||||
_ => throw ArgumentError('Unsupported grant type: $type'),
|
||||
};
|
||||
SpecificGrant_Grant.etherTransfer => _etherHandler,
|
||||
SpecificGrant_Grant.tokenTransfer => _tokenHandler,
|
||||
_ => throw ArgumentError('Unsupported grant type: $type'),
|
||||
};
|
||||
|
||||
@RoutePage()
|
||||
class CreateEvmGrantScreen extends HookConsumerWidget {
|
||||
@@ -62,12 +62,14 @@ class CreateEvmGrantScreen extends HookConsumerWidget {
|
||||
);
|
||||
final validFrom = formValues['validFrom'] as DateTime?;
|
||||
final validUntil = formValues['validUntil'] as DateTime?;
|
||||
if (validFrom != null) sharedSettings.validFrom = toTimestamp(validFrom);
|
||||
if (validFrom != null)
|
||||
sharedSettings.validFrom = toTimestamp(validFrom);
|
||||
if (validUntil != null) {
|
||||
sharedSettings.validUntil = toTimestamp(validUntil);
|
||||
}
|
||||
final gasBytes =
|
||||
optionalBigIntBytes(formValues['maxGasFeePerGas'] as String? ?? '');
|
||||
final gasBytes = optionalBigIntBytes(
|
||||
formValues['maxGasFeePerGas'] as String? ?? '',
|
||||
);
|
||||
if (gasBytes != null) sharedSettings.maxGasFeePerGas = gasBytes;
|
||||
final priorityBytes = optionalBigIntBytes(
|
||||
formValues['maxPriorityFeePerGas'] as String? ?? '',
|
||||
@@ -106,7 +108,8 @@ class CreateEvmGrantScreen extends HookConsumerWidget {
|
||||
SizedBox(height: 1.8.h),
|
||||
const _Section(
|
||||
title: 'Authorization',
|
||||
tooltip: 'Select which SDK client receives this grant and '
|
||||
tooltip:
|
||||
'Select which SDK client receives this grant and '
|
||||
'which of its wallet accesses it applies to.',
|
||||
child: AuthorizationFields(),
|
||||
),
|
||||
@@ -118,7 +121,8 @@ class CreateEvmGrantScreen extends HookConsumerWidget {
|
||||
const Expanded(
|
||||
child: _Section(
|
||||
title: 'Chain',
|
||||
tooltip: 'Restrict this grant to a specific EVM chain ID. '
|
||||
tooltip:
|
||||
'Restrict this grant to a specific EVM chain ID. '
|
||||
'Leave empty to allow any chain.',
|
||||
optional: true,
|
||||
child: ChainIdField(),
|
||||
@@ -128,7 +132,8 @@ class CreateEvmGrantScreen extends HookConsumerWidget {
|
||||
const Expanded(
|
||||
child: _Section(
|
||||
title: 'Timing',
|
||||
tooltip: 'Set an optional validity window. '
|
||||
tooltip:
|
||||
'Set an optional validity window. '
|
||||
'Signing requests outside this period will be rejected.',
|
||||
optional: true,
|
||||
child: ValidityWindowField(),
|
||||
@@ -145,7 +150,8 @@ class CreateEvmGrantScreen extends HookConsumerWidget {
|
||||
const Expanded(
|
||||
child: _Section(
|
||||
title: 'Gas limits',
|
||||
tooltip: 'Cap the gas fees this grant may authorize. '
|
||||
tooltip:
|
||||
'Cap the gas fees this grant may authorize. '
|
||||
'Transactions exceeding these values will be rejected.',
|
||||
optional: true,
|
||||
child: GasFeeOptionsField(),
|
||||
@@ -155,7 +161,8 @@ class CreateEvmGrantScreen extends HookConsumerWidget {
|
||||
const Expanded(
|
||||
child: _Section(
|
||||
title: 'Transaction limits',
|
||||
tooltip: 'Limit how many transactions can be signed '
|
||||
tooltip:
|
||||
'Limit how many transactions can be signed '
|
||||
'within a rolling time window.',
|
||||
optional: true,
|
||||
child: TransactionRateLimitField(),
|
||||
@@ -172,7 +179,8 @@ class CreateEvmGrantScreen extends HookConsumerWidget {
|
||||
SizedBox(height: 1.8.h),
|
||||
_Section(
|
||||
title: 'Grant-specific options',
|
||||
tooltip: 'Rules specific to the selected transfer type. '
|
||||
tooltip:
|
||||
'Rules specific to the selected transfer type. '
|
||||
'Switch between Ether and token above to change these fields.',
|
||||
child: handler.buildForm(context, ref),
|
||||
),
|
||||
@@ -180,8 +188,7 @@ class CreateEvmGrantScreen extends HookConsumerWidget {
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: FilledButton.icon(
|
||||
onPressed:
|
||||
createMutation is MutationPending ? null : submit,
|
||||
onPressed: createMutation is MutationPending ? null : submit,
|
||||
icon: createMutation is MutationPending
|
||||
? SizedBox(
|
||||
width: 1.8.h,
|
||||
@@ -266,9 +273,9 @@ class _Section extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w800),
|
||||
),
|
||||
SizedBox(width: 0.4.w),
|
||||
Tooltip(
|
||||
@@ -283,9 +290,9 @@ class _Section extends StatelessWidget {
|
||||
SizedBox(width: 0.6.w),
|
||||
Text(
|
||||
'(optional)',
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: subtleColor,
|
||||
),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.bodySmall?.copyWith(color: subtleColor),
|
||||
),
|
||||
],
|
||||
],
|
||||
|
||||
@@ -19,4 +19,3 @@ class AuthorizationFields extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,9 +46,7 @@ class GrantCard extends ConsumerWidget {
|
||||
final accessById = <int, ua_sdk.WalletAccessEntry>{
|
||||
for (final a in walletAccesses) a.id: a,
|
||||
};
|
||||
final walletById = <int, WalletEntry>{
|
||||
for (final w in wallets) w.id: w,
|
||||
};
|
||||
final walletById = <int, WalletEntry>{for (final w in wallets) w.id: w};
|
||||
final clientNameById = <int, String>{
|
||||
for (final c in clients) c.id: c.info.name,
|
||||
};
|
||||
@@ -192,8 +190,9 @@ class GrantCard extends ConsumerWidget {
|
||||
padding: EdgeInsets.symmetric(horizontal: 0.8.w),
|
||||
child: Text(
|
||||
'·',
|
||||
style: theme.textTheme.bodySmall
|
||||
?.copyWith(color: muted),
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: muted,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
@@ -201,8 +200,9 @@ class GrantCard extends ConsumerWidget {
|
||||
clientLabel,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: theme.textTheme.bodySmall
|
||||
?.copyWith(color: muted),
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: muted,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -5,7 +5,6 @@ import 'package:hooks_riverpod/experimental/mutation.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:sizer/sizer.dart';
|
||||
|
||||
|
||||
class CreateWalletButton extends ConsumerWidget {
|
||||
const CreateWalletButton({super.key});
|
||||
|
||||
@@ -88,7 +87,6 @@ class RefreshWalletButton extends ConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String _formatError(Object error) {
|
||||
final message = error.toString();
|
||||
if (message.startsWith('Exception: ')) {
|
||||
|
||||
@@ -36,54 +36,51 @@ class WalletTable extends StatelessWidget {
|
||||
return CreamFrame(
|
||||
padding: EdgeInsets.all(2.h),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final tableWidth = math.max(_tableMinWidth, constraints.maxWidth);
|
||||
builder: (context, constraints) {
|
||||
final tableWidth = math.max(_tableMinWidth, constraints.maxWidth);
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Managed wallets',
|
||||
style: theme.textTheme.titleLarge?.copyWith(
|
||||
color: Palette.ink,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Managed wallets',
|
||||
style: theme.textTheme.titleLarge?.copyWith(
|
||||
color: Palette.ink,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
SizedBox(height: 0.6.h),
|
||||
Text(
|
||||
'Every address here is generated and held by Arbiter.',
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: Palette.ink.withValues(alpha: 0.70),
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 0.6.h),
|
||||
Text(
|
||||
'Every address here is generated and held by Arbiter.',
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: Palette.ink.withValues(alpha: 0.70),
|
||||
height: 1.4,
|
||||
),
|
||||
SizedBox(height: 1.6.h),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SizedBox(
|
||||
width: tableWidth,
|
||||
child: Column(
|
||||
children: [
|
||||
const _WalletTableHeader(),
|
||||
SizedBox(height: 1.h),
|
||||
for (var i = 0; i < wallets.length; i++)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: i == wallets.length - 1 ? 0 : 1.h,
|
||||
),
|
||||
child: _WalletTableRow(
|
||||
wallet: wallets[i],
|
||||
index: i,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 1.6.h),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SizedBox(
|
||||
width: tableWidth,
|
||||
child: Column(
|
||||
children: [
|
||||
const _WalletTableHeader(),
|
||||
SizedBox(height: 1.h),
|
||||
for (var i = 0; i < wallets.length; i++)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: i == wallets.length - 1 ? 0 : 1.h,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: _WalletTableRow(wallet: wallets[i], index: i),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user