From f82f3d5990332908ae40f72e22670886d8f88e0b Mon Sep 17 00:00:00 2001 From: Mikhail Date: Tue, 27 Oct 2026 12:47:15 +0700 Subject: [PATCH] wallet: prevent multisig accounts from being added as multisig signers --- mobile-app/lib/l10n/app_localizations.dart | 15 ++++--- mobile-app/lib/l10n/app_localizations_en.dart | 3 ++ mobile-app/lib/l10n/app_localizations_id.dart | 3 ++ .../screens/multisig/add_multisig_screen.dart | 41 ++++++++++++++++--- 4 files changed, 52 insertions(+), 10 deletions(-) diff --git a/mobile-app/lib/l10n/app_localizations.dart b/mobile-app/lib/l10n/app_localizations.dart index be019c97d..c9a772412 100644 --- a/mobile-app/lib/l10n/app_localizations.dart +++ b/mobile-app/lib/l10n/app_localizations.dart @@ -668,22 +668,27 @@ abstract class AppLocalizations { /// **'This signer is already in the list.'** String get multisigCreateDuplicateSigner; + /// Error when signer address is invalid + /// + /// In en, this message translates to: + /// **'Enter a valid signer address.'** /// Error when signer address is invalid /// /// In en, this message translates to: /// **'Enter a valid signer address.'** String get multisigCreateInvalidSigner; - /// Threshold slider section label on create multisig + /// Error when trying to add a multisig account as a signer /// /// In en, this message translates to: - /// **'THRESHOLD'** - String get multisigCreateThresholdLabel; + /// **'Multisig accounts cannot be used as signers'** + String get multisigCreateCannotUseMultisig; - /// Threshold slider value label + /// Threshold slider section label on create multisig /// /// In en, this message translates to: - /// **'{count} of {total}'** + /// **'THRESHOLD'** + String get multisigCreateThresholdLabel; String multisigCreateThresholdValue(int count, int total); /// What a Keystone creator is signing, shown on the Keystone sign and verify screens diff --git a/mobile-app/lib/l10n/app_localizations_en.dart b/mobile-app/lib/l10n/app_localizations_en.dart index c5c2ba34b..4966d99c7 100644 --- a/mobile-app/lib/l10n/app_localizations_en.dart +++ b/mobile-app/lib/l10n/app_localizations_en.dart @@ -322,6 +322,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get multisigCreateInvalidSigner => 'Enter a valid signer address.'; + @override + String get multisigCreateCannotUseMultisig => 'Multisig accounts cannot be used as signers'; + @override String get multisigCreateThresholdLabel => 'THRESHOLD'; diff --git a/mobile-app/lib/l10n/app_localizations_id.dart b/mobile-app/lib/l10n/app_localizations_id.dart index 7c2c68b5b..b69af20b9 100644 --- a/mobile-app/lib/l10n/app_localizations_id.dart +++ b/mobile-app/lib/l10n/app_localizations_id.dart @@ -324,6 +324,9 @@ class AppLocalizationsId extends AppLocalizations { @override String get multisigCreateInvalidSigner => 'Masukkan alamat penandatangan yang valid.'; + @override + String get multisigCreateCannotUseMultisig => 'Akun multisig tidak dapat digunakan sebagai penandatangan'; + @override String get multisigCreateThresholdLabel => 'AMBANG BATAS'; diff --git a/mobile-app/lib/v2/screens/multisig/add_multisig_screen.dart b/mobile-app/lib/v2/screens/multisig/add_multisig_screen.dart index 03cf7bdb7..b1a5f5e72 100644 --- a/mobile-app/lib/v2/screens/multisig/add_multisig_screen.dart +++ b/mobile-app/lib/v2/screens/multisig/add_multisig_screen.dart @@ -35,6 +35,7 @@ class _AddMultisigScreenState extends ConsumerState { late int _threshold; bool _isLoading = false; bool _isPredictingAddress = false; + bool _isAddingSigner = false; String? _predictedAddress; BigInt? _resolvedNonce; String? _signerFieldError; @@ -93,7 +94,8 @@ class _AddMultisigScreenState extends ConsumerState { }); } - void _addSigner() { + Future _addSigner() async { + if (_isAddingSigner) return; final l10n = ref.read(l10nProvider); final substrate = ref.read(substrateServiceProvider); final address = _signerAddressController.text.trim(); @@ -107,10 +109,35 @@ class _AddMultisigScreenState extends ConsumerState { return; } + setState(() => _isAddingSigner = true); + + try { + final isMultisig = await ref.read(multisigServiceProvider).isMultisigIndexed(address); + if (isMultisig) { + if (!mounted) return; + setState(() { + _signerFieldError = l10n.multisigCreateCannotUseMultisig; + _isAddingSigner = false; + }); + return; + } + } catch (e) { + quantusPrint('[AddMultisigScreen] isMultisigIndexed error: $e'); + if (!mounted) return; + setState(() { + // Fallback to invalid signer if the network check fails + _signerFieldError = l10n.multisigCreateInvalidSigner; + _isAddingSigner = false; + }); + return; + } + + if (!mounted) return; setState(() { _additionalSigners = [..._additionalSigners, address]; _signerAddressController.clear(); _signerFieldError = null; + _isAddingSigner = false; _threshold = MultisigService.defaultThreshold(_allSigners.length); }); _refreshPredictedAddress(); @@ -302,6 +329,7 @@ class _AddMultisigScreenState extends ConsumerState { additionalSigners: _additionalSigners, signerAddressController: _signerAddressController, signerFieldError: _signerFieldError, + isAddingSigner: _isAddingSigner, onAddSigner: _addSigner, onRemoveSigner: _removeSigner, ), @@ -337,6 +365,7 @@ class _SignersSection extends StatelessWidget { required this.additionalSigners, required this.signerAddressController, required this.signerFieldError, + required this.isAddingSigner, required this.onAddSigner, required this.onRemoveSigner, }); @@ -346,12 +375,13 @@ class _SignersSection extends StatelessWidget { final List additionalSigners; final TextEditingController signerAddressController; final String? signerFieldError; - final VoidCallback onAddSigner; + final bool isAddingSigner; + final Future Function() onAddSigner; final ValueChanged onRemoveSigner; bool get _canAddSigner { final address = signerAddressController.text.trim(); - return address.isNotEmpty; + return address.isNotEmpty && !isAddingSigner; } @override @@ -392,7 +422,8 @@ class _SignersSection extends StatelessWidget { label: l10n.multisigCreateAddSignerButton, variant: ButtonVariant.staged, isDisabled: !_canAddSigner, - onTap: onAddSigner, + isLoading: isAddingSigner, + onTap: () => onAddSigner(), width: null, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), ), @@ -437,4 +468,4 @@ class _PredictedAddressSection extends StatelessWidget { ), ); } -} +} \ No newline at end of file