feat: Add token to recieve address QR and link to pay anything flow on Solana and Tron, also fix token not showing on evm wallets QRs when scanned from dashboard (#3320)

This commit is contained in:
David Adegoke
2026-06-10 19:31:34 +02:00
committed by GitHub
parent 303143052a
commit 99f96b0a78
5 changed files with 64 additions and 31 deletions
+22 -4
View File
@@ -176,14 +176,23 @@ class PolygonURI extends PaymentURI {
}
class SolanaURI extends PaymentURI {
SolanaURI({required super.amount, required super.address});
SolanaURI({required super.amount, required super.address, this.contractAddress});
final String? contractAddress;
@override
String toString() {
var base = 'solana:$address';
final params = <String>[];
if (amount.isNotEmpty) {
base += '?amount=${amount.replaceAll(',', '.')}';
params.add('amount=${amount.replaceAll(',', '.')}');
}
if (contractAddress != null && contractAddress!.isNotEmpty) {
params.add('spl-token=$contractAddress');
}
if (params.isNotEmpty) {
base += '?${params.join('&')}';
}
return base;
@@ -191,14 +200,23 @@ class SolanaURI extends PaymentURI {
}
class TronURI extends PaymentURI {
TronURI({required super.amount, required super.address});
TronURI({required super.amount, required super.address, this.contractAddress});
final String? contractAddress;
@override
String toString() {
var base = 'tron:$address';
final params = <String>[];
if (amount.isNotEmpty) {
base += '?amount=${amount.replaceAll(',', '.')}';
params.add('amount=${amount.replaceAll(',', '.')}');
}
if (contractAddress != null && contractAddress!.isNotEmpty) {
params.add('token=$contractAddress');
}
if (params.isNotEmpty) {
base += '?${params.join('&')}';
}
return base;
+8
View File
@@ -228,6 +228,14 @@ class _NewSendPageState extends State<NewSendPage> {
_addressControllers[0].text = widget.initialPaymentRequest!.address;
_amountControllers[0].text = widget.initialPaymentRequest!.amount;
_memoControllers[0].text = widget.initialPaymentRequest!.note;
final contractAddress = widget.initialPaymentRequest!.contractAddress;
if (contractAddress != null && contractAddress.isNotEmpty) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
widget.sendViewModel.fetchTokenForContractAddress(contractAddress);
}
});
}
}
/// if the current wallet doesn't match the one in the qr code
+10
View File
@@ -60,6 +60,16 @@ class PaymentRequest {
address = paymentUri.address;
amount = paymentUri.amount;
contractAddress = paymentUri.contractAddress;
} else if (scheme == "tron") {
final token = uri.queryParameters['token'];
if (token != null && token.isNotEmpty) {
contractAddress = token;
}
} else if (scheme == "solana") {
final splToken = uri.queryParameters['spl-token'];
if (splToken != null && splToken.isNotEmpty) {
contractAddress = splToken;
}
}
}
+8 -27
View File
@@ -144,7 +144,7 @@ class TokenUtilities {
return result;
}
/// Finds a token by address across wallets depending on [walletType]
/// Finds a token by address for the given [walletType].
/// - EVM chains: match by contractAddress
/// - Solana: match by mintAddress
/// - Tron: match by contractAddress
@@ -152,34 +152,15 @@ class TokenUtilities {
required WalletType walletType,
required String address,
}) async {
if(address.isEmpty) return null;
if (address.isEmpty) return null;
final lower = address.toLowerCase();
switch (walletType) {
case WalletType.ethereum:
case WalletType.polygon:
case WalletType.base:
case WalletType.arbitrum:
case WalletType.bsc:
final tokens = await loadAllUniqueEvmTokens();
for (final t in tokens) {
if (t.contractAddress.toLowerCase() == lower) return t;
}
return null;
case WalletType.solana:
final solTokens = await loadAllUniqueSolTokens();
for (final t in solTokens) {
if (t.mintAddress.toLowerCase() == lower) return t;
}
return null;
case WalletType.tron:
final tronTokens = await loadAllUniqueTronTokens();
for (final t in tronTokens) {
if (t.contractAddress.toLowerCase() == lower) return t;
}
return null;
default:
return null;
final tokens = await getAvailableTokensForNetwork(walletType);
for (final t in tokens) {
if (t is Erc20Token && t.contractAddress.toLowerCase() == lower) return t;
if (t is SPLToken && t.mintAddress.toLowerCase() == lower) return t;
if (t is TronToken && t.contractAddress.toLowerCase() == lower) return t;
}
return null;
}
static Future<Box<Erc20Token>> _openEvmTokensBoxFor(WalletInfo walletInfo) async {
@@ -34,6 +34,8 @@ import 'package:cw_core/currency.dart';
import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/erc20_token.dart';
import 'package:cw_core/payment_uris.dart';
import 'package:cw_core/spl_token.dart';
import 'package:cw_core/tron_token.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:mobx/mobx.dart';
@@ -202,6 +204,20 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
amount: _amount,
contractAddress: (tokenCurrency as Erc20Token).contractAddress);
}
if (tokenCurrency is TronToken && wallet.type == WalletType.tron) {
return TronURI(
amount: _amount,
address: wallet.walletAddresses.address,
contractAddress: (tokenCurrency as TronToken).contractAddress,
);
}
if (tokenCurrency is SPLToken && wallet.type == WalletType.solana) {
return SolanaURI(
amount: _amount,
address: wallet.walletAddresses.address,
contractAddress: (tokenCurrency as SPLToken).mintAddress,
);
}
if (isLightning && _lnPaymentRequest != null) return _lnPaymentRequest!;
return wallet.walletAddresses.getPaymentUri(_amount);
}