mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
Merge branch 'dev' into cw-1440-new-buy-sell-flow
This commit is contained in:
@@ -11,4 +11,9 @@ class Debounce {
|
||||
_timer?.cancel();
|
||||
_timer = Timer(duration, action);
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
_timer?.cancel();
|
||||
_timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,41 @@
|
||||
import 'package:cw_core/amount/amount_sanitizer.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/lnurl.dart';
|
||||
import 'package:cw_core/payment_uris.dart';
|
||||
import 'package:cake_wallet/nano/nano.dart';
|
||||
import "package:cake_wallet/nano/nano.dart";
|
||||
import "package:cw_core/amount/amount_sanitizer.dart";
|
||||
import "package:cw_core/amount/money.dart";
|
||||
import "package:cw_core/crypto_currency.dart";
|
||||
import "package:cw_core/format_fixed.dart";
|
||||
import "package:cw_core/lnurl.dart";
|
||||
import "package:cw_core/payment_uris.dart";
|
||||
import "package:cw_core/utils/print_verbose.dart";
|
||||
|
||||
class PaymentRequest {
|
||||
PaymentRequest(this.address, this.amount, this.note, this.scheme, this.pjUri,
|
||||
{this.callbackUrl, this.callbackMessage, this.contractAddress});
|
||||
PaymentRequest(
|
||||
this.address,
|
||||
this.amount,
|
||||
this.note,
|
||||
this.scheme,
|
||||
this.pjUri, {
|
||||
this.callbackUrl,
|
||||
this.callbackMessage,
|
||||
this.contractAddress,
|
||||
this.chainId,
|
||||
this.rawTokenAmount,
|
||||
});
|
||||
|
||||
factory PaymentRequest.fromString(String input) {
|
||||
try {
|
||||
return PaymentRequest.fromBolt11(input);
|
||||
} catch (_) {
|
||||
return PaymentRequest.fromUri(Uri.parse(input));
|
||||
try {
|
||||
return PaymentRequest.fromUri(Uri.parse(input));
|
||||
} catch (_) {
|
||||
return PaymentRequest(input, "", "", "", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
factory PaymentRequest.fromBolt11(String invoice) {
|
||||
final amount = getBolt11Amount(invoice) ?? Money.zero(CryptoCurrency.btcln);
|
||||
return PaymentRequest(invoice, amount.toString(), '', 'lightning', null);
|
||||
return PaymentRequest(invoice, amount.toString(), "", "lightning", null);
|
||||
}
|
||||
|
||||
factory PaymentRequest.fromUri(Uri? uri) {
|
||||
@@ -32,13 +48,15 @@ class PaymentRequest {
|
||||
String? callbackMessage;
|
||||
String? pjUri;
|
||||
String? contractAddress;
|
||||
int? chainId;
|
||||
String? rawTokenAmount;
|
||||
|
||||
if (uri != null) {
|
||||
if (uri.queryParameters['pj'] != null) {
|
||||
if (uri.queryParameters["pj"] != null) {
|
||||
pjUri = uri.toString();
|
||||
}
|
||||
|
||||
address = uri.queryParameters['address'] ?? uri.path;
|
||||
address = uri.queryParameters["address"] ?? uri.path;
|
||||
try {
|
||||
final lnAmount = getBolt11Amount(uri.path) ?? Money.zero(CryptoCurrency.btcln);
|
||||
|
||||
@@ -47,27 +65,30 @@ class PaymentRequest {
|
||||
}
|
||||
} catch (_) {}
|
||||
if (amount.isEmpty) {
|
||||
amount = uri.queryParameters['tx_amount'] ?? uri.queryParameters['amount'] ?? "";
|
||||
amount = uri.queryParameters["tx_amount"] ?? uri.queryParameters["amount"] ?? "";
|
||||
}
|
||||
note = uri.queryParameters['tx_description'] ?? uri.queryParameters['message'] ?? "";
|
||||
note = uri.queryParameters["tx_description"] ?? uri.queryParameters["message"] ?? "";
|
||||
scheme = uri.scheme;
|
||||
callbackUrl = uri.queryParameters['callback'];
|
||||
callbackMessage = uri.queryParameters['callbackMessage'];
|
||||
walletType = uri.queryParameters['type'];
|
||||
callbackUrl = uri.queryParameters["callback"];
|
||||
callbackMessage = uri.queryParameters["callbackMessage"];
|
||||
walletType = uri.queryParameters["type"];
|
||||
|
||||
if (scheme == "ethereum") {
|
||||
final paymentUri = ERC681URI.fromUri(uri);
|
||||
final hasExplicitChainId = uri.path.contains("@");
|
||||
|
||||
address = paymentUri.address;
|
||||
amount = paymentUri.amount;
|
||||
contractAddress = paymentUri.contractAddress;
|
||||
chainId = hasExplicitChainId ? paymentUri.chainId : null;
|
||||
rawTokenAmount = paymentUri.rawTokenAmount;
|
||||
} else if (scheme == "tron") {
|
||||
final token = uri.queryParameters['token'];
|
||||
final token = uri.queryParameters["token"];
|
||||
if (token != null && token.isNotEmpty) {
|
||||
contractAddress = token;
|
||||
}
|
||||
} else if (scheme == "solana") {
|
||||
final splToken = uri.queryParameters['spl-token'];
|
||||
final splToken = uri.queryParameters["spl-token"];
|
||||
if (splToken != null && splToken.isNotEmpty) {
|
||||
contractAddress = splToken;
|
||||
}
|
||||
@@ -99,6 +120,8 @@ class PaymentRequest {
|
||||
callbackUrl: callbackUrl,
|
||||
callbackMessage: callbackMessage,
|
||||
contractAddress: contractAddress,
|
||||
chainId: chainId,
|
||||
rawTokenAmount: rawTokenAmount,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -110,23 +133,53 @@ class PaymentRequest {
|
||||
final String? callbackUrl;
|
||||
final String? callbackMessage;
|
||||
final String? contractAddress;
|
||||
final int? chainId;
|
||||
final String? rawTokenAmount;
|
||||
|
||||
String? resolveTokenAmount(CryptoCurrency token) {
|
||||
if (amount.isNotEmpty) {
|
||||
return amount;
|
||||
}
|
||||
|
||||
if (rawTokenAmount == null || rawTokenAmount!.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final raw = BigInt.tryParse(rawTokenAmount!);
|
||||
if (raw == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 1e9 whole tokens is beyond any real payment, so a larger result means the QR
|
||||
// came from an old app version that encoded with 18 decimals regardless of token.
|
||||
if (token.decimals != 18 && raw > BigInt.from(10).pow(token.decimals + 9)) {
|
||||
printV("resolveTokenAmount: reading $rawTokenAmount with legacy 18 decimals");
|
||||
return formatFixed(raw, 18);
|
||||
}
|
||||
|
||||
return formatFixed(raw, token.decimals);
|
||||
}
|
||||
|
||||
/// Checks if the amount string is already in a usable format (e.g., "123.45") and doesn't need to be converted from raw format.
|
||||
///
|
||||
/// This was causing an error for us when we scan Nano QRs with amounts in them, the amounts are already in usable format so when the parsing was done, it returns 0 wrongly.
|
||||
static bool _isAlreadyUsableAmount(String amount) {
|
||||
if (amount.isEmpty) return false;
|
||||
if (amount.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final parsed = double.tryParse(amount.sanitized());
|
||||
if (parsed == null) return false;
|
||||
|
||||
// Check if the amount contains a decimal point and is a reasonable number,
|
||||
// it's likely already in usable format rather than raw format
|
||||
// Raw amounts are typically very large integers without decimal points
|
||||
if (amount.contains('.') && parsed > 0 && parsed < 1000000000) return true;
|
||||
if (parsed == null) {
|
||||
return false;
|
||||
}
|
||||
if (amount.contains(".") && parsed > 0 && parsed < 1000000000) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If it's a small integer (less than 1 billion), it's likely already usable
|
||||
if (parsed == parsed.toInt() && parsed < 1000000000) return true;
|
||||
if (parsed == parsed.toInt() && parsed < 1000000000) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -222,6 +222,24 @@ class TokenUtilities {
|
||||
return null;
|
||||
}
|
||||
|
||||
static String? findSolanaTokenMint(CryptoCurrency currency) {
|
||||
if (currency is SPLToken) return currency.mintAddress;
|
||||
|
||||
for (final token in loadDefaultSolTokensForSwap()) {
|
||||
if (_matchesCurrency(token, currency)) return token.mintAddress;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static String? findTronTokenContract(CryptoCurrency currency) {
|
||||
if (currency is TronToken) return currency.contractAddress;
|
||||
|
||||
for (final token in loadDefaultTronTokensForSwap()) {
|
||||
if (_matchesCurrency(token, currency)) return token.contractAddress;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static bool isNativeToken(CryptoCurrency currency) {
|
||||
final title = currency.title.toLowerCase();
|
||||
final tag = currency.tag?.toLowerCase();
|
||||
@@ -289,9 +307,7 @@ class TokenUtilities {
|
||||
final allTokens = <CryptoCurrency>[baseCurrency];
|
||||
final addedAddresses = <String>{};
|
||||
|
||||
// Handle EVM networks
|
||||
if (isEVMCompatibleChain(network)) {
|
||||
// First, collect all user tokens
|
||||
final userTokens = await _getUserTokensForNetwork(baseCurrency);
|
||||
for (final token in userTokens) {
|
||||
if (token is Erc20Token) {
|
||||
@@ -302,7 +318,17 @@ class TokenUtilities {
|
||||
}
|
||||
}
|
||||
|
||||
// Then add tokens from CryptoCurrency.all that don't duplicate user tokens
|
||||
if (evm != null) {
|
||||
final chainId = evm!.getChainIdByWalletType(network);
|
||||
for (final token in evm!.getDefaultTokensByChainId(chainId)) {
|
||||
final address = token.contractAddress.toLowerCase();
|
||||
if (addedAddresses.add(address)) {
|
||||
allTokens.add(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add tokens from CryptoCurrency.all that don't duplicate user tokens
|
||||
for (final currency in CryptoCurrency.all) {
|
||||
// Match by tag for POL/BASE, match by title==tag for ETH
|
||||
final matches = (baseCurrency.tag == null && baseCurrency.title == currency.tag) ||
|
||||
@@ -320,10 +346,7 @@ class TokenUtilities {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Solana network
|
||||
else if (network == WalletType.solana) {
|
||||
} else if (network == WalletType.solana) {
|
||||
final userSolTokens = await loadAllUniqueSolTokens();
|
||||
for (final token in userSolTokens) {
|
||||
final mintAddress = token.mintAddress.toLowerCase();
|
||||
@@ -332,6 +355,13 @@ class TokenUtilities {
|
||||
}
|
||||
}
|
||||
|
||||
for (final token in loadDefaultSolTokensForSwap()) {
|
||||
final mintAddress = token.mintAddress.toLowerCase();
|
||||
if (addedAddresses.add(mintAddress)) {
|
||||
allTokens.add(token);
|
||||
}
|
||||
}
|
||||
|
||||
for (final currency in CryptoCurrency.all) {
|
||||
if (currency.tag?.toLowerCase() == 'sol') {
|
||||
if (currency is SPLToken) {
|
||||
@@ -356,6 +386,13 @@ class TokenUtilities {
|
||||
}
|
||||
}
|
||||
|
||||
for (final token in loadDefaultTronTokensForSwap()) {
|
||||
final contractAddress = token.contractAddress.toLowerCase();
|
||||
if (addedAddresses.add(contractAddress)) {
|
||||
allTokens.add(token);
|
||||
}
|
||||
}
|
||||
|
||||
for (final currency in CryptoCurrency.all) {
|
||||
if (currency.tag?.toLowerCase() == 'trx') {
|
||||
if (currency is TronToken) {
|
||||
|
||||
Reference in New Issue
Block a user