mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-17 05:41:29 +02:00
Cw 1118 silent payment fixes (#2373)
* fix: hive box error * fix: address validator * fix: guarantee save on sp scanned * fix: too many saves * feat: network based address validation * fix: sp testnet * feat: ltc validator * fix: use general sp regex on address getter and remove print
This commit is contained in:
@@ -405,8 +405,11 @@ abstract class ElectrumWalletBase
|
||||
|
||||
// Add new TX record
|
||||
transactionHistory.addMany(message);
|
||||
|
||||
// Update balance record
|
||||
balance[currency]!.confirmed += tx.amount;
|
||||
|
||||
await save();
|
||||
}
|
||||
|
||||
await updateAllUnspents();
|
||||
@@ -544,7 +547,6 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
|
||||
node!.isElectrs = false;
|
||||
node!.save();
|
||||
return node!.isElectrs!;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,27 +5,30 @@ import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/zano/zano.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
|
||||
const BEFORE_REGEX = '(^|\\s)';
|
||||
const AFTER_REGEX = '(\$|\\s)';
|
||||
|
||||
class AddressValidator extends TextValidator {
|
||||
AddressValidator({required CryptoCurrency type})
|
||||
AddressValidator({required CryptoCurrency type, bool isTestnet = false})
|
||||
: super(
|
||||
errorMessage: S.current.error_text_address,
|
||||
useAdditionalValidation: type == CryptoCurrency.btc || type == CryptoCurrency.ltc
|
||||
? (String txt) => BitcoinAddressUtils.validateAddress(
|
||||
address: txt,
|
||||
network: type == CryptoCurrency.btc
|
||||
? BitcoinNetwork.mainnet
|
||||
? isTestnet
|
||||
? BitcoinNetwork.testnet
|
||||
: BitcoinNetwork.mainnet
|
||||
: LitecoinNetwork.mainnet,
|
||||
)
|
||||
: type == CryptoCurrency.zano
|
||||
: type == CryptoCurrency.zano
|
||||
? zano?.validateAddress
|
||||
: null,
|
||||
pattern: getPattern(type),
|
||||
pattern: getPattern(type, isTestnet: isTestnet),
|
||||
length: getLength(type));
|
||||
|
||||
static String getPattern(CryptoCurrency type) {
|
||||
static String getPattern(CryptoCurrency type, {required bool isTestnet}) {
|
||||
var pattern = "";
|
||||
if (type is Erc20Token) {
|
||||
pattern = '0x[0-9a-zA-Z]+';
|
||||
@@ -37,10 +40,28 @@ class AddressValidator extends TextValidator {
|
||||
pattern = '[0-9a-zA-Z]{59}|[0-9a-zA-Z]{92}|[0-9a-zA-Z]{104}'
|
||||
'|[0-9a-zA-Z]{105}|addr1[0-9a-zA-Z]{98}';
|
||||
case CryptoCurrency.btc:
|
||||
pattern =
|
||||
'${P2pkhAddress.regex.pattern}|${P2shAddress.regex.pattern}|${P2wpkhAddress.regex.pattern}|${P2trAddress.regex.pattern}|${P2wshAddress.regex.pattern}|${SilentPaymentAddress.regex.pattern}';
|
||||
if (isTestnet) {
|
||||
pattern = '(^|\s)([mnL][a-km-zA-HJ-NP-Z1-9]{25,34})'
|
||||
'|([23M][a-km-zA-HJ-NP-Z1-9]{25,34})'
|
||||
'|(tb1q[ac-hj-np-z02-9]{25,39})'
|
||||
'|(tb1p([ac-hj-np-z02-9]{39}|[ac-hj-np-z02-9]{59}|[ac-hj-np-z02-9]{8,89}))'
|
||||
'|(tb1q[ac-hj-np-z02-9]{40,80})'
|
||||
'|(${silentPaymentAddressPatternTestnet})(\$|\s)';
|
||||
} else {
|
||||
pattern = '(^|\s)(1[a-km-zA-HJ-NP-Z1-9]{25,34})'
|
||||
'|(3[a-km-zA-HJ-NP-Z1-9]{25,34})'
|
||||
'|(bc1q[ac-hj-np-z02-9]{25,39})'
|
||||
'|(bc1p([ac-hj-np-z02-9]{39}|[ac-hj-np-z02-9]{59}|[ac-hj-np-z02-9]{8,89}))'
|
||||
'|(bc1q[ac-hj-np-z02-9]{40,80})'
|
||||
'|(${silentPaymentAddressPatternMainnet})(\$|\s)';
|
||||
}
|
||||
case CryptoCurrency.ltc:
|
||||
pattern = '${P2wpkhAddress.regex.pattern}|${MwebAddress.regex.pattern}';
|
||||
pattern = '(^|\s)(L[a-km-zA-HJ-NP-Z1-9]{25,34})'
|
||||
'|([3M][a-km-zA-HJ-NP-Z1-9]{25,34})'
|
||||
'|(ltc1q[ac-hj-np-z02-9]{25,39})'
|
||||
'|(ltc1p([ac-hj-np-z02-9]{39}|[ac-hj-np-z02-9]{59}|[ac-hj-np-z02-9]{8,89}))'
|
||||
'|(ltc1q[ac-hj-np-z02-9]{40,80})'
|
||||
'|(${MwebAddress.regex.pattern})(\$|\s)';
|
||||
case CryptoCurrency.nano:
|
||||
pattern = '[0-9a-zA-Z_]+';
|
||||
case CryptoCurrency.banano:
|
||||
@@ -281,9 +302,11 @@ class AddressValidator extends TextValidator {
|
||||
}
|
||||
}
|
||||
|
||||
static String get silentPaymentAddressPattern => SilentPaymentAddress.regex.pattern;
|
||||
static String get silentPaymentAddressPatternMainnet => 'sp1[0-9a-zA-Z]{113}';
|
||||
static String get silentPaymentAddressPatternTestnet => '(tsp|sprt)1[0-9a-zA-Z]{113}';
|
||||
static String get mWebAddressPattern => MwebAddress.regex.pattern;
|
||||
|
||||
// NOTE: not needed to check for network here as it's a general address catcher, validation is separate
|
||||
static String? getAddressFromStringPattern(CryptoCurrency type) {
|
||||
String? pattern = null;
|
||||
|
||||
|
||||
@@ -736,8 +736,7 @@ class SendPage extends BasePage {
|
||||
currentTheme: currentTheme,
|
||||
footerType: FooterType.singleActionButton,
|
||||
titleText: S.of(context).proceed_on_device,
|
||||
contentImage:
|
||||
'assets/images/hardware_wallet/ledger_nano_x.png',
|
||||
contentImage: 'assets/images/hardware_wallet/ledger_nano_x.png',
|
||||
contentImageColor: Theme.of(context).colorScheme.onSurface,
|
||||
content: S.of(context).proceed_on_device_description,
|
||||
singleActionButtonText: S.of(context).cancel,
|
||||
@@ -810,7 +809,8 @@ class SendPage extends BasePage {
|
||||
bool isRegularElectrumAddress(String address) {
|
||||
final supportedTypes = [CryptoCurrency.btc, CryptoCurrency.ltc, CryptoCurrency.bch];
|
||||
final excludedPatterns = [
|
||||
RegExp(AddressValidator.silentPaymentAddressPattern),
|
||||
RegExp(AddressValidator.silentPaymentAddressPatternMainnet),
|
||||
RegExp(AddressValidator.silentPaymentAddressPatternTestnet),
|
||||
RegExp(AddressValidator.mWebAddressPattern)
|
||||
];
|
||||
|
||||
|
||||
@@ -46,7 +46,8 @@ abstract class SendTemplateViewModelBase with Store {
|
||||
AmountValidator get amountValidator =>
|
||||
AmountValidator(currency: walletTypeToCryptoCurrency(_wallet.type));
|
||||
|
||||
AddressValidator get addressValidator => AddressValidator(type: _wallet.currency);
|
||||
AddressValidator get addressValidator =>
|
||||
AddressValidator(type: _wallet.currency, isTestnet: _wallet.isTestnet);
|
||||
|
||||
TemplateValidator get templateValidator => TemplateValidator();
|
||||
|
||||
@@ -102,7 +103,8 @@ abstract class SendTemplateViewModelBase with Store {
|
||||
@computed
|
||||
List<CryptoCurrency> get walletCurrencies => _wallet.balance.keys.toList();
|
||||
|
||||
bool get hasMultipleTokens => isEVMCompatibleChain(_wallet.type) ||
|
||||
_wallet.type == WalletType.solana ||
|
||||
_wallet.type == WalletType.tron;
|
||||
bool get hasMultipleTokens =>
|
||||
isEVMCompatibleChain(_wallet.type) ||
|
||||
_wallet.type == WalletType.solana ||
|
||||
_wallet.type == WalletType.tron;
|
||||
}
|
||||
|
||||
@@ -215,7 +215,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
|
||||
Validator<String> get allAmountValidator => AllAmountValidator();
|
||||
|
||||
Validator<String> get addressValidator => AddressValidator(type: selectedCryptoCurrency);
|
||||
Validator<String> get addressValidator =>
|
||||
AddressValidator(type: selectedCryptoCurrency, isTestnet: wallet.isTestnet);
|
||||
|
||||
Validator<String> get textValidator => TextValidator();
|
||||
|
||||
@@ -777,12 +778,12 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
return S.current.solana_no_associated_token_account_exception;
|
||||
}
|
||||
|
||||
if (errorMessage.contains('insufficient funds for rent') &&
|
||||
errorMessage.contains('Transaction simulation failed') &&
|
||||
if (errorMessage.contains('insufficient funds for rent') &&
|
||||
errorMessage.contains('Transaction simulation failed') &&
|
||||
errorMessage.contains('account_index')) {
|
||||
final accountIndexMatch = RegExp(r'account_index: (\d+)').firstMatch(errorMessage);
|
||||
if (accountIndexMatch != null) {
|
||||
return int.parse(accountIndexMatch.group(1)!) == 0
|
||||
return int.parse(accountIndexMatch.group(1)!) == 0
|
||||
? S.current.insufficientFundsForRentError
|
||||
: S.current.insufficientFundsForRentErrorReceiver;
|
||||
}
|
||||
|
||||
@@ -24,17 +24,14 @@ void main() {
|
||||
test('returns correct pattern for Monero', () {
|
||||
final pattern = AddressValidator.getPattern(CryptoCurrency.xmr);
|
||||
expect(pattern, isNotEmpty);
|
||||
expect(pattern,
|
||||
contains('4[0-9a-zA-Z]{94}|8[0-9a-zA-Z]{94}|[0-9a-zA-Z]{106}'));
|
||||
expect(pattern, contains('4[0-9a-zA-Z]{94}|8[0-9a-zA-Z]{94}|[0-9a-zA-Z]{106}'));
|
||||
});
|
||||
|
||||
test('returns correct pattern for Litecoin', () {
|
||||
final pattern = AddressValidator.getPattern(CryptoCurrency.ltc);
|
||||
expect(pattern, isNotEmpty);
|
||||
expect(
|
||||
pattern,
|
||||
contains(
|
||||
'(bc|tb|ltc)1q[ac-hj-np-z02-9]{25,39}|(ltc|t)mweb1q[ac-hj-np-z02-9]{90,120}'));
|
||||
expect(pattern,
|
||||
contains('(bc|tb|ltc)1q[ac-hj-np-z02-9]{25,39}|(ltc|t)mweb1q[ac-hj-np-z02-9]{90,120}'));
|
||||
});
|
||||
|
||||
test('returns empty string for unknown currency', () {
|
||||
@@ -67,29 +64,25 @@ void main() {
|
||||
|
||||
group('getAddressFromStringPattern', () {
|
||||
test('returns correct pattern for Bitcoin', () {
|
||||
final pattern =
|
||||
AddressValidator.getAddressFromStringPattern(CryptoCurrency.btc);
|
||||
final pattern = AddressValidator.getAddressFromStringPattern(CryptoCurrency.btc);
|
||||
expect(pattern, isNotNull);
|
||||
expect(pattern, contains('(bc|tb)1q'));
|
||||
});
|
||||
|
||||
test('returns correct pattern for Ethereum', () {
|
||||
final pattern =
|
||||
AddressValidator.getAddressFromStringPattern(CryptoCurrency.eth);
|
||||
final pattern = AddressValidator.getAddressFromStringPattern(CryptoCurrency.eth);
|
||||
expect(pattern, isNotNull);
|
||||
expect(pattern, contains('0x[0-9a-zA-Z]+'));
|
||||
});
|
||||
|
||||
test('returns correct pattern for Monero', () {
|
||||
final pattern =
|
||||
AddressValidator.getAddressFromStringPattern(CryptoCurrency.xmr);
|
||||
final pattern = AddressValidator.getAddressFromStringPattern(CryptoCurrency.xmr);
|
||||
expect(pattern, isNotNull);
|
||||
expect(pattern, contains('(4[0-9a-zA-Z]{94})'));
|
||||
});
|
||||
|
||||
test('returns null for unsupported currency', () {
|
||||
final pattern =
|
||||
AddressValidator.getAddressFromStringPattern(CryptoCurrency.dash);
|
||||
final pattern = AddressValidator.getAddressFromStringPattern(CryptoCurrency.dash);
|
||||
expect(pattern, isNull);
|
||||
});
|
||||
});
|
||||
@@ -97,8 +90,7 @@ void main() {
|
||||
group('validation', () {
|
||||
test('validates valid Bitcoin address', () {
|
||||
final validator = AddressValidator(type: CryptoCurrency.btc);
|
||||
expect(validator.isValid('bc1qhg4l43pmq5v5atmtlr7gnwyuxs043cvrut5hkq'),
|
||||
isTrue);
|
||||
expect(validator.isValid('bc1qhg4l43pmq5v5atmtlr7gnwyuxs043cvrut5hkq'), isTrue);
|
||||
expect(validator.isValid('3AD1Btx1MzYGmdpNpeujCfuvU5SsU2LX88'), isTrue);
|
||||
expect(validator.isValid('1HARAhFcvz8ZQp5MhnLFeUynC4bkha3Hv8'), isTrue);
|
||||
});
|
||||
@@ -106,8 +98,7 @@ void main() {
|
||||
test('rejects invalid Bitcoin address', () {
|
||||
final validator = AddressValidator(type: CryptoCurrency.btc);
|
||||
expect(validator.isValid('invalid_address'), isFalse);
|
||||
expect(validator.isValid('bc1qhg4l43pmq5v5atmtlr7gnwyuxs043CakeWallet'),
|
||||
isFalse);
|
||||
expect(validator.isValid('bc1qhg4l43pmq5v5atmtlr7gnwyuxs043CakeWallet'), isFalse);
|
||||
});
|
||||
|
||||
test('validates valid Ethereum address', () {
|
||||
@@ -119,8 +110,8 @@ void main() {
|
||||
test('rejects invalid Ethereum address', () {
|
||||
final validator = AddressValidator(type: CryptoCurrency.eth);
|
||||
expect(validator.isValid('invalid_address'), isFalse);
|
||||
expect(validator.isValid('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc'),
|
||||
isFalse); // Too short
|
||||
expect(
|
||||
validator.isValid('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc'), isFalse); // Too short
|
||||
expect(validator.isValid('C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'),
|
||||
isFalse); // Missing 0x prefix
|
||||
});
|
||||
@@ -144,8 +135,7 @@ void main() {
|
||||
|
||||
test('validates valid Litecoin address', () {
|
||||
final validator = AddressValidator(type: CryptoCurrency.ltc);
|
||||
expect(validator.isValid('ltc1qzvxlvlk8wsmue0np20eh3d3qxsusx9jstf8qw8'),
|
||||
isTrue);
|
||||
expect(validator.isValid('ltc1qzvxlvlk8wsmue0np20eh3d3qxsusx9jstf8qw8'), isTrue);
|
||||
expect(
|
||||
validator.isValid(
|
||||
'ltcmweb1qqt9hqch2d0vfdsvt4tf27gullem2tcd57xxrvta9xwvfmwdkn4927q6d8sq6ftw7lkqdkr5g36eqn7w06edgq8tz7gy0nv5d4lhajctkzuath23a'),
|
||||
@@ -155,15 +145,20 @@ void main() {
|
||||
test('rejects invalid Litecoin address', () {
|
||||
final validator = AddressValidator(type: CryptoCurrency.ltc);
|
||||
expect(validator.isValid('invalid_address'), isFalse);
|
||||
expect(
|
||||
validator.isValid('ltc1qzvxlvlk8wsmue0np20eh3d3qxsusxCakeWallet'),
|
||||
isFalse);
|
||||
expect(validator.isValid('ltc1qzvxlvlk8wsmue0np20eh3d3qxsusxCakeWallet'), isFalse);
|
||||
});
|
||||
});
|
||||
|
||||
group('silentPaymentAddressPattern', () {
|
||||
group('silentPaymentAddressPatternMainnet', () {
|
||||
test('returns a non-empty pattern', () {
|
||||
final pattern = AddressValidator.silentPaymentAddressPattern;
|
||||
final pattern = AddressValidator.silentPaymentAddressPatternMainnet;
|
||||
expect(pattern, isNotEmpty);
|
||||
});
|
||||
});
|
||||
|
||||
group('silentPaymentAddressPatternTestnet', () {
|
||||
test('returns a non-empty pattern', () {
|
||||
final pattern = AddressValidator.silentPaymentAddressPatternTestnet;
|
||||
expect(pattern, isNotEmpty);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user