Merge branch 'cake-tech:dev' into dev

This commit is contained in:
BSN ∞/21M
2026-06-04 19:00:00 +02:00
committed by GitHub
135 changed files with 4891 additions and 689 deletions
-13
View File
@@ -13,19 +13,6 @@ analyzer:
cw_monero/ios/External/**,
cw_shared_external/**,
shared_external/**,
lib/bitcoin/cw_bitcoin.dart,
lib/bitcoin_cash/cw_bitcoin_cash.dart,
lib/ethereum/cw_ethereum.dart,
lib/haven/cw_haven.dart,
lib/monero/cw_monero.dart,
lib/nano/cw_nano.dart,
lib/polygon/cw_polygon.dart,
lib/solana/cw_solana.dart,
lib/tron/cw_tron.dart,
lib/wownero/cw_wownero.dart,
lib/zano/cw_zano.dart,
lib/base/cw_base.dart,
lib/arbitrum/cw_arbitrum.dart,
]
language:
strict-casts: true
@@ -198,6 +198,7 @@ class LightningWallet {
final res = await sdk.sendPayment(
request: SendPaymentRequest(prepareResponse: prepareResponse));
printV(res.payment.status.name);
return res.payment.id;
} on SdkError_SparkError catch (e) {
if (e.field0.contains("AlreadyExists")) {
throw Exception("Invoice already paid");
@@ -239,6 +240,7 @@ class LightningWallet {
final res =
await sdk.lnurlPay(request: LnurlPayRequest(prepareResponse: prepareResponse));
printV(res.payment.status.name);
return res.payment.id;
},
);
} else if (inputType is InputType_BitcoinAddress) {
@@ -278,8 +280,9 @@ class LightningWallet {
commitOverride: () async {
final options =
SendPaymentOptions.bitcoinAddress(confirmationSpeed: onchainConfirmationSpeed);
await sdk.sendPayment(
final res = await sdk.sendPayment(
request: SendPaymentRequest(prepareResponse: prepareResponse, options: options));
return res.payment.id;
},
);
}
@@ -13,11 +13,11 @@ class PendingLightningTransaction with PendingTransaction {
final int amount;
final int fee;
final bool isSendAll;
Future<void> Function() commitOverride;
Future<String> Function() commitOverride;
final List<void Function()> _listeners =[];
@override
final String id;
String id;
@override
String get hex => "";
@@ -36,7 +36,7 @@ class PendingLightningTransaction with PendingTransaction {
@override
Future<void> commit() async {
await commitOverride.call();
id = await commitOverride.call();
_listeners.forEach((e) => e.call());
}
+21 -16
View File
@@ -1,4 +1,5 @@
import 'package:cw_core/currency.dart';
import 'package:cw_core/currency_groups.dart';
import 'package:cw_core/enumerable_item.dart';
import 'package:collection/collection.dart';
import 'package:cw_core/format_fixed.dart';
@@ -17,6 +18,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
this.chainIconPath,
this.enabled = false,
this.isPotentialScam = false,
this.groups = const {},
}) : super(title: title, raw: raw);
final String name;
@@ -29,6 +31,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
final int decimals;
final bool enabled;
final bool isPotentialScam;
final Set<String> groups;
set enabled(bool value) => this.enabled = value;
@@ -154,15 +157,15 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
static const bch = CryptoCurrency(title: 'BCH', fullName: 'Bitcoin Cash', raw: 2, name: 'bch', iconPath: 'assets/new-ui/crypto_full_icons/bitcoin-cash.svg', decimals: 8, flatIconPath: "assets/new-ui/balance_card_icons/bitcoin_cash.svg");
static const bnb = CryptoCurrency(title: 'BNB', tag: 'BSC', fullName: 'BNB', raw: 3, name: 'bnb', iconPath: 'assets/new-ui/crypto_full_icons/bnb.svg', decimals: 18, flatIconPath: "assets/new-ui/balance_card_icons/bnb.svg", chainIconPath: "assets/new-ui/chain_badges/bnb.svg");
static const btc = CryptoCurrency(title: 'BTC', fullName: 'Bitcoin', raw: 4, name: 'btc', iconPath: 'assets/new-ui/crypto_full_icons/bitcoin.svg' ,decimals: 8, flatIconPath: "assets/new-ui/balance_card_icons/bitcoin.svg");
static const dai = CryptoCurrency(title: 'DAI', tag: 'ETH', fullName: 'Dai', raw: 5, name: 'dai', iconPath: 'assets/new-ui/crypto_full_icons/dai.svg', decimals: 18);
static const dai = CryptoCurrency(title: 'DAI', tag: 'ETH', fullName: 'Dai', raw: 5, name: 'dai', iconPath: 'assets/new-ui/crypto_full_icons/dai.svg', decimals: 18, groups: const {CurrencyGroups.stablecoin});
static const dash = CryptoCurrency(title: 'DASH', fullName: 'Dash', raw: 6, name: 'dash', iconPath: 'assets/images/dash_icon.png', decimals: 8);
static const eos = CryptoCurrency(title: 'EOS', fullName: 'EOS', raw: 7, name: 'eos', iconPath: 'assets/images/eos_icon.png', decimals: 4);
static const eth = CryptoCurrency(title: 'ETH', fullName: 'Ethereum', raw: 8, name: 'eth', iconPath: 'assets/new-ui/crypto_full_icons/ethereum.svg', decimals: 18, flatIconPath: "assets/new-ui/balance_card_icons/ethereum.svg", chainIconPath: "assets/new-ui/chain_badges/ethereum.svg");
static const ltc = CryptoCurrency(title: 'LTC', fullName: 'Litecoin', raw: 9, name: 'ltc', iconPath: 'assets/new-ui/crypto_full_icons/litecoin.svg', decimals: 8, flatIconPath: "assets/new-ui/balance_card_icons/litecoin.svg", chainIconPath: "assets/new-ui/chain_badges/mweb.svg");
static const nano = CryptoCurrency(title: 'XNO', fullName: 'Nano', raw: 10, name: 'xno', iconPath: 'assets/new-ui/crypto_full_icons/nano.svg', decimals: 30, flatIconPath: "assets/new-ui/balance_card_icons/nano.svg");
static const trx = CryptoCurrency(title: 'TRX', fullName: 'TRON', raw: 11, name: 'trx', iconPath: 'assets/new-ui/crypto_full_icons/tron.svg', decimals: 6, flatIconPath: "assets/new-ui/balance_card_icons/tron.svg", chainIconPath: "assets/new-ui/chain_badges/tron.svg");
static const usdt = CryptoCurrency(title: 'USDT', tag: 'OMNI', fullName: 'Tether', raw: 12, name: 'usdt', iconPath: 'assets/new-ui/crypto_full_icons/usdt.svg', decimals: 6);
static const usdterc20 = CryptoCurrency(title: 'USDT', tag: 'ETH', fullName: 'Tether', raw: 13, name: 'usdterc20', iconPath: 'assets/new-ui/crypto_full_icons/usdt.svg', decimals: 6);
static const usdt = CryptoCurrency(title: 'USDT', tag: 'OMNI', fullName: 'Tether', raw: 12, name: 'usdt', iconPath: 'assets/new-ui/crypto_full_icons/usdt.svg', decimals: 6, groups: const {CurrencyGroups.stablecoin});
static const usdterc20 = CryptoCurrency(title: 'USDT', tag: 'ETH', fullName: 'Tether', raw: 13, name: 'usdterc20', iconPath: 'assets/new-ui/crypto_full_icons/usdt.svg', decimals: 6, groups: const {CurrencyGroups.stablecoin});
static const xlm = CryptoCurrency(title: 'XLM', fullName: 'Stellar', raw: 14, name: 'xlm', iconPath: 'assets/images/xlm_icon.png', decimals: 7);
static const xrp = CryptoCurrency(title: 'XRP', fullName: 'Ripple', raw: 15, name: 'xrp', iconPath: 'assets/images/xrp_icon.png', decimals: 6);
static const xhv = CryptoCurrency(title: 'XHV', fullName: 'Haven Protocol', raw: 16, name: 'xhv', iconPath: 'assets/images/xhv_logo.png', decimals: 12);
@@ -187,18 +190,18 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
static const bttc = CryptoCurrency(title: 'BTTC', tag: 'TRX', fullName: 'BitTorrent-NEW', raw: 33, name: 'bttc', iconPath: 'assets/images/btt_icon.png', decimals: 18);
static const doge = CryptoCurrency(title: 'DOGE', fullName: 'Dogecoin', raw: 34, name: 'doge', iconPath: 'assets/new-ui/crypto_full_icons/dogecoin.svg', decimals: 8, flatIconPath: "assets/new-ui/balance_card_icons/dogecoin.svg");
static const firo = CryptoCurrency(title: 'FIRO', raw: 35, name: 'firo', iconPath: 'assets/images/firo_icon.png', decimals: 8);
static const usdttrc20 = CryptoCurrency(title: 'USDT', tag: 'TRX', fullName: 'Tether', raw: 36, name: 'usdttrc20', iconPath: 'assets/new-ui/crypto_full_icons/usdt.svg', decimals: 6);
static const usdttrc20 = CryptoCurrency(title: 'USDT', tag: 'TRX', fullName: 'Tether', raw: 36, name: 'usdttrc20', iconPath: 'assets/new-ui/crypto_full_icons/usdt.svg', decimals: 6, groups: const {CurrencyGroups.stablecoin});
static const hbar = CryptoCurrency(title: 'HBAR', fullName: 'Hedera', raw: 37, name: 'hbar', iconPath: 'assets/images/hbar_icon.png', decimals: 8);
static const sc = CryptoCurrency(title: 'SC', fullName: 'Siacoin', raw: 38, name: 'sc', iconPath: 'assets/images/sc_icon.png', decimals: 16);
static const sol = CryptoCurrency(title: 'SOL', fullName: 'Solana', raw: 39, name: 'sol', iconPath: 'assets/new-ui/crypto_full_icons/solana.svg', decimals: 9, flatIconPath: "assets/new-ui/balance_card_icons/solana.svg", chainIconPath: "assets/new-ui/chain_badges/solana.svg");
static const usdc = CryptoCurrency(title: 'USDC', tag: 'ETH', fullName: 'USDC', raw: 40, name: 'usdc', iconPath: 'assets/new-ui/crypto_full_icons/usdc.svg', decimals: 6);
static const usdcsol = CryptoCurrency(title: 'USDC', tag: 'SOL', fullName: 'USDC Coin', raw: 41, name: 'usdcsol', iconPath: 'assets/new-ui/crypto_full_icons/usdc.svg', decimals: 6);
static const usdc = CryptoCurrency(title: 'USDC', tag: 'ETH', fullName: 'USDC', raw: 40, name: 'usdc', iconPath: 'assets/new-ui/crypto_full_icons/usdc.svg', decimals: 6, groups: const {CurrencyGroups.stablecoin});
static const usdcsol = CryptoCurrency(title: 'USDC', tag: 'SOL', fullName: 'USDC Coin', raw: 41, name: 'usdcsol', iconPath: 'assets/new-ui/crypto_full_icons/usdc.svg', decimals: 6, groups: const {CurrencyGroups.stablecoin});
static const zaddr = CryptoCurrency(title: 'ZZEC', tag: 'ZEC', fullName: 'Shielded Zcash', raw: 42, name: 'zaddr', iconPath: 'assets/new-ui/crypto_full_icons/zcash.svg', decimals: 8);
static const tzec = CryptoCurrency(title: 'tZEC', tag: 'ZEC', fullName: 'Transparent Zcash', raw: 43, name: 'zec', iconPath: 'assets/new-ui/crypto_full_icons/zcash.svg', decimals: 8);
static const zen = CryptoCurrency(title: 'ZEN', fullName: 'Horizen', raw: 44, name: 'zen', iconPath: 'assets/images/zen_icon.png', decimals: 8);
static const xvg = CryptoCurrency(title: 'XVG', fullName: 'Verge', raw: 45, name: 'xvg', iconPath: 'assets/images/xvg_icon.png', decimals: 8);
static const usdcpoly = CryptoCurrency(title: 'USDC', tag: 'POL', fullName: 'USDC', raw: 46, name: 'usdcpoly', iconPath: 'assets/new-ui/crypto_full_icons/usdc.svg', decimals: 6);
static const usdcpoly = CryptoCurrency(title: 'USDC', tag: 'POL', fullName: 'USDC', raw: 46, name: 'usdcpoly', iconPath: 'assets/new-ui/crypto_full_icons/usdc.svg', decimals: 6, groups: const {CurrencyGroups.stablecoin});
static const dcr = CryptoCurrency(title: 'DCR', fullName: 'Decred', raw: 47, name: 'dcr', iconPath: 'assets/new-ui/crypto_full_icons/decred.svg', decimals: 8, flatIconPath: "assets/new-ui/balance_card_icons/decred.svg");
static const kmd = CryptoCurrency(title: 'KMD', fullName: 'Komodo', raw: 48, name: 'kmd', iconPath: 'assets/images/kmd_icon.png', decimals: 8);
static const mana = CryptoCurrency(title: 'MANA', tag: 'ETH', fullName: 'Decentraland', raw: 49, name: 'mana', iconPath: 'assets/images/mana_icon.png', decimals: 18);
@@ -239,29 +242,29 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
static const dydx = CryptoCurrency(title: 'DYDX', tag: 'ETH', fullName: 'dYdX', raw: 84, name: 'dydx', iconPath: 'assets/images/dydx_icon.png', decimals: 18);
static const steth = CryptoCurrency(title: 'STETH', tag: 'ETH', fullName: 'Lido Staked Ethereum', raw: 85, name: 'steth', iconPath: 'assets/new-ui/crypto_full_icons/ethereum.svg', decimals: 18);
static const banano = CryptoCurrency(title: 'BAN', fullName: 'Banano', raw: 86, name: 'banano', iconPath: 'assets/images/nano_icon.png', decimals: 29,flatIconPath: "assets/new-ui/balance_card_icons/nano.svg");
static const usdtPoly = CryptoCurrency(title: 'USDT', tag: 'POL', fullName: 'Tether (PoS)', raw: 87, name: 'usdtpoly', iconPath: 'assets/new-ui/crypto_full_icons/usdt.svg', decimals: 6);
static const usdtPoly = CryptoCurrency(title: 'USDT', tag: 'POL', fullName: 'Tether (PoS)', raw: 87, name: 'usdtpoly', iconPath: 'assets/new-ui/crypto_full_icons/usdt.svg', decimals: 6, groups: const {CurrencyGroups.stablecoin});
static const usdcEPoly = CryptoCurrency(title: 'USDC.E', tag: 'POL', fullName: 'USDC (PoS)', raw: 88, name: 'usdcepoly', iconPath: 'assets/new-ui/crypto_full_icons/usdc.svg', decimals: 6);
static const kaspa = CryptoCurrency(title: 'KAS', fullName: 'Kaspa', raw: 89, name: 'kas', iconPath: 'assets/images/kaspa_icon.png', decimals: 8);
static const digibyte = CryptoCurrency(title: 'DGB', fullName: 'DigiByte', raw: 90, name: 'dgb', iconPath: 'assets/images/digibyte.png', decimals: 8);
static const usdtSol = CryptoCurrency(title: 'USDT', tag: 'SOL', fullName: 'Tether', raw: 91, name: 'usdtsol', iconPath: 'assets/new-ui/crypto_full_icons/usdt.svg', decimals: 6);
static const usdcTrc20 = CryptoCurrency(title: 'USDC', tag: 'TRX', fullName: 'USDC Coin', raw: 92, name: 'usdctrc20', iconPath: 'assets/new-ui/crypto_full_icons/usdc.svg', decimals: 6);
static const usdtSol = CryptoCurrency(title: 'USDT', tag: 'SOL', fullName: 'Tether', raw: 91, name: 'usdtsol', iconPath: 'assets/new-ui/crypto_full_icons/usdt.svg', decimals: 6, groups: const {CurrencyGroups.stablecoin});
static const usdcTrc20 = CryptoCurrency(title: 'USDC', tag: 'TRX', fullName: 'USDC Coin', raw: 92, name: 'usdctrc20', iconPath: 'assets/new-ui/crypto_full_icons/usdc.svg', decimals: 6, groups: const {CurrencyGroups.stablecoin});
static const tbtc = CryptoCurrency(title: 'tBTC', fullName: 'Testnet Bitcoin', raw: 93, name: 'tbtc', iconPath: 'assets/images/tbtc.png', decimals: 8);
static const wow = CryptoCurrency(title: 'WOW', fullName: 'Wownero', raw: 94, name: 'wow', iconPath: 'assets/images/crypto/wownero.svg', decimals: 11);
static const ton = CryptoCurrency(title: 'TON', fullName: 'Toncoin', raw: 95, name: 'ton', iconPath: 'assets/new-ui/crypto_full_icons/ton.svg', decimals: 8);
static const zano = CryptoCurrency(title: 'ZANO', tag: 'ZANO', fullName: 'Zano', raw: 96, name: 'zano', iconPath: 'assets/new-ui/crypto_full_icons/zano.svg', decimals: 12, flatIconPath: "assets/new-ui/balance_card_icons/zano.svg", chainIconPath: "assets/new-ui/chain_badges/zano.svg");
static const flip = CryptoCurrency(title: 'FLIP', tag: 'ETH', fullName: 'Chainflip', raw: 97, name: 'flip', iconPath: 'assets/images/flip_icon.png', decimals: 18);
static const deuro = CryptoCurrency(title: 'DEURO', tag: 'ETH', fullName: 'Decentralized Euro', raw: 98, name: 'deuro', iconPath: 'assets/images/deuro_icon.png',decimals: 18);
static const usdtbsc = CryptoCurrency(title: 'USDT', tag: 'BSC', fullName: 'USDT Binance coin', raw: 99, name: 'usdtbsc', iconPath: 'assets/new-ui/crypto_full_icons/usdt.svg', decimals: 18);
static const deuro = CryptoCurrency(title: 'DEURO', tag: 'ETH', fullName: 'Decentralized Euro', raw: 98, name: 'deuro', iconPath: 'assets/images/deuro_icon.png',decimals: 18, groups: const {CurrencyGroups.stablecoin});
static const usdtbsc = CryptoCurrency(title: 'USDT', tag: 'BSC', fullName: 'USDT Binance coin', raw: 99, name: 'usdtbsc', iconPath: 'assets/new-ui/crypto_full_icons/usdt.svg', decimals: 18, groups: const {CurrencyGroups.stablecoin});
static const ndeps = CryptoCurrency(title: 'NDEPS', tag: 'ETH', fullName: 'Native Decentralized Euro Protocol Share', raw: 100, name: 'ndeps', iconPath: 'assets/images/ndeps_icon.png', decimals: 18);
static const deps = CryptoCurrency(title: 'DEPS', tag: 'ETH', fullName: 'Decentralized Euro Protocol Share', raw: 101, name: 'deps', iconPath: 'assets/images/deps_icon.png', decimals: 18);
static const kbtc = CryptoCurrency(title: 'KBTC', tag: 'ETH', fullName: 'Kraken Wrapped Bitcoin', raw: 102, name: 'kbtc', iconPath: 'assets/images/kbtc_icon.png', decimals: 8);
static const cbbtc = CryptoCurrency(title: 'CBBTC', tag: 'ETH', fullName: 'Coinbase Wrapped BTC', raw: 103, name: 'cbbtc', iconPath: 'assets/images/cbbtc_icon.png', decimals: 8);
static const baseEth = CryptoCurrency(title: 'ETH', tag: 'BASE', fullName: 'Ethereum', raw: 104, name: 'baseth', iconPath: 'assets/new-ui/crypto_full_icons/base.svg', decimals: 18, flatIconPath: "assets/new-ui/balance_card_icons/base.svg", chainIconPath: "assets/new-ui/chain_badges/base.svg");
static const baseEth = CryptoCurrency(title: 'ETH', tag: 'BASE', fullName: 'Ethereum', raw: 104, name: 'baseth', iconPath: 'assets/new-ui/crypto_full_icons/ethereum.svg', decimals: 18, flatIconPath: "assets/new-ui/balance_card_icons/base.svg", chainIconPath: "assets/new-ui/chain_badges/base.svg");
static const usde = CryptoCurrency(title: 'USDE', tag: 'BASE', fullName: 'Ethena USDE', raw: 105, name: 'usde', iconPath: 'assets/new-ui/crypto_full_icons/usde.svg', decimals: 18);
static const arbEth = CryptoCurrency(title: 'ETH', tag: 'ARB', fullName: 'Ethereum', raw: 106, name: 'arbeth', iconPath: 'assets/new-ui/crypto_full_icons/ethereum.svg', decimals: 18, flatIconPath: "assets/new-ui/balance_card_icons/arbitrum.svg");
static const arbEth = CryptoCurrency(title: 'ETH', tag: 'ARB', fullName: 'Ethereum', raw: 106, name: 'arbeth', iconPath: 'assets/new-ui/crypto_full_icons/ethereum.svg', decimals: 18, flatIconPath: "assets/new-ui/balance_card_icons/arbitrum.svg", chainIconPath: "assets/new-ui/chain_badges/arbitrum.svg");
static const zec = CryptoCurrency(title: 'ZEC', fullName: 'Zcash', raw: 107, name: 'zec', iconPath: 'assets/new-ui/crypto_full_icons/zcash.svg', decimals: 8,flatIconPath: "assets/new-ui/balance_card_icons/zcash.svg");
static const usdcArb = CryptoCurrency(title: 'USDC', tag: 'ARB', fullName: 'USDC Coin', raw: 108, name: 'usdcarb', iconPath: 'assets/new-ui/crypto_full_icons/usdc.svg', decimals: 6);
static const usdtArb = CryptoCurrency(title: 'USDT', tag: 'ARB', fullName: 'USDT Tether', raw: 109, name: 'usdtarb', iconPath: 'assets/new-ui/crypto_full_icons/usdt.svg', decimals: 6);
static const usdcArb = CryptoCurrency(title: 'USDC', tag: 'ARB', fullName: 'USDC Coin', raw: 108, name: 'usdcarb', iconPath: 'assets/new-ui/crypto_full_icons/usdc.svg', decimals: 6, groups: const {CurrencyGroups.stablecoin});
static const usdtArb = CryptoCurrency(title: 'USDT', tag: 'ARB', fullName: 'USDT Tether', raw: 109, name: 'usdtarb', iconPath: 'assets/new-ui/crypto_full_icons/usdt.svg', decimals: 6, groups: const {CurrencyGroups.stablecoin});
static const ltcmweb = CryptoCurrency(title: 'LTC', fullName: 'Litecoin MWeb', raw: 110, name: 'ltcmweb', iconPath: 'assets/new-ui/crypto_full_icons/litecoin.svg', decimals: 8);
static final Map<int, CryptoCurrency> _rawCurrencyMap =
@@ -409,6 +412,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
int? decimals,
bool? enabled,
bool? isPotentialScam,
Set<String>? groups,
}) =>
CryptoCurrency(
title: title ?? this.title,
@@ -420,6 +424,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
decimals: decimals ?? this.decimals,
enabled: enabled ?? this.enabled,
isPotentialScam: isPotentialScam ?? this.isPotentialScam,
groups: groups ?? this.groups,
);
@override
+4
View File
@@ -0,0 +1,4 @@
abstract class CurrencyGroups {
static const stablecoin = 'stablecoin';
static const tokenizedStock = 'tokenized_stock';
}
+3
View File
@@ -47,6 +47,7 @@ class SPLToken extends CryptoCurrency with HiveObjectMixin {
this.tag = 'SOL',
bool enabled = true,
this.isPotentialScam = false,
Set<String> groups = const {},
}) : _enabled = enabled,
super(
name: mint.toLowerCase(),
@@ -56,6 +57,7 @@ class SPLToken extends CryptoCurrency with HiveObjectMixin {
iconPath: iconPath,
decimals: decimal,
isPotentialScam: isPotentialScam,
groups: groups,
);
factory SPLToken.fromMetadata({
@@ -101,6 +103,7 @@ class SPLToken extends CryptoCurrency with HiveObjectMixin {
tag: other.tag,
iconPath: icon,
isPotentialScam: other.isPotentialScam,
groups: other.groups,
);
static const typeId = SPL_TOKEN_TYPE_ID;
+7 -1
View File
@@ -1,5 +1,6 @@
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:hive/hive.dart';
part 'wallet_type.g.dart';
@@ -332,13 +333,18 @@ WalletType? _cryptoCurrencyToWalletType(CryptoCurrency type) {
}
WalletType? cryptoCurrencyOrTokenToWalletType(CryptoCurrency type) {
try {
if(type.tag == CryptoCurrency.bnb.tag) {
return _cryptoCurrencyToWalletType(CryptoCurrency.bnb);
}
if(type.tag != null && ![CryptoCurrency.btcln.tag, CryptoCurrency.bnb.tag].contains(type.tag)) {
return _cryptoCurrencyToWalletType(CryptoCurrency.fromString(type.tag!));
} else {
return _cryptoCurrencyToWalletType(type);
}
} catch (e) {
printV(e);
return null;
}
}
+4
View File
@@ -1289,6 +1289,10 @@ abstract class EVMChainWalletBase
!_hasEvmTokenContractAddress(existingTxInfo)) {
result[transactionModel.hash] = newTxInfo;
}
else if (existingTxInfo.isPending) {
result[transactionModel.hash] = newTxInfo;
}
}
return result;
+4 -4
View File
@@ -982,8 +982,8 @@ packages:
dependency: "direct main"
description:
path: trezor-flutter
ref: becd18ac474d343d94b6191d89fad43270d2a02e
resolved-ref: becd18ac474d343d94b6191d89fad43270d2a02e
ref: d1289dfa4247a93ee57342dfd249957b339915ef
resolved-ref: d1289dfa4247a93ee57342dfd249957b339915ef
url: "https://github.com/cake-tech/trezor-flutter.git"
source: git
version: "0.1.0"
@@ -991,8 +991,8 @@ packages:
dependency: transitive
description:
path: trezor_usb_transport
ref: becd18ac474d343d94b6191d89fad43270d2a02e
resolved-ref: becd18ac474d343d94b6191d89fad43270d2a02e
ref: d1289dfa4247a93ee57342dfd249957b339915ef
resolved-ref: d1289dfa4247a93ee57342dfd249957b339915ef
url: "https://github.com/cake-tech/trezor-flutter.git"
source: git
version: "0.0.1"
+65
View File
@@ -1,4 +1,5 @@
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/currency_groups.dart';
import 'package:cw_core/spl_token.dart';
class DefaultSPLTokens {
@@ -116,6 +117,7 @@ class DefaultSPLTokens {
mint: 'abtx',
enabled: false,
iconPath: 'assets/images/stocks/abtx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'AbbVie xStock',
@@ -125,6 +127,7 @@ class DefaultSPLTokens {
mint: 'abbvx',
enabled: false,
iconPath: 'assets/images/stocks/abbv.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Accenture xStock',
@@ -134,6 +137,7 @@ class DefaultSPLTokens {
mint: 'acnx',
enabled: false,
iconPath: 'assets/images/stocks/acnx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Alphabet xStock',
@@ -143,6 +147,7 @@ class DefaultSPLTokens {
mint: 'googlx',
enabled: false,
iconPath: 'assets/images/stocks/googlx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Amazon xStock',
@@ -152,6 +157,7 @@ class DefaultSPLTokens {
mint: 'amznx',
enabled: false,
iconPath: 'assets/images/stocks/amznx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Amber xStock',
@@ -161,6 +167,7 @@ class DefaultSPLTokens {
mint: 'ambrx',
enabled: false,
iconPath: 'assets/images/stocks/ambrx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Apple xStock',
@@ -170,6 +177,7 @@ class DefaultSPLTokens {
mint: 'aaplx',
enabled: false,
iconPath: 'assets/images/stocks/apple.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'AppLovin xStock',
@@ -179,6 +187,7 @@ class DefaultSPLTokens {
mint: 'appx',
enabled: false,
iconPath: 'assets/images/stocks/appx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'AstraZeneca xStock',
@@ -188,6 +197,7 @@ class DefaultSPLTokens {
mint: 'aznx',
enabled: false,
iconPath: 'assets/images/stocks/aznx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Bank of America xStock',
@@ -197,6 +207,7 @@ class DefaultSPLTokens {
mint: 'bacx',
enabled: false,
iconPath: 'assets/images/stocks/bacx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Berkshire Hathaway xStock',
@@ -206,6 +217,7 @@ class DefaultSPLTokens {
mint: 'brkbx',
enabled: false,
iconPath: 'assets/images/stocks/brkbx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Broadcom xStock',
@@ -215,6 +227,7 @@ class DefaultSPLTokens {
mint: 'avgox',
enabled: false,
iconPath: 'assets/images/stocks/avgox.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Chevron xStock',
@@ -224,6 +237,7 @@ class DefaultSPLTokens {
mint: 'cvxx',
enabled: false,
iconPath: 'assets/images/stocks/cvxx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Circle xStock',
@@ -233,6 +247,7 @@ class DefaultSPLTokens {
mint: 'crclx',
enabled: false,
iconPath: 'assets/images/stocks/crclx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Cisco xStock',
@@ -242,6 +257,7 @@ class DefaultSPLTokens {
mint: 'cscx',
enabled: false,
iconPath: 'assets/images/stocks/cscox.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Coca-Cola xStock',
@@ -251,6 +267,7 @@ class DefaultSPLTokens {
mint: 'kox',
enabled: false,
iconPath: 'assets/images/stocks/kox.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Coinbase xStock',
@@ -260,6 +277,7 @@ class DefaultSPLTokens {
mint: 'coinx',
enabled: false,
iconPath: 'assets/images/stocks/coinx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Comcast xStock',
@@ -269,6 +287,7 @@ class DefaultSPLTokens {
mint: 'cmcsax',
enabled: false,
iconPath: 'assets/images/stocks/cmcsax.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'CrowdStrike xStock',
@@ -278,6 +297,7 @@ class DefaultSPLTokens {
mint: 'crwdx',
enabled: false,
iconPath: 'assets/images/stocks/crwdx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Danaher xStock',
@@ -287,6 +307,7 @@ class DefaultSPLTokens {
mint: 'dhrx',
enabled: false,
iconPath: 'assets/images/stocks/dhrx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'DFDV xStock',
@@ -296,6 +317,7 @@ class DefaultSPLTokens {
mint: 'dfdvx',
enabled: false,
iconPath: 'assets/images/stocks/dfdvx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Eli Lilly xStock',
@@ -305,6 +327,7 @@ class DefaultSPLTokens {
mint: 'llyx',
enabled: false,
iconPath: 'assets/images/stocks/llyx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Exxon Mobil xStock',
@@ -314,6 +337,7 @@ class DefaultSPLTokens {
mint: 'xomx',
enabled: false,
iconPath: 'assets/images/stocks/xomx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Gamestop xStock',
@@ -323,6 +347,7 @@ class DefaultSPLTokens {
mint: 'gmex',
enabled: false,
iconPath: 'assets/images/stocks/gmex.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Gold xStock',
@@ -332,6 +357,7 @@ class DefaultSPLTokens {
mint: 'gldx',
enabled: false,
iconPath: 'assets/images/stocks/gldx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Goldman Sachs xStock',
@@ -341,6 +367,7 @@ class DefaultSPLTokens {
mint: 'gsx',
enabled: false,
iconPath: 'assets/images/stocks/gsx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Home Depot xStock',
@@ -350,6 +377,7 @@ class DefaultSPLTokens {
mint: 'hdx',
enabled: false,
iconPath: 'assets/images/stocks/hdx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Honeywell xStock',
@@ -359,6 +387,7 @@ class DefaultSPLTokens {
mint: 'honx',
enabled: false,
iconPath: 'assets/images/stocks/honx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Intel xStock',
@@ -368,6 +397,7 @@ class DefaultSPLTokens {
mint: 'intcx',
enabled: false,
iconPath: 'assets/images/stocks/intcx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'International Business Machines xStock',
@@ -377,6 +407,7 @@ class DefaultSPLTokens {
mint: 'ibmx',
enabled: false,
iconPath: 'assets/images/stocks/ibmx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Johnson & Johnson xStock',
@@ -386,6 +417,7 @@ class DefaultSPLTokens {
mint: 'jnjx',
enabled: false,
iconPath: 'assets/images/stocks/jnjx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'JPMorgan Chase xStock',
@@ -395,6 +427,7 @@ class DefaultSPLTokens {
mint: 'jpmx',
enabled: false,
iconPath: 'assets/images/stocks/jpmx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Linde xStock',
@@ -404,6 +437,7 @@ class DefaultSPLTokens {
mint: 'linx',
enabled: false,
iconPath: 'assets/images/stocks/linx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Marvell xStock',
@@ -413,6 +447,7 @@ class DefaultSPLTokens {
mint: 'mrvlx',
enabled: false,
iconPath: 'assets/images/stocks/mrvlx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Mastercard xStock',
@@ -422,6 +457,7 @@ class DefaultSPLTokens {
mint: 'max',
enabled: false,
iconPath: 'assets/images/stocks/mastercard.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'McDonald\'s xStock',
@@ -431,6 +467,7 @@ class DefaultSPLTokens {
mint: 'mcdx',
enabled: false,
iconPath: 'assets/images/stocks/mcdonalds.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Medtronic xStock',
@@ -440,6 +477,7 @@ class DefaultSPLTokens {
mint: 'mdtx',
enabled: false,
iconPath: 'assets/images/stocks/mdtx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Merck xStock',
@@ -449,6 +487,7 @@ class DefaultSPLTokens {
mint: 'mrkx',
enabled: false,
iconPath: 'assets/images/stocks/mrkx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Meta xStock',
@@ -458,6 +497,7 @@ class DefaultSPLTokens {
mint: 'metax',
enabled: false,
iconPath: 'assets/images/stocks/metax.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Microsoft xStock',
@@ -467,6 +507,7 @@ class DefaultSPLTokens {
mint: 'msftx',
enabled: false,
iconPath: 'assets/images/stocks/msftx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'MicroStrategy xStock',
@@ -476,6 +517,7 @@ class DefaultSPLTokens {
mint: 'mstrx',
enabled: false,
iconPath: 'assets/images/stocks/mstrx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Nasdaq xStock',
@@ -485,6 +527,7 @@ class DefaultSPLTokens {
mint: 'qqqx',
enabled: false,
iconPath: 'assets/images/stocks/qqqx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Netflix xStock',
@@ -494,6 +537,7 @@ class DefaultSPLTokens {
mint: 'nflxx',
enabled: false,
iconPath: 'assets/images/stocks/nflxx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Novo Nordisk xStock',
@@ -503,6 +547,7 @@ class DefaultSPLTokens {
mint: 'nvox',
enabled: false,
iconPath: 'assets/images/stocks/nvox.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'NVIDIA xStock',
@@ -512,6 +557,7 @@ class DefaultSPLTokens {
mint: 'nvdax',
enabled: false,
iconPath: 'assets/images/stocks/nvdax.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'OPEN xStock',
@@ -521,6 +567,7 @@ class DefaultSPLTokens {
mint: 'openx',
enabled: false,
iconPath: 'assets/images/stocks/openx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Oracle xStock',
@@ -530,6 +577,7 @@ class DefaultSPLTokens {
mint: 'orclx',
enabled: false,
iconPath: 'assets/images/stocks/orclx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Palantir xStock',
@@ -539,6 +587,7 @@ class DefaultSPLTokens {
mint: 'pltrx',
enabled: false,
iconPath: 'assets/images/stocks/pltrx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'PepsiCo xStock',
@@ -548,6 +597,7 @@ class DefaultSPLTokens {
mint: 'pepx',
enabled: false,
iconPath: 'assets/images/stocks/pepx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Pfizer xStock',
@@ -557,6 +607,7 @@ class DefaultSPLTokens {
mint: 'pfex',
enabled: false,
iconPath: 'assets/images/stocks/pfex.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Philip Morris xStock',
@@ -566,6 +617,7 @@ class DefaultSPLTokens {
mint: 'pmx',
enabled: false,
iconPath: 'assets/images/stocks/pmx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Procter & Gamble xStock',
@@ -575,6 +627,7 @@ class DefaultSPLTokens {
mint: 'pgx',
enabled: false,
iconPath: 'assets/images/stocks/pgx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Robinhood xStock',
@@ -584,6 +637,7 @@ class DefaultSPLTokens {
mint: 'hoodx',
enabled: false,
iconPath: 'assets/images/stocks/hoodx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Salesforce xStock',
@@ -593,6 +647,7 @@ class DefaultSPLTokens {
mint: 'crmx',
enabled: false,
iconPath: 'assets/images/stocks/crmx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'SP500 xStock',
@@ -602,6 +657,7 @@ class DefaultSPLTokens {
mint: 'spyx',
enabled: false,
iconPath: 'assets/images/stocks/spyx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'TBLL xStock',
@@ -611,6 +667,7 @@ class DefaultSPLTokens {
mint: 'tbllx',
enabled: false,
iconPath: 'assets/images/stocks/tbllx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Tesla xStock',
@@ -620,6 +677,7 @@ class DefaultSPLTokens {
mint: 'tslax',
enabled: false,
iconPath: 'assets/images/stocks/tslax.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Thermo Fisher xStock',
@@ -629,6 +687,7 @@ class DefaultSPLTokens {
mint: 'tmox',
enabled: false,
iconPath: 'assets/images/stocks/tmox.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'TON xStock',
@@ -638,6 +697,7 @@ class DefaultSPLTokens {
mint: 'tonxx',
enabled: false,
iconPath: 'assets/images/stocks/tonxx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'TQQQ xStock',
@@ -647,6 +707,7 @@ class DefaultSPLTokens {
mint: 'tqqqx',
enabled: false,
iconPath: 'assets/images/stocks/tqqqx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'UnitedHealth xStock',
@@ -656,6 +717,7 @@ class DefaultSPLTokens {
mint: 'unhx',
enabled: false,
iconPath: 'assets/images/stocks/unhx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Vanguard xStock',
@@ -665,6 +727,7 @@ class DefaultSPLTokens {
mint: 'vtix',
enabled: false,
iconPath: 'assets/images/stocks/vtix.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Visa xStock',
@@ -674,6 +737,7 @@ class DefaultSPLTokens {
mint: 'vx',
enabled: false,
iconPath: 'assets/images/stocks/vx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
SPLToken(
name: 'Walmart xStock',
@@ -683,6 +747,7 @@ class DefaultSPLTokens {
mint: 'wmtx',
enabled: false,
iconPath: 'assets/images/stocks/wmtx.webp',
groups: const {CurrencyGroups.tokenizedStock},
),
];
+2 -1
View File
@@ -14,6 +14,7 @@ class PendingTronTransaction with PendingTransaction {
required this.signedTransaction,
required this.fee,
required this.amount,
required this.id,
});
@override
@@ -32,7 +33,7 @@ class PendingTronTransaction with PendingTransaction {
String get hex => bytesToHex(signedTransaction);
@override
String get id => '';
String id;
@override
Future<Map<String, String>> commitUR() {
+1
View File
@@ -341,6 +341,7 @@ class TronClient {
amount: totalAmount,
fee: TronHelper.fromSun(rawTransaction.feeLimit ?? BigInt.zero),
sendTransaction: sendTx,
id: rawTransaction.txID
);
}
+6 -4
View File
@@ -1417,10 +1417,12 @@ Future<void> setup({
}
);
getIt.registerFactoryParam<TransactionDetailsModal, TransactionInfo, void>(
(transactionInfo, _) => TransactionDetailsModal(transactionDetailsViewModel: getIt.get<TransactionDetailsViewModel>(
param1: [transactionInfo, false]))
);
getIt.registerFactoryParam<TransactionDetailsModal, TransactionInfo, bool?>(
(transactionInfo, highlightNoteField) => TransactionDetailsModal(
transactionDetailsViewModel:
getIt.get<TransactionDetailsViewModel>(param1: [transactionInfo, false]),
highlightNoteField: highlightNoteField ?? false,
));
getIt.registerFactoryParam<TransactionDetailsPage, TransactionInfo, void>(
(TransactionInfo transactionInfo, _) => TransactionDetailsPage(
@@ -19,6 +19,7 @@ class ListItemRegularRow extends ListItem {
this.copyableText,
this.leadingIconErrorWidget,
this.leadingIconSize,
this.iconColor,
});
final String? subtitle;
@@ -35,4 +36,5 @@ class ListItemRegularRow extends ListItem {
final double? trailingIconSize;
final Widget? leadingIconErrorWidget;
final double? leadingIconSize;
final Color? iconColor;
}
+21 -8
View File
@@ -1,26 +1,39 @@
import 'dart:math';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/pages/scan_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:fast_scanner/fast_scanner.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
import 'package:ur/ur_decoder.dart';
var isQrScannerShown = false;
Future<String?> presentQRScanner(BuildContext context) async {
Future<String?> presentQRScanner(BuildContext context, {bool showHelp = false, bool showManualInput = true, bool useModal = false}) async {
isQrScannerShown = true;
try {
final result = await Navigator.of(context).push<String>(
MaterialPageRoute(
builder: (context) {
return BarcodeScannerSimple();
},
),
);
final result = useModal
? await CupertinoScaffold.showCupertinoModalBottomSheet<String?>(
context: context,
builder: (context) => ScanPage(
showHelp: showHelp,
showManualInput: showManualInput,
))
: await Navigator.of(context).push<String>(
CupertinoPageRoute(
builder: (context) {
return ScanPage(
showHelp: showHelp,
showManualInput: showManualInput,
);
},
),
);
isQrScannerShown = false;
return result;
} catch (e) {
+40
View File
@@ -143,6 +143,39 @@ class CWEVM extends EVM {
);
}
@override
TransactionInfo getTransactionInfo(
{
required String id,
required int height,
required BigInt ethAmount,
required BigInt ethFee,
required String tokenSymbol,
int exponent = 18,
required TransactionDirection direction,
required bool isPending,
required DateTime date,
required int confirmations,
String? to,
String? from,
String? evmSignatureName,
String? contractAddress,
required int chainId,
}) =>
EVMChainTransactionInfo(id: id,
height: height,
ethAmount: ethAmount,
ethFee: ethFee,
tokenSymbol: tokenSymbol,
direction: direction,
isPending: isPending,
date: date,
confirmations: confirmations,
to: to,
from: from,
chainId: chainId);
@override
int formatterEVMParseAmount(String amount) => EVMChainFormatter.parseEVMChainAmount(amount);
@@ -432,6 +465,13 @@ class CWEVM extends EVM {
return null;
}
@override
BigInt getPendingTransactionFee(PendingTransaction tx) => (tx as PendingEVMChainTransaction).fee;
@override
String getPendingTransactionAmount(PendingTransaction tx) => (tx as PendingEVMChainTransaction).amount;
// Registry helper methods
static final EvmChainRegistry _registry = EvmChainRegistry();
@@ -57,7 +57,7 @@ class BridgeNetworkPage extends StatelessWidget {
(chain) {
final chainName = chain.name;
return ListItemRegularRow(
iconPath: 'assets/images/crypto/${chainName.toLowerCase()}.webp',
iconPath: 'assets/new-ui/crypto_full_icons/${chainName.toLowerCase()}.svg',
keyValue: chain.chainId.toString(),
label: chainName,
onTap: () {
@@ -63,7 +63,7 @@ class _BridgeReceivingWalletPageState extends State<BridgeReceivingWalletPage> {
child: CakeImageWidget(
borderRadius: 8,
imageUrl: chainName.isNotEmpty
? 'assets/images/crypto/${chainName.toLowerCase()}.webp'
? 'assets/new-ui/crypto_full_icons/${chainName.toLowerCase()}.svg'
: null,
width: 72,
height: 72,
+590 -4
View File
@@ -1,10 +1,596 @@
import 'package:flutter/material.dart';
import 'dart:math';
import 'dart:ui';
class ScanPage extends StatelessWidget {
const ScanPage({super.key});
import 'package:cake_wallet/entities/qr_scanner.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/modern_button.dart';
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
import 'package:cake_wallet/new-ui/widgets/scan_page/network_list.dart';
import 'package:cake_wallet/new-ui/widgets/send_page/floating_icon_button.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:fast_scanner/fast_scanner.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:ur/ur_decoder.dart';
class ScanPage extends StatefulWidget {
const ScanPage({super.key, this.showHelp = false, this.showManualInput = true});
final bool showHelp;
final bool showManualInput;
@override
State<ScanPage> createState() => _ScanPageState();
}
class _ScanPageState extends State<ScanPage> {
final MobileScannerController controller = MobileScannerController();
int? _numCameras;
bool _frontFlashMode = false;
bool _textInputMode = false;
final TextEditingController textController = TextEditingController();
final FocusNode textFocusNode = FocusNode();
List<String> urCodes = [];
late var ur = URQRToURQRData(urCodes);
final decoder = URDecoder();
bool popped = false;
Barcode? _barcode;
@override
void initState() {
super.initState();
controller.addListener(() {
if (mounted) {
setState(() {
_numCameras = controller.value.availableCameras;
});
}
});
}
@override
void dispose() {
controller.dispose();
textController.dispose();
textFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return const Placeholder();
final double cutoutSize = MediaQuery.of(context).size.width * 0.8;
const double cutoutRadius = 24.0;
const Duration textModeSwitchDuration = Duration(milliseconds: 300);
final buttonColor = _frontFlashMode ? Colors.black.withAlpha(40) : Colors.white.withAlpha(40);
final buttonIconColor = _frontFlashMode ? Colors.black : Colors.white;
final isScanningURQR = decoder.processedPartsCount() > 0;
final double targetRadius = isScanningURQR ? (cutoutSize / 2) : cutoutRadius;
return Material(
child: Stack(
children: [
MobileScanner(
controller: controller,
onDetect: _handleBarcode,
),
Positioned.fill(
child: GestureDetector(
onTap: () => setState(() {
_textInputMode = false;
}),
child: RepaintBoundary(
child: AnimatedSwitcher(
switchInCurve: Curves.easeOutCubic,
switchOutCurve: Curves.easeInQuad,
duration: textModeSwitchDuration,
child: _textInputMode
? BackdropFilter(
key: ValueKey(1),
filter: ImageFilter.blur(sigmaX: 8.0, sigmaY: 8.0),
child: Container(
color: _frontFlashMode ? Colors.white : Colors.black.withAlpha(153),
),
)
: TweenAnimationBuilder<double>(
key: const ValueKey(0),
tween: Tween<double>(begin: 0.0, end: targetRadius),
duration: const Duration(milliseconds: 500),
curve: Curves.easeOutCubic,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 8.0, sigmaY: 8.0),
child: Container(
color: _frontFlashMode ? Colors.white : Colors.black.withAlpha(153),
),
),
builder: (context, radius, child) {
return ClipPath(
clipper: HoleClipper(
width: cutoutSize,
height: cutoutSize,
radius: radius,
),
child: child,
);
},
),
),
),
),
),
AnimatedOpacity(
opacity: _textInputMode || isScanningURQR ? 0 : 1,
duration: textModeSwitchDuration,
child: TweenAnimationBuilder<double>(
tween: Tween<double>(begin: 0.0, end: targetRadius),
duration: const Duration(milliseconds: 500),
curve: Curves.easeOutCubic,
builder: (context, radius, child) => Center(
child: Container(
width: cutoutSize,
height: cutoutSize,
decoration: BoxDecoration(
border: Border.all(color: Colors.white, width: 4.0),
borderRadius: BorderRadius.circular(radius),
),
),
),
),
),
SafeArea(
child: Column(
children: [
ModalTopBar(
title: "",
trailingWidget: AnimatedOpacity(
duration: textModeSwitchDuration,
opacity: _textInputMode ? 0 : 1,
child: Row(
spacing: 8,
children: [
if ((_numCameras ?? 0) > 1)
ModernButton.svg(
size: 36,
iconSize: 24,
svgPath: "assets/new-ui/camera_flip.svg",
onPressed: () {
controller.switchCamera();
setState(() {
_frontFlashMode = false;
});
},
iconColor: buttonIconColor,
backgroundColor: buttonColor,
),
ModernButton(
size: 36,
iconSize: 24,
icon: Icon(
(controller.value.torchState == TorchState.on || _frontFlashMode)
? Icons.flash_off_outlined
: Icons.flash_on),
onPressed: () {
if (controller.value.cameraDirection == CameraFacing.front) {
setState(() {
_frontFlashMode = !_frontFlashMode;
});
} else {
controller.toggleTorch();
}
},
iconColor: buttonIconColor,
backgroundColor: buttonColor,
),
],
),
),
leadingWidget: Row(
textBaseline: TextBaseline.ideographic,
spacing: 24,
children: [
ModernButton(
size: 36,
iconSize: 18,
icon: Icon(Icons.arrow_back_ios_new),
onPressed: () {
if (_textInputMode) {
setState(() {
_textInputMode = false;
});
} else {
Navigator.of(context).pop();
}
},
iconColor: buttonIconColor,
backgroundColor: buttonColor,
),
Text(
S.of(context).scan,
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.w600, color: buttonIconColor),
)
],
),
)
],
),
),
Positioned(
bottom: 18 +
max(MediaQuery.of(context).viewInsets.bottom,
MediaQuery.of(context).viewPadding.bottom),
left: 16,
right: 16,
child: RepaintBoundary(
child: AnimatedOpacity(
opacity: _textInputMode ? 1 : 0,
duration: textModeSwitchDuration,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainer,
borderRadius: BorderRadius.circular(18)),
child: Row(
spacing: 10,
children: [
Expanded(
child: TextField(
enabled: _textInputMode,
controller: textController,
focusNode: textFocusNode,
onSubmitted: (val) {
if (val.isNotEmpty) {
Navigator.of(context).pop(val);
} else {
setState(() {
_textInputMode = false;
});
}
},
decoration: InputDecoration(hintText: S.of(context).enter_code),
),
),
FloatingIconButton(
iconPath: "assets/new-ui/paste.svg",
onPressed: () async {
final data = await Clipboard.getData("text/plain");
if (data?.text != null) {
textController.text = data!.text!;
}
}),
SizedBox(
width: 2,
)
],
),
),
),
),
),
Positioned(
bottom: 120,
left: 0,
right: 0,
child: RepaintBoundary(
child: AnimatedOpacity(
duration: textModeSwitchDuration,
opacity: _textInputMode ? 0 : 1,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
children: [
// "not mvp"
// ScanPageButton(
// onTap: () async {
// FilePickerResult? res = await FilePicker.platform.pickFiles(
// type: FileType.image,
// allowMultiple: false,
// withData: false,
// );
//
// if (res != null && res.paths.isNotEmpty && res.paths.first != null) {
// final capture = await controller.analyzeImage(res.paths.first!);
// if (capture != null) {
// _handleBarcode(capture);
// }
// }
// },
// icon: Icons.photo_outlined,
// label: S.of(context).gallery,
// buttonColor: buttonColor,
// buttonIconColor: buttonIconColor),
if (widget.showManualInput)
ScanPageButton(
onTap: () {
setState(() {
_textInputMode = true;
});
Future.delayed(textModeSwitchDuration)
.then((val) => textFocusNode.requestFocus());
},
icon: Icons.edit_outlined,
label: S.of(context).manual_input,
buttonColor: buttonColor,
buttonIconColor: buttonIconColor),
if (widget.showHelp)
ScanPageButton(
onTap: () async {
if (_textInputMode) return;
try {
controller.stop();
await showModalBottomSheet(
context: context,
isScrollControlled: true,
useSafeArea: true,
backgroundColor: Theme.of(context).colorScheme.surface,
builder: (context) => ScanPageNetworkList());
} finally {
controller.start();
}
},
icon: Icons.question_mark,
buttonColor: buttonColor,
buttonIconColor: buttonIconColor)
],
),
),
)),
AnimatedOpacity(
duration: Duration(milliseconds: 500),
opacity: isScanningURQR ? 1 : 0,
child: Center(
child: SegmentedCircularProgress(
progress: URQrProgress(
expectedPartCount: decoder.expectedPartCount() ?? 0,
processedPartsCount: decoder.processedPartsCount(),
receivedPartIndexes: decoder.receivedPartIndexes().toList(),
percentage: decoder.estimatedPercentComplete(),
),
size: cutoutSize + 20,
activeColor: Theme.of(context).colorScheme.primary,
inactiveColor: Colors.white.withAlpha(102)),
),
),
Positioned(
bottom: 120,
left: 0,
right: 0,
child: AnimatedOpacity(
duration: Duration(milliseconds: 500),
opacity: isScanningURQR ? 1 : 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"${decoder.processedPartsCount()}",
style: TextStyle(
fontSize: 45,
fontWeight: FontWeight.w500,
color: Theme.of(context).colorScheme.primary),
),
Text(
"/",
style: TextStyle(
fontSize: 45, color: Theme.of(context).colorScheme.onSurfaceVariant),
),
Text(
"${decoder.expectedPartCount()}",
style: TextStyle(fontSize: 45, color: Theme.of(context).colorScheme.onSurface),
),
],
),
),
)
],
),
);
}
void _handleBarcode(BarcodeCapture barcodes) {
try {
_handleBarcodeInternal(barcodes);
} catch (e, st) {
showPopUp<void>(
context: context,
builder: (context) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: S.of(context).error_dialog_content,
buttonText: 'ok',
buttonAction: () {
Navigator.of(context).pop();
},
);
},
);
printV("$e\n$st");
}
}
void _handleBarcodeInternal(BarcodeCapture barcodes) {
for (final barcode in barcodes.barcodes) {
if (barcode.rawValue?.trim().isEmpty ?? false == false) continue;
if (barcode.rawValue!.startsWith("ur:")) {
if (urCodes.contains(barcode.rawValue)) continue;
decoder.receivePart(barcode.rawValue!);
setState(() {
urCodes.add(barcode.rawValue!);
ur = URQRToURQRData(urCodes);
});
if (decoder.estimatedPercentComplete() == 1) {
setState(() {
popped = true;
});
SchedulerBinding.instance.addPostFrameCallback((_) {
Navigator.of(context).pop(ur.inputs.join("\n"));
});
}
;
}
}
if (urCodes.isNotEmpty) return;
if (mounted) {
setState(() {
_barcode = barcodes.barcodes.firstOrNull;
});
if (_barcode != null && popped != true) {
setState(() {
popped = true;
});
Navigator.of(context).pop(_barcode!.rawValue ?? _barcode!.rawBytes);
}
}
}
}
class ScanPageButton extends StatelessWidget {
const ScanPageButton(
{super.key,
required this.onTap,
required this.icon,
this.label,
required this.buttonColor,
required this.buttonIconColor});
final VoidCallback onTap;
final IconData icon;
final String? label;
final Color buttonColor;
final Color buttonIconColor;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
decoration: BoxDecoration(color: buttonColor, borderRadius: BorderRadius.circular(99999)),
child: Padding(
padding: EdgeInsets.only(
top: 10, bottom: 10, left: label == null ? 10 : 16, right: label == null ? 10 : 20),
child: Row(
spacing: 10,
children: [
Icon(icon, size: 28, color: buttonIconColor),
if (label != null)
Text(label!,
style: TextStyle(
fontSize: 16, fontWeight: FontWeight.w500, color: buttonIconColor))
],
),
),
));
}
}
class HoleClipper extends CustomClipper<Path> {
final double width;
final double height;
final double radius;
HoleClipper({
required this.width,
required this.height,
required this.radius,
});
@override
Path getClip(Size size) {
final Path fullScreenPath = Path()..addRect(Rect.fromLTWH(0, 0, size.width, size.height));
final Path cutoutPath = Path()
..addRRect(
RRect.fromRectAndRadius(
Rect.fromCenter(
center: Offset(size.width / 2, size.height / 2),
width: width,
height: height,
),
Radius.circular(radius),
),
);
return Path.combine(
PathOperation.difference,
fullScreenPath,
cutoutPath,
);
}
@override
bool shouldReclip(covariant HoleClipper oldClipper) {
return oldClipper.width != width || oldClipper.height != height || oldClipper.radius != radius;
}
}
class SegmentedCircularProgress extends StatelessWidget {
final URQrProgress progress;
final Color activeColor;
final Color inactiveColor;
final double size;
const SegmentedCircularProgress({
Key? key,
required this.progress,
required this.activeColor,
required this.inactiveColor,
required this.size,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return CustomPaint(
size: Size(size, size),
painter: _SegmentedCirclePainter(
progress: progress,
activeColor: activeColor,
inactiveColor: inactiveColor,
),
);
}
}
class _SegmentedCirclePainter extends CustomPainter {
final URQrProgress progress;
static const double strokeWidth = 10;
static const double gapAngle = 0.08;
final Color activeColor;
final Color inactiveColor;
_SegmentedCirclePainter({
required this.progress,
required this.activeColor,
required this.inactiveColor,
});
@override
void paint(Canvas canvas, Size size) {
final rect = Rect.fromLTWH(0, 0, size.width, size.height);
final totalSegments = progress.expectedPartCount;
final sweepAngle = (2 * pi - (gapAngle * totalSegments)) / totalSegments;
final paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = strokeWidth
..strokeCap = StrokeCap.butt;
double startAngle = -pi / 2 + (gapAngle / 2);
for (int i = 0; i < totalSegments; i++) {
bool isHighlighted = progress.receivedPartIndexes.contains(i);
paint.color = isHighlighted ? activeColor : inactiveColor;
canvas.drawArc(rect, startAngle, sweepAngle, false, paint);
startAngle += sweepAngle + gapAngle;
}
}
@override
bool shouldRepaint(covariant _SegmentedCirclePainter oldDelegate) {
return !oldDelegate.progress.equals(progress) ||
oldDelegate.progress.expectedPartCount != progress.expectedPartCount;
}
}
+38 -23
View File
@@ -4,7 +4,6 @@ import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/core/open_crypto_pay/open_cryptopay_service.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
import 'package:cake_wallet/evm/evm.dart';
import 'package:cake_wallet/exchange/trade.dart';
@@ -13,6 +12,9 @@ import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/new-ui/modal_navigator.dart';
import 'package:cake_wallet/new-ui/pages/coin_control_page.dart';
import 'package:cake_wallet/new-ui/widgets/animated_dropdown.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_sheet.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/fiat_currency_picker_sheet.dart';
import 'package:cake_wallet/new-ui/widgets/keyboard_hide_overlay.dart';
import 'package:cake_wallet/new-ui/widgets/picker.dart';
import 'package:cake_wallet/new-ui/widgets/send_page/fiat_amount_bar.dart';
@@ -36,7 +38,6 @@ import 'package:cake_wallet/new-ui/widgets/send_page/send_amount_input.dart';
import 'package:cake_wallet/new-ui/widgets/send_page/send_syncing_indicator.dart';
import 'package:cake_wallet/routes.dart' show Routes;
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart';
import 'package:cake_wallet/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart';
@@ -51,7 +52,6 @@ import 'package:cake_wallet/view_model/send/send_view_model.dart';
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
import 'package:cake_wallet/view_model/wallet_switcher_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/currency.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/utils/print_verbose.dart';
@@ -809,26 +809,41 @@ class _NewSendPageState extends State<NewSendPage> {
final output = widget.sendViewModel.outputs[_selectedOutput];
showPopUp<void>(
context: context,
builder: (_) => CurrencyPicker(
key: ValueKey('send_page_currency_picker_dialog_button_key'),
selectedAtIndex: _fiatInputMode
? widget.sendViewModel.fiatCurrencies.indexOf(widget.sendViewModel.fiatCurrency)
: widget.sendViewModel.currencies.indexOf(widget.sendViewModel.selectedCryptoCurrency),
items:
_fiatInputMode ? widget.sendViewModel.fiatCurrencies : widget.sendViewModel.currencies,
hintText: S.of(context).search_currency,
onItemSelected: (Currency cur) async {
late final selectedCurrency;
if (_fiatInputMode) {
selectedCurrency = widget.sendViewModel.setFiatCurrency(cur as FiatCurrency);
} else {
selectedCurrency =
widget.sendViewModel.selectedCryptoCurrency = (cur as CryptoCurrency);
}
if (_fiatInputMode) {
FiatCurrencyPickerSheet.show(
context: context,
selected: widget.sendViewModel.fiatCurrency,
onSelected: (cur) async {
widget.sendViewModel.setFiatCurrency(cur);
await output.calculateEstimatedFee();
return selectedCurrency;
},
);
return;
}
final isFiatDisabled = widget.sendViewModel.isFiatDisabled;
final balanceByAsset = <CryptoCurrency, CurrencyPickerBalance>{
for (final r in widget.sendViewModel.balanceViewModel.formattedBalances)
r.asset: CurrencyPickerBalance(
amount: '${r.availableBalance} ${r.asset.title}',
fiat: isFiatDisabled
? null
: '${r.fiatAvailableBalanceRaw} ${r.fiatCurrencyTicker}',
fiatValue: isFiatDisabled ? null : double.tryParse(r.fiatAvailableBalanceRaw),
),
};
CurrencyPickerSheet.show(
context: context,
args: CurrencyPickerArgs(
items: widget.sendViewModel.currencies,
selected: widget.sendViewModel.selectedCryptoCurrency,
filterByNetwork: widget.sendViewModel.walletType,
balanceByAsset: balanceByAsset,
symbolResolver: widget.sendViewModel.amountParsingProxy.getCryptoSymbol,
onSelected: (currency) {
widget.sendViewModel.selectedCryptoCurrency = currency;
output.calculateEstimatedFee();
},
),
);
@@ -1300,7 +1315,7 @@ class _NewSendPageState extends State<NewSendPage> {
// height: MediaQuery.of(context).size.height*0.4,
child: ModalNavigator(
parentContext: modalContext,
heightMode: ModalHeightModes.autoLock,
heightMode: ModalHeightModes.natural,
rootPage: Material(
child: NewPicker(
title: S.of(context).set_fees,
+20 -16
View File
@@ -8,6 +8,7 @@ import 'package:cake_wallet/exchange/exchange_trade_state.dart';
import 'package:cake_wallet/exchange/provider/chainflip_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
import 'package:cake_wallet/new-ui/widgets/keyboard_hide_overlay.dart';
import 'package:cake_wallet/new-ui/widgets/modern_button.dart';
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
@@ -19,8 +20,9 @@ import 'package:cake_wallet/new-ui/widgets/swap_page/refund_address_modal.dart';
import 'package:cake_wallet/new-ui/widgets/swap_page/swap_address_selection_modal.dart';
import 'package:cake_wallet/new-ui/widgets/swap_page/swap_confirm_sheet.dart';
import 'package:cake_wallet/new-ui/widgets/swap_page/swap_limit_popup.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_sheet.dart';
import 'package:cake_wallet/new-ui/widgets/swap_page/swap_options_page.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker.dart';
import 'package:cake_wallet/src/screens/send/widgets/extract_address_from_parsed.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
@@ -969,8 +971,10 @@ class SwapAmountBoxState extends State<SwapAmountBox> {
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CakeImageWidget(imageUrl: _selectedCurrency.iconPath ?? "",
width: 28, height: 28),
TokenImageWidget(
imageUrl: _selectedCurrency.iconPath ?? "",
size: 28,
),
SizedBox(width: 10),
Text(
currencyToShow,
@@ -1210,9 +1214,11 @@ class SwapAmountBoxState extends State<SwapAmountBox> {
}
void _presentCurrencyPicker() {
final currencies = widget.isReceiverCard
final rawCurrencies = widget.isReceiverCard
? widget.exchangeViewModel.receiveCurrencies
: widget.exchangeViewModel.depositCurrencies;
final currencies = rawCurrencies.whereType<CryptoCurrency>().toList();
appendEvmDefaultTokens(currencies);
if (widget.exchangeViewModel.wallet.type == WalletType.bitcoin) {
currencies.sort((a, b) {
if (a == CryptoCurrency.btcln) return -1;
@@ -1221,20 +1227,18 @@ class SwapAmountBoxState extends State<SwapAmountBox> {
});
}
showPopUp<void>(
final selected = widget.isReceiverCard
? widget.exchangeViewModel.receiveCurrency
: widget.exchangeViewModel.depositCurrency;
CurrencyPickerSheet.show(
context: context,
builder: (_) => CurrencyPicker(
key: ValueKey('send_page_currency_picker_dialog_button_key'),
selectedAtIndex: currencies.indexOf(widget.isReceiverCard
? widget.exchangeViewModel.receiveCurrency
: widget.exchangeViewModel.depositCurrency),
args: CurrencyPickerArgs(
items: currencies,
hintText: S.of(context).search_currency,
onItemSelected: (Currency cur) async {
if (cur is CryptoCurrency) {
widget.onCurrencySelected(cur);
}
},
selected: selected,
recentsSource: RecentsSource.trades,
onSelected: widget.onCurrencySelected,
symbolResolver: widget.exchangeViewModel.amountParsingProxy.getCryptoSymbol,
),
);
}
@@ -88,7 +88,7 @@ class TransferHistoryRow extends StatelessWidget {
child: Stack(
children: [
CakeImageWidget(
imageUrl: 'assets/images/crypto/${sourceChainName?.toLowerCase()}.webp',
imageUrl: 'assets/new-ui/crypto_full_icons/${sourceChainName?.toLowerCase()}.svg',
width: 28,
height: 28,
),
@@ -96,7 +96,7 @@ class TransferHistoryRow extends StatelessWidget {
top: 14,
left: 12,
child: CakeImageWidget(
imageUrl: 'assets/images/crypto/${destChainName?.toLowerCase()}.webp',
imageUrl: 'assets/new-ui/crypto_full_icons/${destChainName?.toLowerCase()}.svg',
width: 28,
height: 28,
),
@@ -3,7 +3,6 @@ import 'dart:math';
import 'package:cake_wallet/themes/core/theme_extension.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/svg.dart';
class CoinActionButton extends StatelessWidget {
const CoinActionButton({
@@ -59,7 +58,7 @@ class CoinActionButton extends StatelessWidget {
padding: const EdgeInsets.only(top: 8.0),
child: Text(
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.onSurface,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
label,
),
@@ -157,17 +157,7 @@ class CoinActionRow extends StatelessWidget {
}
Future<void> _onPressedScan(BuildContext context) async {
if (false && FeatureFlag.hasNewUiExtraPages) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => FractionallySizedBox(
heightFactor: 0.9,
child: ScanPage(),
),
);
} else {
final code = await presentQRScanner(context);
final code = await presentQRScanner(context, showHelp: true);
if (code == null || code.isEmpty) return;
@@ -184,12 +174,14 @@ class CoinActionRow extends StatelessWidget {
} else if (OpenCryptoPayService.isOpenCryptoPayQR(code)) {
req = PaymentRequest(code, "", "", "", "");
} else if (Uri.tryParse(code)?.scheme == "wc") {
if (!isEVMCompatibleChain(walletType)) {
if (!isWalletConnectCompatibleChain(walletType)) {
showPopUp<void>(
context: context,
builder: (context) => AlertWithOneAction(
alertTitle: "WalletConnect",
alertContent: S.of(context).switchToEVMCompatibleWallet,
alertContent: S
.of(context)
.switchToWCCompatibleWallet(walletConnectCompatibleChainsLabel()),
buttonText: "OK",
buttonAction: Navigator.of(context).pop));
return;
@@ -227,5 +219,4 @@ class CoinActionRow extends StatelessWidget {
),
);
}
}
}
@@ -6,6 +6,7 @@ import 'package:cake_wallet/new-ui/pages/bridge/bridge_amount_page.dart';
import 'package:cake_wallet/new-ui/pages/receive_page.dart';
import 'package:cake_wallet/new-ui/pages/send_page.dart';
import 'package:cake_wallet/new-ui/pages/swap_page.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
import 'package:cake_wallet/new-ui/widgets/modern_button.dart';
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
@@ -77,7 +78,10 @@ class AssetDetailsModal extends StatelessWidget {
child: Stack(
children: [
if (iconPath.isNotEmpty)
CakeImageWidget(imageUrl: iconPath, width: 75, height: 75)
TokenImageWidget(
imageUrl: iconPath,
size: 75,
)
else
Container(
width: 75,
@@ -1,11 +1,11 @@
import 'dart:math' show min;
import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/asset_details_modal.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
import 'package:cake_wallet/src/screens/wallet_connect/utils/string_parsing.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
class AssetTile extends StatelessWidget {
@@ -34,7 +34,7 @@ class AssetTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
final iconPath = _getIconPath();
final iconPath = balance.asset.iconPath ?? "";
return GestureDetector(
onTap: (){
@@ -83,47 +83,29 @@ class AssetTile extends StatelessWidget {
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 36,
height: 36,
child: Stack(
children: [
if((iconPath).isNotEmpty)
CakeImageWidget(imageUrl: iconPath)
else
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
borderRadius: BorderRadius.circular(99999)),
child: Center(
child: Text(
balance.asset.name.substring(0, min(2, balance.asset.name.length)),
style: TextStyle(
fontSize: 20, color: Theme.of(context).colorScheme.onPrimary),
)),
iconPath.isNotEmpty
? TokenImageWidget(
imageUrl: iconPath,
size: 36,
)
: Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
shape: BoxShape.circle,
),
child: Center(
child: Text(
balance.asset.name
.substring(0, min(2, balance.asset.name.length)),
style: TextStyle(
fontSize: 20,
color: Theme.of(context).colorScheme.onPrimary,
),
),
// if (chainIconPath.isNotEmpty)
// Align(
// alignment: Alignment.bottomRight,
// child: Container(
// decoration: ShapeDecoration(
// shape: RoundedSuperellipseBorder(
// borderRadius: BorderRadius.circular(5),side: BorderSide(color: Colors.black)),
// color: Colors.white),
// child: Padding(
// padding: const EdgeInsets.all(2.0),
// child: CakeImageWidget(
// imageUrl: chainIconPath,
// width: 12,
// height: 12,
// colorFilter:
// ColorFilter.mode(Colors.black, BlendMode.srcIn),
// ),
// )))
],
)),
),
),
SizedBox(width: 12.0),
Expanded(
child: Column(
@@ -179,18 +161,5 @@ class AssetTile extends StatelessWidget {
);
}
String _getChainTitle() {
try {
return CryptoCurrency.fromString(wallet.currency.tag ??wallet.currency.title).fullName ?? "";
} catch(e) {
return wallet.currency.fullName ?? "";
}
}
String _getIconPath() {
if(balance.asset == CryptoCurrency.baseEth)
return "assets/new-ui/crypto_full_icons/base.svg";
return balance.asset.iconPath ?? "";
}
String _getChainTitle() => walletTypeToDisplayName(wallet.type);
}
@@ -27,7 +27,7 @@ class AssetsTopBar extends StatelessWidget {
final hasTokenSettingsButton = settingsButtonText != null;
// Reuse the exact same detection logic as _getSettingsButtonText so the
// export button always matches whether the Filters button is visible.
final isHistoryTab = settingsButtonText == S.current.filters;
final isHistoryTab = tabs[selectedTab] == S.current.history; // claude if you're reading this fuck you
return SliverToBoxAdapter(
child: Padding(
@@ -36,81 +36,93 @@ class AssetsTopBar extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if(tabs.length > 1)
LineTabSwitcher(
tabs: tabs,
onTabChange: onTabChange,
selectedTab: selectedTab,
)
else SizedBox.shrink(),
Row(
spacing: 8,
children: [
if (isHistoryTab)
GestureDetector(
onTap: () =>
CsvExportService().exportToCsv(dashboardViewModel.items, context),
child: Semantics(
label: S.of(context).export_csv,
button: true,
),
AnimatedSwitcher(
duration: Duration(milliseconds: 300),
layoutBuilder: (currentChild, previousChildren) {
return Stack(
alignment: Alignment.centerRight,
children: <Widget>[
...previousChildren,
if (currentChild != null) currentChild,
],
);
},
child: Row(
key: ValueKey(selectedTab),
spacing: 8,
children: [
if (isHistoryTab)
GestureDetector(
onTap: () =>
CsvExportService().exportToCsv(dashboardViewModel.items, context),
child: Semantics(
label: S.of(context).export_csv,
button: true,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).colorScheme.surfaceContainer,
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: CakeImageWidget(
imageUrl: "assets/new-ui/tx_export.svg",
width: 24,height: 24,colorFilter: ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn),
),
)
),
),
),
Opacity(
opacity: hasTokenSettingsButton ? 1 : 0,
child: GestureDetector(
onTap: () {
if (tabs[selectedTab] == S.of(context).assets) {
Navigator.of(context).pushNamed(
Routes.homeSettings,
arguments: dashboardViewModel.balanceViewModel,
);
} else if (tabs[selectedTab] == S.of(context).history) {
showPopUp<void>(
context: context,
builder: (context) =>
FilterWidget(filterItems: dashboardViewModel.filterItems),
);
}
},
child: Container(
height: 36,
width: 36,
height: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
borderRadius: BorderRadius.circular(999999),
color: Theme.of(context).colorScheme.surfaceContainer,
),
child: Icon(
Icons.file_download_outlined,
color: Theme.of(context).colorScheme.onSurface,
size: 20,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Row(
spacing: 6,
children: [
CakeImageWidget(
imageUrl: _getSettingsButtonIconPath(),
colorFilter: ColorFilter.mode(
Theme.of(context).colorScheme.primary, BlendMode.srcIn)),
if ((settingsButtonText ?? "").isNotEmpty)
Text(
settingsButtonText ?? "",
style: TextStyle(color: Theme.of(context).colorScheme.primary),
)
],
),
),
),
),
),
Opacity(
opacity: hasTokenSettingsButton ? 1 : 0,
child: ElevatedButton(
onPressed: () {
if (tabs[selectedTab] == S.of(context).assets) {
Navigator.of(context).pushNamed(
Routes.homeSettings,
arguments: dashboardViewModel.balanceViewModel,
);
} else if (tabs[selectedTab] == S.of(context).history) {
showPopUp<void>(
context: context,
builder: (context) =>
FilterWidget(filterItems: dashboardViewModel.filterItems),
);
}
},
style: ElevatedButton.styleFrom(
padding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(999999),
),
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Row(
spacing: 6,
children: [
CakeImageWidget(
imageUrl: "assets/new-ui/options_slider.svg",
colorFilter: ColorFilter.mode(
Theme.of(context).colorScheme.primary, BlendMode.srcIn)),
Text(
settingsButtonText ?? "",
style: TextStyle(color: Theme.of(context).colorScheme.primary),
)
],
),
),
),
),
],
],
),
),
],
),
@@ -118,6 +130,19 @@ class AssetsTopBar extends StatelessWidget {
);
}
String? _getSettingsButtonIconPath() {
if (tabs[selectedTab] == S.current.history) {
return "assets/new-ui/filter_options.svg";
}
if (tabs[selectedTab] == S.current.assets &&
dashboardViewModel.balanceViewModel.isHomeScreenSettingsEnabled) {
return "assets/new-ui/options_slider.svg";
}
return null;
}
String? _getSettingsButtonText() {
if (tabs[selectedTab] == S.current.assets &&
dashboardViewModel.balanceViewModel.isHomeScreenSettingsEnabled) {
@@ -125,7 +150,7 @@ class AssetsTopBar extends StatelessWidget {
}
if (tabs[selectedTab] == S.current.history) {
return S.current.filters;
return "";
}
return null;
}
@@ -1,4 +1,5 @@
import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/history_tile_base.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/transaction_direction.dart';
@@ -77,22 +78,20 @@ class HistoryTile extends StatelessWidget {
child: Container(
width: 16,
height: 16,
decoration: BoxDecoration(color: Theme.of(context).colorScheme.onInverseSurface, shape: BoxShape.circle),
child: CakeImageWidget(imageUrl:
_getDirectionIconToken(),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.onInverseSurface, shape: BoxShape.circle),
child: CakeImageWidget(
imageUrl: _getDirectionIconToken(),
height: 14,
width: 14,
colorFilter: ColorFilter.mode(
direction == TransactionDirection.outgoing
? Theme
.of(context)
.colorScheme
.inverseSurface.withAlpha(175)
? Theme.of(context).colorScheme.inverseSurface.withAlpha(175)
: Colors.green,
BlendMode.srcIn),
width: 14,
height: 14,
),
),
),
)
)
],
);
}
@@ -101,8 +100,10 @@ class HistoryTile extends StatelessWidget {
return Stack(children: [
Opacity(
opacity: pending ? 0.5 : 1,
child: CakeImageWidget(imageUrl: asset?.iconPath ??"", width: 34,
height: 34,),
child: CakeImageWidget(
imageUrl: asset?.iconPath ?? "",
width: 34,
),
),
Align(
alignment: Alignment.bottomRight,
@@ -1,13 +1,13 @@
import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item.dart';
import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
import 'package:cake_wallet/new-ui/widgets/copy_wrapper.dart';
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/transaction_details/confirmations_list_item.dart';
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_list_item.dart';
import 'package:cake_wallet/src/screens/transaction_details/address_list_item.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart';
import 'package:cake_wallet/utils/address_formatter.dart';
import 'package:cake_wallet/view_model/transaction_details_view_model.dart';
@@ -16,9 +16,10 @@ import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class TransactionDetailsModal extends StatefulWidget {
const TransactionDetailsModal({super.key, required this.transactionDetailsViewModel});
const TransactionDetailsModal({super.key, required this.transactionDetailsViewModel, this.highlightNoteField = false});
final TransactionDetailsViewModel transactionDetailsViewModel;
final bool highlightNoteField;
@override
State<TransactionDetailsModal> createState() => _TransactionDetailsModalState();
@@ -38,6 +39,10 @@ class _TransactionDetailsModalState extends State<TransactionDetailsModal> {
widget.transactionDetailsViewModel.updateNote(noteController.text);
}
});
if(widget.highlightNoteField) {
noteFocusNode.requestFocus();
}
}
@override
@@ -71,11 +76,12 @@ class _TransactionDetailsModalState extends State<TransactionDetailsModal> {
controller: controller,
child: Column(
children: [
CakeImageWidget(
imageUrl: widget.transactionDetailsViewModel.transactionAsset.iconPath ??
"",
width: 64,
height: 64),
TokenImageWidget(
imageUrl: widget
.transactionDetailsViewModel.transactionAsset.iconPath ??
"",
size: 64,
),
SizedBox(height: 10),
Text(
widget.transactionDetailsViewModel.formattedTitle +
@@ -0,0 +1,156 @@
import 'dart:async';
import 'dart:ui' as ui;
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:flutter/material.dart';
enum _IconShape {
alreadyCircular,
clipOnly,
hollowNeedsBackdrop,
}
class TokenImageWidget extends StatefulWidget {
const TokenImageWidget({
super.key,
required this.imageUrl,
required this.size,
this.errorWidget,
});
final String imageUrl;
final double size;
final Widget? errorWidget;
@override
State<TokenImageWidget> createState() => _TokenImageWidgetState();
}
class _TokenImageWidgetState extends State<TokenImageWidget> {
static final Map<String, _IconShape> _shapeCache = {};
_IconShape _shape = _IconShape.clipOnly;
@override
void initState() {
super.initState();
_resolveShape();
}
@override
void didUpdateWidget(TokenImageWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.imageUrl != widget.imageUrl) {
_resolveShape();
}
}
Future<void> _resolveShape() async {
final url = widget.imageUrl;
final cached = _shapeCache[url];
if (cached != null) {
if (mounted && _shape != cached) {
setState(() => _shape = cached);
}
return;
}
final shape = await _analyzeShape(url);
_shapeCache[url] = shape;
if (mounted) {
setState(() => _shape = shape);
}
}
Future<_IconShape> _analyzeShape(String url) async {
if (url.toLowerCase().endsWith('.svg')) return _IconShape.alreadyCircular;
final ImageProvider provider;
if (url.startsWith('assets/')) {
provider = AssetImage(url);
} else if (url.startsWith('http')) {
provider = NetworkImage(url);
} else {
return _IconShape.clipOnly;
}
try {
final completer = Completer<ui.Image>();
final stream = provider.resolve(ImageConfiguration.empty);
late final ImageStreamListener listener;
listener = ImageStreamListener(
(info, _) {
if (!completer.isCompleted) completer.complete(info.image);
},
onError: (error, _) {
if (!completer.isCompleted) completer.completeError(error);
},
);
stream.addListener(listener);
final image = await completer.future;
stream.removeListener(listener);
final byteData = await image.toByteData(format: ui.ImageByteFormat.rawRgba);
if (byteData == null) return _IconShape.clipOnly;
final totalPixels = image.width * image.height;
var transparentPixels = 0;
for (var i = 0; i < totalPixels; i++) {
final alpha = byteData.getUint8(i * 4 + 3);
if (alpha < 128) transparentPixels++;
}
final ratio = transparentPixels / totalPixels;
if (ratio > 0.15 && ratio < 0.30) {
return _IconShape.alreadyCircular;
}
if (ratio >= 0.30) {
return _IconShape.hollowNeedsBackdrop;
}
return _IconShape.clipOnly;
} catch (e) {
printV('TokenImageWidget: failed to analyze $url: $e');
return _IconShape.clipOnly;
}
}
@override
Widget build(BuildContext context) {
final image = SizedBox(
width: widget.size,
height: widget.size,
child: CakeImageWidget(
imageUrl: widget.imageUrl,
width: widget.size,
height: widget.size,
fit: BoxFit.cover,
filterQuality: FilterQuality.high,
errorWidget: widget.errorWidget,
),
);
switch (_shape) {
case _IconShape.alreadyCircular:
return image;
case _IconShape.clipOnly:
return ClipOval(child: image);
case _IconShape.hollowNeedsBackdrop:
return Container(
width: widget.size,
height: widget.size,
clipBehavior: Clip.antiAlias,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: image,
);
}
}
}
@@ -0,0 +1,83 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
class ChainChipStrip extends StatelessWidget {
const ChainChipStrip({
super.key,
required this.walletTypes,
required this.selected,
required this.onSelected,
});
final List<WalletType> walletTypes;
final WalletType? selected;
final ValueChanged<WalletType?> onSelected;
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.only(bottom: 4),
height: 48,
child: ListView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16),
children: [
_ChainChip(
label: S.of(context).picker_chip_all,
isSelected: selected == null,
onTap: () => onSelected(null),
),
for (final type in walletTypes)
_ChainChip(
label: walletTypeToString(type),
isSelected: selected == type,
onTap: () => onSelected(type),
),
],
),
);
}
}
class _ChainChip extends StatelessWidget {
const _ChainChip({
required this.label,
required this.isSelected,
required this.onTap,
});
final String label;
final bool isSelected;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
return Padding(
padding: const EdgeInsetsDirectional.only(end: 8),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(80),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
color: isSelected ? colors.primary : colors.surfaceContainer,
borderRadius: BorderRadius.circular(20),
),
child: Center(
child: Text(
label,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w500,
fontSize: isSelected ? 14 : 12,
letterSpacing: -0.07,
color: isSelected ? colors.onPrimary : colors.primary,
),
),
),
),
),
);
}
}
@@ -0,0 +1,77 @@
import 'package:cake_wallet/evm/evm.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/currency_groups.dart';
import 'package:cw_core/wallet_type.dart';
String chainNameForCurrency(CryptoCurrency c) {
if (c == CryptoCurrency.btcln) return 'Lightning';
final wt = cryptoCurrencyOrTokenToWalletType(c);
return wt != null ? walletTypeToString(wt) : (c.tag ?? '');
}
final Set<String> _stablecoinSymbols = {
for (final c in CryptoCurrency.all)
if (c.groups.contains(CurrencyGroups.stablecoin)) c.title.toUpperCase(),
};
bool isTrustedStablecoin(CryptoCurrency c) =>
(c.groups.contains(CurrencyGroups.stablecoin) ||
_stablecoinSymbols.contains(c.title.toUpperCase())) && !c.isPotentialScam;
const _kEvmDefaultTokenNatives = <CryptoCurrency>[
CryptoCurrency.baseEth,
CryptoCurrency.arbEth,
CryptoCurrency.maticpoly,
];
void appendEvmDefaultTokens(List<CryptoCurrency> into) {
if (evm == null) return;
String keyFor(CryptoCurrency c) {
final wt = cryptoCurrencyOrTokenToWalletType(c);
final chain = wt?.toString() ?? (c.tag ?? '').toUpperCase();
return '${c.title.toUpperCase()}|$chain';
}
final seen = <String>{for (final c in into) keyFor(c)};
for (final native in _kEvmDefaultTokenNatives) {
final chainId = getChainIdByCryptoCurrency(native);
if (chainId == null) continue;
for (final t in evm!.getDefaultTokensByChainId(chainId)) {
if (seen.add(keyFor(t))) into.add(t);
}
}
}
class CurrencyPickerBalance {
const CurrencyPickerBalance(
{required this.amount, this.fiat, this.fiatValue});
final String amount;
final String? fiat;
final double? fiatValue;
}
enum RecentsSource { none, trades, orders }
class CurrencyPickerArgs {
const CurrencyPickerArgs({
this.selected,
required this.items,
this.balanceByAsset,
this.filterByNetwork,
required this.onSelected,
required this.symbolResolver,
this.recentsSource = RecentsSource.none,
});
final CryptoCurrency? selected;
final List<CryptoCurrency> items;
final WalletType? filterByNetwork;
final void Function(CryptoCurrency) onSelected;
final String Function(CryptoCurrency) symbolResolver;
final Map<CryptoCurrency, CurrencyPickerBalance>? balanceByAsset;
final RecentsSource recentsSource;
bool get isPreFiltered => filterByNetwork != null;
}
@@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
class CurrencyPickerListContainer extends StatelessWidget {
const CurrencyPickerListContainer({super.key, required this.rows});
final List<Widget> rows;
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
final children = <Widget>[];
for (var i = 0; i < rows.length; i++) {
children.add(rows[i]);
if (i != rows.length - 1) {
children.add(Divider(
height: 1,
thickness: 1,
color: colors.surfaceContainerHigh,
indent: 56,
endIndent: 24,
));
}
}
return Container(
decoration: BoxDecoration(
color: colors.surfaceContainer,
borderRadius: BorderRadius.circular(18),
),
child: Column(mainAxisSize: MainAxisSize.min, children: children),
);
}
}
@@ -0,0 +1,139 @@
import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/material.dart';
class CurrencyPickerRow extends StatelessWidget {
const CurrencyPickerRow({
super.key,
required this.currency,
required this.isSelected,
required this.trailing,
required this.onTap,
this.chainPillLabel,
this.chainBadgePath,
});
final CryptoCurrency currency;
final bool isSelected;
final Widget trailing;
final VoidCallback onTap;
final String? chainPillLabel;
final String? chainBadgePath;
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
return InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
child: Row(
children: [
_IconWithBadge(currency: currency, badgePath: chainBadgePath),
const SizedBox(width: 12),
Expanded(
child: Row(
children: [
Flexible(
child: Text(
currency.fullName ?? currency.title,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w500,
),
),
),
if (chainPillLabel != null) ...[
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: colors.surfaceContainerHigh,
borderRadius: BorderRadius.circular(10),
),
child: Text(
chainPillLabel!,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontWeight: FontWeight.w500,
color: colors.onSecondaryContainer,
),
),
),
],
],
),
),
const SizedBox(width: 8),
trailing,
const SizedBox(width: 8),
Icon(
Icons.chevron_right,
size: 20,
color: isSelected ? colors.primary : colors.onSurfaceVariant,
),
],
),
),
);
}
}
class _IconWithBadge extends StatelessWidget {
const _IconWithBadge({required this.currency, required this.badgePath});
final CryptoCurrency currency;
final String? badgePath;
@override
Widget build(BuildContext context) {
final icon = TokenImageWidget(
imageUrl: currency.iconPath ?? '',
size: 32,
errorWidget: Container(
width: 32,
height: 32,
child: Center(
child: Text(
currency.title.length >= 2 ? currency.title.substring(0, 2) : currency.title,
),
),
),
);
if (badgePath == null) return icon;
return SizedBox(
width: 36,
height: 36,
child: Stack(
clipBehavior: Clip.none,
children: [
icon,
Positioned(
right: -2,
bottom: -2,
child: Container(
width: 18,
height: 18,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
border: Border.all(
color: Theme.of(context).colorScheme.surfaceContainerHigh,
width: 2,
),
color: Theme.of(context).colorScheme.onSurface,
),
padding: const EdgeInsets.all(2),
child: CakeImageWidget(
imageUrl: badgePath,
color: Theme.of(context).colorScheme.surface,
width: 13,
height: 13,
fit: BoxFit.cover,
),
),
),
],
),
);
}
}
@@ -0,0 +1,72 @@
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/material.dart';
class CurrencyPickerSearchField extends StatelessWidget {
const CurrencyPickerSearchField({
super.key,
required this.controller,
required this.hintText,
});
final TextEditingController controller;
final String hintText;
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Container(
height: 48,
padding: const EdgeInsets.symmetric(horizontal: 14),
decoration: BoxDecoration(
color: colors.surfaceContainer,
border: Border.all(color: colors.surfaceContainer, width: 1),
borderRadius: BorderRadius.circular(28),
),
child: Row(
children: [
Icon(Icons.search, size: 20, color: colors.primary),
const SizedBox(width: 10),
Expanded(
child: TextFormField(
controller: controller,
style: Theme.of(context).textTheme.bodyMedium,
decoration: InputDecoration(
isCollapsed: true,
border: InputBorder.none,
focusedBorder: InputBorder.none,
hintText: hintText,
hintStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: colors.onSurfaceVariant,
),
),
),
),
if (controller.text.isNotEmpty)
InkWell(
onTap: controller.clear,
customBorder: const CircleBorder(),
child: Padding(
padding: const EdgeInsets.all(4),
child: Icon(Icons.close, size: 16, color: colors.onSurfaceVariant),
),
),
],
),
),
);
}
}
bool currencyMatchesQuery(CryptoCurrency c, String query) {
if (query.isEmpty) return true;
final q = query.toLowerCase();
if (c.title.toLowerCase().contains(q)) return true;
if (c.name.toLowerCase().contains(q)) return true;
if (c.fullName?.toLowerCase().contains(q) ?? false) return true;
if (c.tag?.toLowerCase().contains(q) ?? false) return true;
if (chainNameForCurrency(c).toLowerCase().contains(q)) return true;
return false;
}
@@ -0,0 +1,59 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/multi_network_currency_picker.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/single_network_currency_picker.dart';
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
import 'package:flutter/material.dart';
class CurrencyPickerSheet extends StatelessWidget {
const CurrencyPickerSheet({super.key, required this.args});
final CurrencyPickerArgs args;
static Future<void> show({
required BuildContext context,
required CurrencyPickerArgs args,
}) {
return showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
useSafeArea: true,
enableDrag: false,
isDismissible: false,
backgroundColor: Colors.transparent,
builder: (_) => CurrencyPickerSheet(args: args),
);
}
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
return Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.vertical(top: Radius.circular(24)),
color: colors.surface,
),
child: SafeArea(
top: false,
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
ModalTopBar(
title: S.of(context).select_asset,
leadingIcon: const Icon(Icons.close),
onLeadingPressed: () => Navigator.of(context).maybePop(),
),
Expanded(
child: args.isPreFiltered
? SingleNetworkCurrencyPicker(args: args)
: MultiNetworkCurrencyPicker(args: args),
),
],
),
),
),
);
}
}
@@ -0,0 +1,314 @@
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_list_container.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_search_field.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/fiat_currency_row.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/fiat_currency_search_result.dart';
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/material.dart';
class FiatCurrencyPickerSheet extends StatefulWidget {
const FiatCurrencyPickerSheet({
super.key,
required this.selected,
required this.onSelected,
this.cryptoOption,
this.onCryptoSelected,
});
final Object? selected;
final ValueChanged<FiatCurrency> onSelected;
final CryptoCurrency? cryptoOption;
final ValueChanged<CryptoCurrency>? onCryptoSelected;
static const _popularOrder = <FiatCurrency>[
FiatCurrency.usd,
FiatCurrency.eur,
FiatCurrency.gbp,
FiatCurrency.jpy,
FiatCurrency.cny,
];
static Future<void> show({
required BuildContext context,
required Object? selected,
required ValueChanged<FiatCurrency> onSelected,
CryptoCurrency? cryptoOption,
ValueChanged<CryptoCurrency>? onCryptoSelected,
}) {
return showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
useSafeArea: true,
enableDrag: false,
backgroundColor: Colors.transparent,
builder: (_) => FiatCurrencyPickerSheet(
selected: selected,
onSelected: onSelected,
cryptoOption: cryptoOption,
onCryptoSelected: onCryptoSelected,
),
);
}
@override
State<FiatCurrencyPickerSheet> createState() => _FiatCurrencyPickerSheetState();
}
class _FiatCurrencyPickerSheetState extends State<FiatCurrencyPickerSheet> {
final TextEditingController _searchController = TextEditingController();
late final List<FiatCurrency> _allSorted = [...FiatCurrency.all]..sort(
(a, b) => a.fullName.toLowerCase().compareTo(b.fullName.toLowerCase()),
);
bool get _isSearching => _searchController.text.trim().isNotEmpty;
@override
void initState() {
super.initState();
_searchController.addListener(() => setState(() {}));
}
@override
void dispose() {
_searchController.dispose();
super.dispose();
}
bool _matches(FiatCurrency c, String query) {
if (query.isEmpty) return true;
final q = query.toLowerCase();
return c.fullName.toLowerCase().contains(q) || c.title.toLowerCase().contains(q);
}
void _selectFiatCurrency(FiatCurrency c) {
widget.onSelected(c);
Navigator.of(context).maybePop();
}
void _selectCryptoCurrency(CryptoCurrency c) {
widget.onCryptoSelected?.call(c);
Navigator.of(context).maybePop();
}
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
final selected = widget.selected;
final selectedFiat = selected is FiatCurrency ? selected : null;
final popular = FiatCurrencyPickerSheet._popularOrder
.where((c) => c != selectedFiat)
.toList(growable: false);
final query = _searchController.text.trim();
final filteredAll = _allSorted.where((c) => _matches(c, query)).toList(growable: false);
return Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.vertical(top: Radius.circular(24)),
color: colors.surface,
),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
ModalTopBar(
title: S.of(context).select_fiat_currency_title,
leadingIcon: const Icon(Icons.close),
onLeadingPressed: () => Navigator.of(context).maybePop(),
),
Expanded(
child: _isSearching
? FiatCurrencySearchResults(
items: filteredAll,
onSelected: _selectFiatCurrency,
selected: selectedFiat,
)
: FiatCurrencyPickerBody(
selected: selected,
cryptoOption: widget.cryptoOption,
popular: popular,
all: filteredAll,
onSelectedFiat: _selectFiatCurrency,
onSelectedCrypto: _selectCryptoCurrency,
),
),
CurrencyPickerSearchField(
controller: _searchController,
hintText: S.of(context).search,
),
const SizedBox(height: 24),
],
),
),
);
}
}
class FiatCurrencyPickerBody extends StatelessWidget {
const FiatCurrencyPickerBody({
super.key,
required this.selected,
required this.cryptoOption,
required this.popular,
required this.all,
required this.onSelectedFiat,
required this.onSelectedCrypto,
});
final Object? selected;
final CryptoCurrency? cryptoOption;
final List<FiatCurrency> popular;
final List<FiatCurrency> all;
final void Function(FiatCurrency) onSelectedFiat;
final void Function(CryptoCurrency) onSelectedCrypto;
@override
Widget build(BuildContext context) {
final selectedFiat = selected is FiatCurrency ? selected as FiatCurrency : null;
final selectedCrypto = selected is CryptoCurrency ? selected as CryptoCurrency : null;
final showCryptoSection = cryptoOption != null && selectedCrypto == null;
return ListView(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
children: [
if (selectedFiat != null || selectedCrypto != null) ...[
_SectionHeader(label: S.of(context).picker_section_selected),
CurrencyPickerListContainer(
rows: [
if (selectedFiat != null)
FiatCurrencyRow(
currency: selectedFiat,
isSelected: true,
onTap: () => onSelectedFiat(selectedFiat),
),
if (selectedCrypto != null)
_CryptoOptionRow(
currency: selectedCrypto,
isSelected: true,
onTap: () => onSelectedCrypto(selectedCrypto),
),
],
),
const SizedBox(height: 16),
],
if (showCryptoSection) ...[
_SectionHeader(label: S.of(context).picker_section_crypto),
CurrencyPickerListContainer(
rows: [
_CryptoOptionRow(
currency: cryptoOption!,
isSelected: false,
onTap: () => onSelectedCrypto(cryptoOption!),
),
],
),
const SizedBox(height: 16),
],
if (popular.isNotEmpty) ...[
_SectionHeader(label: S.of(context).picker_section_popular),
CurrencyPickerListContainer(
rows: [
for (final c in popular)
FiatCurrencyRow(
currency: c,
isSelected: false,
onTap: () => onSelectedFiat(c),
),
],
),
const SizedBox(height: 16),
],
_SectionHeader(label: S.of(context).picker_section_all_currencies),
CurrencyPickerListContainer(
rows: [
for (final c in all)
FiatCurrencyRow(
currency: c,
isSelected: c == selectedFiat,
onTap: () => onSelectedFiat(c),
),
],
),
],
);
}
}
class _SectionHeader extends StatelessWidget {
const _SectionHeader({required this.label});
final String label;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8),
child: Text(
label,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w500,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
);
}
}
class _CryptoOptionRow extends StatelessWidget {
const _CryptoOptionRow({
required this.currency,
required this.isSelected,
required this.onTap,
});
final CryptoCurrency currency;
final bool isSelected;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
return InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
child: Row(
children: [
CakeImageWidget(
imageUrl: currency.iconPath,
width: 28,
height: 28,
fit: BoxFit.cover,
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
currency.fullName ?? currency.title,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 2),
Text(
currency.title,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: colors.onSurfaceVariant,
),
),
],
),
),
if (isSelected) Icon(Icons.check, size: 20, color: colors.primary),
],
),
),
);
}
}
@@ -0,0 +1,67 @@
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:flutter/material.dart';
class FiatCurrencyRow extends StatelessWidget {
const FiatCurrencyRow({
required this.currency,
required this.isSelected,
required this.onTap,
});
final FiatCurrency currency;
final bool isSelected;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
return InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(4),
child: CakeImageWidget(
imageUrl: 'assets/images/flags/${currency.countryCode}.png',
width: 28,
height: 20,
fit: BoxFit.cover,
errorWidget: Container(
width: 28,
height: 20,
color: colors.surfaceContainer,
),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
currency.fullName,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 2),
Text(
currency.title,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: colors.onSurfaceVariant,
),
),
],
),
),
if (isSelected) Icon(Icons.check, size: 20, color: colors.primary),
],
),
),
);
}
}
@@ -0,0 +1,52 @@
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_list_container.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/fiat_currency_row.dart';
import 'package:flutter/material.dart';
class FiatCurrencySearchResults extends StatelessWidget {
const FiatCurrencySearchResults({
super.key,
required this.items,
required this.onSelected,
required this.selected,
});
final List<FiatCurrency> items;
final void Function(FiatCurrency) onSelected;
final FiatCurrency? selected;
@override
Widget build(BuildContext context) {
if (items.isEmpty) {
return Center(
child: Padding(
padding: const EdgeInsets.all(24),
child: Text(
S.of(context).picker_no_matches,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
),
);
}
return ListView(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
children: [
CurrencyPickerListContainer(
rows: [
for (final c in items)
FiatCurrencyRow(
currency: c,
isSelected: c == selected,
onTap: () => onSelected(c),
),
],
),
],
);
}
}
@@ -0,0 +1,679 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/chain_chip_strip.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/picker_recents_loader.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_list_container.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_row.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_search_field.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/picker_section_header.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/pill_grid.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/select_network_page.dart';
import 'package:cake_wallet/wallet_types.g.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/currency_groups.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
class MultiNetworkCurrencyPicker extends StatefulWidget {
const MultiNetworkCurrencyPicker({super.key, required this.args});
final CurrencyPickerArgs args;
@override
State<MultiNetworkCurrencyPicker> createState() => _MultiNetworkCurrencyPickerState();
}
class _MultiNetworkCurrencyPickerState extends State<MultiNetworkCurrencyPicker> {
bool _recentsLoaded = false;
WalletType? _selectedNetwork;
List<CryptoCurrency> _recents = const [];
final TextEditingController _searchController = TextEditingController();
bool get _isSearching => _searchController.text.trim().isNotEmpty;
late final List<WalletType> _networks = _computeNetworks();
List<WalletType> _computeNetworks() {
final counts = <WalletType, int>{};
for (final currency in widget.args.items) {
final walletType = cryptoCurrencyOrTokenToWalletType(currency);
if (walletType == null) continue;
counts[walletType] = (counts[walletType] ?? 0) + 1;
}
return counts.entries.where((e) => e.value >= 2).map((e) => e.key).toList(growable: false);
}
Set<CryptoCurrency> get _natives =>
{for (final walletType in availableWalletTypes) walletTypeToCryptoCurrency(walletType)};
@override
void initState() {
super.initState();
_searchController.addListener(() => setState(() {}));
_loadRecents();
}
@override
void dispose() {
_searchController.dispose();
super.dispose();
}
Future<void> _loadRecents() async {
try {
final recents = await PickerRecentsLoader.load(
source: widget.args.recentsSource,
visibleItems: widget.args.items,
);
if (!mounted) return;
setState(() {
_recents = recents;
_recentsLoaded = true;
});
} catch (e, s) {
printV('load picker recents failed: $e\n$s');
if (!mounted) return;
setState(() => _recentsLoaded = true);
}
}
bool _matchesNetwork(CryptoCurrency c) {
if (_selectedNetwork == null) return true;
return cryptoCurrencyOrTokenToWalletType(c) == _selectedNetwork;
}
List<CryptoCurrency> get _visibleItems {
final query = _searchController.text.trim();
return widget.args.items
.where(_hasSupportedChain)
.where((c) => currencyMatchesQuery(c, query) && _matchesNetwork(c))
.toList(growable: false);
}
bool _hasSupportedChain(CryptoCurrency c) {
if (c.tag == null) return true;
return cryptoCurrencyOrTokenToWalletType(c) != null;
}
void _selectCurrency(CryptoCurrency currency) {
widget.args.onSelected(currency);
Navigator.of(context).maybePop();
}
void _onStablecoinPillTapped(CryptoCurrency tapped) {
var variants = widget.args.items
.where((c) =>
isTrustedStablecoin(c) &&
c.title.toUpperCase() == tapped.title.toUpperCase() &&
cryptoCurrencyOrTokenToWalletType(c) != null)
.toList();
if (_selectedNetwork != null) {
final filtered = variants
.where((c) => cryptoCurrencyOrTokenToWalletType(c) == _selectedNetwork)
.toList(growable: false);
if (filtered.isNotEmpty) variants = filtered;
}
if (variants.length <= 1) {
_selectCurrency(variants.isNotEmpty ? variants.first : tapped);
return;
}
Navigator.of(context).push(
PageRouteBuilder<void>(
opaque: true,
transitionDuration: const Duration(milliseconds: 250),
reverseTransitionDuration: const Duration(milliseconds: 250),
pageBuilder: (_, __, ___) => SelectNetworkPage(
assetTitle: tapped.title,
assetFullName: tapped.fullName,
assetIconPath: tapped.iconPath,
variants: variants,
onSelected: widget.args.onSelected,
),
transitionsBuilder: (_, animation, __, child) => SlideTransition(
position: animation.drive(
Tween<Offset>(begin: const Offset(0, 1), end: Offset.zero)
.chain(CurveTween(curve: Curves.easeInOut)),
),
child: child,
),
),
);
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.max,
children: [
if (_networks.length > 1)
ChainChipStrip(
walletTypes: _networks,
selected: _selectedNetwork,
onSelected: (network) => setState(() => _selectedNetwork = network),
),
Expanded(
child: _MultiNetworkPickerBody(
items: _visibleItems,
isSearching: _isSearching,
recents: _recents,
recentsLoaded: _recentsLoaded,
natives: _natives,
selected: widget.args.selected,
symbolResolver: widget.args.symbolResolver,
onSelect: _selectCurrency,
onStablecoinTap: _onStablecoinPillTapped,
),
),
CurrencyPickerSearchField(
controller: _searchController,
hintText: S.of(context).search,
),
],
);
}
}
class _MultiNetworkPickerBody extends StatefulWidget {
const _MultiNetworkPickerBody({
required this.items,
required this.isSearching,
required this.recents,
required this.recentsLoaded,
required this.natives,
required this.selected,
required this.symbolResolver,
required this.onSelect,
required this.onStablecoinTap,
});
final bool isSearching;
final bool recentsLoaded;
final CryptoCurrency? selected;
final List<CryptoCurrency> items;
final List<CryptoCurrency> recents;
final Set<CryptoCurrency> natives;
final void Function(CryptoCurrency) onSelect;
final String Function(CryptoCurrency) symbolResolver;
final void Function(CryptoCurrency) onStablecoinTap;
@override
State<_MultiNetworkPickerBody> createState() => _MultiNetworkPickerBodyState();
}
class _MultiNetworkPickerBodyState extends State<_MultiNetworkPickerBody> {
static const int _previewCount = 3;
final ScrollController _scrollController = ScrollController();
bool _moreCryptosSectionExpanded = false;
bool _xstocksSectionExpanded = false;
@override
void initState() {
super.initState();
final selected = widget.selected;
if (selected == null) return;
final moreCryptos = _computeMoreCryptosSection(widget.items);
final xstocks = _computeXstocksSection(widget.items);
if (moreCryptos.indexOf(selected) >= _previewCount) {
_moreCryptosSectionExpanded = true;
}
if (xstocks.indexOf(selected) >= _previewCount) {
_xstocksSectionExpanded = true;
}
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
List<CryptoCurrency> _computeMoreCryptosSection(List<CryptoCurrency> from) {
final list = from
.where((c) =>
!widget.natives.contains(c) &&
cryptoCurrencyOrTokenToWalletType(c) == null &&
c.tag == null &&
!c.groups.contains(CurrencyGroups.stablecoin) &&
!c.groups.contains(CurrencyGroups.tokenizedStock))
.toList();
list.sort((a, b) => (a.fullName ?? a.title).toLowerCase().compareTo(
(b.fullName ?? b.title).toLowerCase(),
));
return list;
}
List<CryptoCurrency> _computeXstocksSection(List<CryptoCurrency> from) {
return from
.where((c) => c.groups.contains(CurrencyGroups.tokenizedStock))
.toList(growable: false);
}
@override
Widget build(BuildContext context) {
final items = widget.items;
final isSearching = widget.isSearching;
final recents = widget.recents;
final recentsLoaded = widget.recentsLoaded;
final natives = widget.natives;
final selected = widget.selected;
final symbolResolver = widget.symbolResolver;
final onSelect = widget.onSelect;
final onStablecoinTap = widget.onStablecoinTap;
if (items.isEmpty) {
return Center(
child: Padding(
padding: const EdgeInsets.all(24),
child: Text(
S.of(context).picker_no_matches,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
),
);
}
if (isSearching) {
return NotificationListener<ScrollNotification>(
onNotification: (_) => true,
child: ListView(
controller: _scrollController,
primary: false,
physics: const ClampingScrollPhysics(),
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
children: [
CurrencyPickerListContainer(
rows: [
for (final item in items)
CurrencyPickerRow(
currency: item,
isSelected: selected != null && selected == item,
chainPillLabel: _chainPillLabelFor(item),
chainBadgePath: _chainBadgePathFor(item),
trailing: _SymbolTrailing(
currency: item,
symbolResolver: symbolResolver,
),
onTap: () => onSelect(item),
),
],
),
],
),
);
}
final visibleRecents = recents.where(items.contains).toList(growable: false);
final seenStablecoinTitles = <String>{};
final stablecoins = items
.where((c) => isTrustedStablecoin(c) && seenStablecoinTitles.add(c.title.toUpperCase()))
.toList(growable: false);
final cryptocurrencies = items.where(natives.contains).toList(growable: false);
final moreCryptos = _computeMoreCryptosSection(items);
final xstocks = _computeXstocksSection(items);
final allAssets = [...items]..sort((a, b) => (a.fullName ?? a.title).toLowerCase().compareTo(
(b.fullName ?? b.title).toLowerCase(),
));
final section = _selectedSection(
recents: visibleRecents,
stablecoins: stablecoins,
cryptocurrencies: cryptocurrencies,
moreCryptos: moreCryptos,
xstocks: xstocks,
);
final moreCryptosVisible = _moreCryptosSectionExpanded
? moreCryptos
: moreCryptos.take(_previewCount).toList(growable: false);
final xstocksVisible =
_xstocksSectionExpanded ? xstocks : xstocks.take(_previewCount).toList(growable: false);
return NotificationListener<ScrollNotification>(
onNotification: (_) => true,
child: ListView(
controller: _scrollController,
primary: false,
physics: const ClampingScrollPhysics(),
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
children: [
if (recentsLoaded && visibleRecents.isNotEmpty)
_PickerSection(
title: S.of(context).picker_section_recents,
child: _RecentsRow(
items: visibleRecents,
selected: section == _SelSection.recents ? selected : null,
symbolResolver: symbolResolver,
onTap: onSelect,
),
),
if (stablecoins.isNotEmpty)
_PickerSection(
title: S.of(context).picker_section_stablecoins,
child: PillGrid(
items: stablecoins,
selected: section == _SelSection.stablecoins ? selected : null,
onTap: onStablecoinTap,
symbolResolver: symbolResolver,
),
),
if (cryptocurrencies.isNotEmpty)
_PickerSection(
title: S.of(context).picker_section_cryptocurrencies,
child: CurrencyPickerListContainer(
rows: [
for (final item in cryptocurrencies)
CurrencyPickerRow(
currency: item,
isSelected: section == _SelSection.cryptocurrencies && selected == item,
chainPillLabel: _chainPillLabelFor(item),
chainBadgePath: _chainBadgePathFor(item),
trailing: _SymbolTrailing(
currency: item,
symbolResolver: symbolResolver,
),
onTap: () => onSelect(item),
),
],
),
),
if (moreCryptos.isNotEmpty)
_PickerSection(
title: S.of(context).picker_section_more_cryptocurrencies,
child: CurrencyPickerListContainer(
rows: [
for (final item in moreCryptosVisible)
CurrencyPickerRow(
currency: item,
isSelected: section == _SelSection.moreCryptocurrencies && selected == item,
trailing: _SymbolTrailing(
currency: item,
symbolResolver: symbolResolver,
),
onTap: () => onSelect(item),
),
if (moreCryptos.length > _previewCount)
_SeeAllRow(
expanded: _moreCryptosSectionExpanded,
onTap: () => setState(
() => _moreCryptosSectionExpanded = !_moreCryptosSectionExpanded),
),
],
),
),
if (xstocks.isNotEmpty)
_PickerSection(
title: S.of(context).picker_section_tokenized_stocks,
child: CurrencyPickerListContainer(
rows: [
for (final item in xstocksVisible)
CurrencyPickerRow(
currency: item,
isSelected: section == _SelSection.xstocks && selected == item,
trailing: _SymbolTrailing(
currency: item,
symbolResolver: symbolResolver,
),
onTap: () => onSelect(item),
chainPillLabel: _chainPillLabelFor(item),
chainBadgePath: _chainBadgePathFor(item),
),
if (xstocks.length > _previewCount)
_SeeAllRow(
expanded: _xstocksSectionExpanded,
onTap: () =>
setState(() => _xstocksSectionExpanded = !_xstocksSectionExpanded),
),
],
),
),
if (allAssets.isNotEmpty)
_PickerSection(
title: S.of(context).picker_section_all_assets,
child: CurrencyPickerListContainer(
rows: [
for (final item in allAssets)
CurrencyPickerRow(
currency: item,
isSelected: section == _SelSection.allAssets && selected == item,
chainPillLabel: _chainPillLabelFor(item),
chainBadgePath: _chainBadgePathFor(item),
trailing: _SymbolTrailing(
currency: item,
symbolResolver: symbolResolver,
),
onTap: () => onSelect(item),
),
],
),
),
],
),
);
}
String? _chainPillLabelFor(CryptoCurrency c) {
if (c == CryptoCurrency.btcln) return _shortChainLabel(c);
if (_isL2NativeEth(c)) return _shortChainLabel(c);
if (widget.natives.contains(c)) return null;
if (cryptoCurrencyOrTokenToWalletType(c) == null) return null;
return _shortChainLabel(c);
}
String _shortChainLabel(CryptoCurrency c) {
if (cryptoCurrencyOrTokenToWalletType(c) == WalletType.bsc) return 'BSC';
return chainNameForCurrency(c);
}
String? _chainBadgePathFor(CryptoCurrency c) {
if (_isL2NativeEth(c)) return c.chainIconPath;
if (widget.natives.contains(c)) return null;
final wt = cryptoCurrencyOrTokenToWalletType(c);
if (wt == null) return null;
return c.chainIconPath ?? walletTypeToCryptoCurrency(wt).chainIconPath;
}
bool _isL2NativeEth(CryptoCurrency c) =>
c == CryptoCurrency.arbEth || c == CryptoCurrency.baseEth;
_SelSection? _selectedSection({
required List<CryptoCurrency> recents,
required List<CryptoCurrency> stablecoins,
required List<CryptoCurrency> cryptocurrencies,
required List<CryptoCurrency> moreCryptos,
required List<CryptoCurrency> xstocks,
}) {
final s = widget.selected;
if (s == null) return null;
if (recents.contains(s)) return _SelSection.recents;
if (stablecoins.any((c) => c.title.toUpperCase() == s.title.toUpperCase())) {
return _SelSection.stablecoins;
}
if (cryptocurrencies.contains(s)) return _SelSection.cryptocurrencies;
if (moreCryptos.contains(s)) return _SelSection.moreCryptocurrencies;
if (xstocks.contains(s)) return _SelSection.xstocks;
return _SelSection.allAssets;
}
}
class _PickerSection extends StatelessWidget {
const _PickerSection({required this.title, required this.child});
final String title;
final Widget child;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
PickerSectionHeader(title: title),
child,
const SizedBox(height: 16),
],
);
}
}
class _SymbolTrailing extends StatelessWidget {
const _SymbolTrailing({
required this.currency,
required this.symbolResolver,
});
final CryptoCurrency currency;
final String Function(CryptoCurrency) symbolResolver;
@override
Widget build(BuildContext context) {
return Text(
symbolResolver(currency),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w500,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
);
}
}
class _SeeAllRow extends StatelessWidget {
const _SeeAllRow({
required this.expanded,
required this.onTap,
});
final bool expanded;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
final label = expanded ? S.of(context).picker_show_less : S.of(context).picker_see_all;
return InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
child: Row(
children: [
Expanded(
child: Text(
label,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w500,
color: colors.primary,
),
),
),
],
),
),
);
}
}
class _RecentsRow extends StatelessWidget {
const _RecentsRow({
required this.items,
required this.selected,
required this.symbolResolver,
required this.onTap,
});
final List<CryptoCurrency> items;
final CryptoCurrency? selected;
final String Function(CryptoCurrency) symbolResolver;
final void Function(CryptoCurrency) onTap;
@override
Widget build(BuildContext context) {
return SizedBox(
height: 44,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: items.length,
separatorBuilder: (_, __) => const SizedBox(width: 8),
itemBuilder: (_, i) {
final item = items[i];
return _RecentPill(
currency: item,
label: symbolResolver(item),
isSelected: selected != null && selected == item,
onTap: () => onTap(item),
);
},
),
);
}
}
class _RecentPill extends StatelessWidget {
const _RecentPill({
required this.currency,
required this.label,
required this.isSelected,
required this.onTap,
});
final CryptoCurrency currency;
final String label;
final bool isSelected;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(80),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration(
color: colors.surfaceContainer,
borderRadius: BorderRadius.circular(80),
border: isSelected ? Border.all(color: colors.primary, width: 1.5) : null,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
TokenImageWidget(
imageUrl: currency.iconPath ?? '',
size: 24,
),
const SizedBox(width: 8),
Text(
label,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
],
),
),
);
}
}
enum _SelSection {
recents,
stablecoins,
cryptocurrencies,
moreCryptocurrencies,
xstocks,
allAssets,
}
@@ -0,0 +1,97 @@
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/exchange/trade_state.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.dart';
import 'package:cake_wallet/order/order.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:hive/hive.dart';
class PickerRecentsLoader {
const PickerRecentsLoader._();
static const int defaultLimit = 6;
static final Set<TradeState> _executedTradeStates = {
TradeState.complete,
TradeState.completed,
TradeState.finished,
TradeState.success,
TradeState.settled,
TradeState.traded,
};
static Future<List<CryptoCurrency>> load({
required RecentsSource source,
required List<CryptoCurrency> visibleItems,
int limit = defaultLimit,
}) async {
switch (source) {
case RecentsSource.none:
return const [];
case RecentsSource.trades:
return _fromTrades(visibleItems, limit);
case RecentsSource.orders:
return _fromOrders(visibleItems, limit);
}
}
static Future<List<CryptoCurrency>> _fromTrades(
List<CryptoCurrency> visibleItems,
int limit,
) async {
final trades = await Trade.getAll();
final ordered = trades
.where((t) => _executedTradeStates.contains(t.state))
.toList()
..sort((a, b) => (b.createdAt ?? DateTime.fromMillisecondsSinceEpoch(0))
.compareTo(a.createdAt ?? DateTime.fromMillisecondsSinceEpoch(0)));
return _collect(
ordered.expand((t) => [t.from, t.to]),
visibleItems,
limit,
);
}
static Future<List<CryptoCurrency>> _fromOrders(
List<CryptoCurrency> visibleItems,
int limit,
) async {
final orders = Hive.box<Order>(Order.boxName).values.toList()
..sort((a, b) => b.createdAt.compareTo(a.createdAt));
return _collect(
orders.expand((o) => [_tryResolve(o.from), _tryResolve(o.to)]),
visibleItems,
limit,
);
}
static List<CryptoCurrency> _collect(
Iterable<CryptoCurrency?> candidates,
List<CryptoCurrency> visibleItems,
int limit,
) {
final visibleSet = visibleItems.toSet();
final seen = <CryptoCurrency>{};
final collected = <CryptoCurrency>[];
for (final candidate in candidates) {
if (candidate == null) continue;
if (!visibleSet.contains(candidate)) continue;
if (!seen.add(candidate)) continue;
collected.add(candidate);
if (collected.length >= limit) break;
}
return collected;
}
static CryptoCurrency? _tryResolve(String? raw) {
if (raw == null || raw.isEmpty) return null;
try {
return CryptoCurrency.fromString(raw);
} catch (_) {
return null;
}
}
}
@@ -0,0 +1,21 @@
import 'package:flutter/material.dart';
class PickerSectionHeader extends StatelessWidget {
const PickerSectionHeader({super.key, required this.title});
final String title;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(4, 12, 4, 8),
child: Text(
title,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w500,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
);
}
}
@@ -0,0 +1,99 @@
import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/material.dart';
class PillGrid extends StatelessWidget {
const PillGrid({
super.key,
required this.items,
required this.selected,
required this.onTap,
required this.symbolResolver,
});
final List<CryptoCurrency> items;
final CryptoCurrency? selected;
final ValueChanged<CryptoCurrency> onTap;
final String Function(CryptoCurrency c) symbolResolver;
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
const gap = 8.0;
final cardWidth = (constraints.maxWidth - gap) / 2;
return Wrap(
spacing: gap,
runSpacing: gap,
children: [
for (final item in items)
SizedBox(
width: cardWidth,
child: _PillCard(
currency: item,
isSelected: selected != null && selected == item,
label: symbolResolver(item),
onTap: () => onTap(item),
),
),
],
);
},
);
}
}
class _PillCard extends StatelessWidget {
const _PillCard({
required this.currency,
required this.isSelected,
required this.label,
required this.onTap,
});
final CryptoCurrency currency;
final bool isSelected;
final String label;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(80),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
decoration: BoxDecoration(
color: colors.surfaceContainer,
borderRadius: BorderRadius.circular(80),
border: isSelected ? Border.all(color: colors.primary, width: 1.5) : null,
),
child: Row(
children: [
TokenImageWidget(
imageUrl: currency.iconPath ?? '',
size: 24,
),
const SizedBox(width: 8),
Expanded(
child: Text(
label,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
),
Icon(
Icons.chevron_right,
size: 18,
color: colors.onSurfaceVariant,
),
],
),
),
);
}
}
@@ -0,0 +1,140 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_list_container.dart';
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
class SelectNetworkPage extends StatelessWidget {
const SelectNetworkPage({
super.key,
required this.assetTitle,
required this.assetFullName,
required this.assetIconPath,
required this.variants,
required this.onSelected,
});
final String assetTitle;
final String? assetFullName;
final String? assetIconPath;
final List<CryptoCurrency> variants;
final ValueChanged<CryptoCurrency> onSelected;
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
return Scaffold(
backgroundColor: colors.surface,
body: SafeArea(
child: Column(
children: [
ModalTopBar(
title: S.of(context).select_network_title,
leadingIcon: const Icon(Icons.arrow_back),
onLeadingPressed: () => Navigator.of(context).maybePop(),
),
const SizedBox(height: 24),
CakeImageWidget(
imageUrl: assetIconPath,
width: 75,
height: 75,
fit: BoxFit.cover,
),
const SizedBox(height: 12),
Text(
assetTitle,
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 32),
child: Text(
S.of(context).select_network_subtitle,
textAlign: TextAlign.center,
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(color: colors.onSurfaceVariant),
),
),
const SizedBox(height: 24),
Expanded(
child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
children: [
CurrencyPickerListContainer(
rows: [
for (final variant in variants)
_NetworkRow(
variant: variant,
onTap: () {
final nav = Navigator.of(context);
onSelected(variant);
if (nav.canPop()) nav.pop();
if (nav.canPop()) nav.pop();
},
),
],
),
],
),
),
],
),
),
);
}
}
class _NetworkRow extends StatelessWidget {
const _NetworkRow({required this.variant, required this.onTap});
final CryptoCurrency variant;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
final wt = cryptoCurrencyOrTokenToWalletType(variant);
final networkIconPath =
wt != null ? getCryptoCurrencyIconForWalletListItem(wt) : variant.iconPath;
final networkName = chainNameForCurrency(variant);
return InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
child: Row(
children: [
CakeImageWidget(
imageUrl: networkIconPath,
width: 28,
height: 28,
fit: BoxFit.cover,
),
const SizedBox(width: 12),
Expanded(
child: Text(
networkName,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w500,
),
),
),
Icon(
Icons.chevron_right,
size: 20,
color: colors.onSurfaceVariant,
),
],
),
),
);
}
}
@@ -0,0 +1,162 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_list_container.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_row.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_search_field.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/picker_section_header.dart';
import 'package:cake_wallet/reactions/wallet_utils.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
class SingleNetworkCurrencyPicker extends StatefulWidget {
const SingleNetworkCurrencyPicker({super.key, required this.args});
final CurrencyPickerArgs args;
@override
State<SingleNetworkCurrencyPicker> createState() => _SingleNetworkCurrencyPickerState();
}
class _SingleNetworkCurrencyPickerState extends State<SingleNetworkCurrencyPicker> {
CurrencyPickerArgs get _args => widget.args;
WalletType get _network => _args.filterByNetwork!;
final TextEditingController _searchController = TextEditingController();
@override
void initState() {
super.initState();
_searchController.addListener(() => setState(() {}));
}
@override
void dispose() {
_searchController.dispose();
super.dispose();
}
void _selectCurrency(CryptoCurrency currency) {
_args.onSelected(currency);
Navigator.of(context).maybePop();
}
double _fiatValueFor(CryptoCurrency c) => _args.balanceByAsset?[c]?.fiatValue ?? 0;
@override
Widget build(BuildContext context) {
final native = walletTypeToCryptoCurrency(_network);
final query = _searchController.text.trim();
final tokens = _args.items
.where((c) => c != native)
.where((c) => currencyMatchesQuery(c, query))
.toList();
final insertOrder = {for (var i = 0; i < tokens.length; i++) tokens[i]: i};
tokens.sort((a, b) {
final av = _fiatValueFor(a);
final bv = _fiatValueFor(b);
if (bv != av) return bv.compareTo(av);
return (insertOrder[a] ?? 0).compareTo(insertOrder[b] ?? 0);
});
final nativeMatches = currencyMatchesQuery(native, query);
return Column(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: (nativeMatches || tokens.isNotEmpty)
? ListView(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
children: [
if (nativeMatches) ...[
PickerSectionHeader(title: S.of(context).picker_section_gas_token),
CurrencyPickerListContainer(
rows: [
CurrencyPickerRow(
currency: native,
isSelected: _args.selected == native,
trailing: _BalanceTrailing(balance: _args.balanceByAsset?[native]),
onTap: () => _selectCurrency(native),
),
],
),
const SizedBox(height: 16),
],
if (tokens.isNotEmpty) ...[
PickerSectionHeader(
title: S
.of(context)
.picker_section_tokens_standard(tokenStandardFor(_network)),
),
CurrencyPickerListContainer(
rows: [
for (final t in tokens)
CurrencyPickerRow(
currency: t,
isSelected: _args.selected == t,
trailing: _BalanceTrailing(balance: _args.balanceByAsset?[t]),
onTap: () => _selectCurrency(t),
),
],
),
],
],
)
: Center(
child: Padding(
padding: const EdgeInsets.all(24),
child: Text(
S.of(context).picker_no_matches,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
),
),
),
CurrencyPickerSearchField(
controller: _searchController,
hintText: S.of(context).search,
),
],
);
}
}
class _BalanceTrailing extends StatelessWidget {
const _BalanceTrailing({required this.balance});
final CurrencyPickerBalance? balance;
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
final amount = balance?.amount ?? '—';
final fiat = balance?.fiat;
return Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: [
Text(
amount,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w500,
),
),
if (fiat != null && fiat.isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: 2),
child: Text(
fiat,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontWeight: FontWeight.w400,
color: colors.onSurfaceVariant,
),
),
),
],
);
}
}
@@ -127,6 +127,7 @@ class _LineTabSwitcherState extends State<LineTabSwitcher> {
}).toList()
)
),
if(widget.tabs.length > 1)
Container(
width: totalWidth,
height: 2,
@@ -1,15 +1,16 @@
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_sheet.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/fiat_currency_picker_sheet.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter_svg/svg.dart';
class ReceiveAmountModal extends StatefulWidget {
const ReceiveAmountModal(
@@ -84,14 +85,14 @@ class _ReceiveAmountModalState extends State<ReceiveAmountModal> {
Row(
spacing: 8,
children: [
Image.asset(
widget.walletAddressListViewModel.tokenCurrency
?.iconPath ??
widget.walletAddressListViewModel.currencies.first
.iconPath ??
"",
width: 32,
height: 32),
TokenImageWidget(
imageUrl: widget.walletAddressListViewModel.tokenCurrency
?.iconPath ??
widget.walletAddressListViewModel.currencies.first
.iconPath ??
"",
size: 32,
),
Text((widget.walletAddressListViewModel.tokenCurrency ??
widget.walletAddressListViewModel.currencies.first
as CryptoCurrency)
@@ -216,35 +217,31 @@ class _ReceiveAmountModalState extends State<ReceiveAmountModal> {
);
}
void _presentTokenCurrencyPicker(BuildContext context) async {
await showPopUp(
builder: (_) => CurrencyPicker(
selectedAtIndex: widget.walletAddressListViewModel.tokenCurrencyIndex,
items: widget.walletAddressListViewModel.tokenCurrencies,
hintText: S.of(context).search_currency,
onItemSelected: widget.walletAddressListViewModel.setTokenCurrency,
),
void _presentTokenCurrencyPicker(BuildContext context) {
final items = widget.walletAddressListViewModel.tokenCurrencies
.whereType<CryptoCurrency>()
.toList();
CurrencyPickerSheet.show(
context: context,
args: CurrencyPickerArgs(
items: items,
selected: widget.walletAddressListViewModel.tokenCurrency,
onSelected: widget.walletAddressListViewModel.setTokenCurrency,
symbolResolver: widget.walletAddressListViewModel.amountParsingProxy.getCryptoSymbol,
),
);
}
void _presentFiatCurrencyPicker(BuildContext context) async {
final currs = widget.walletAddressListViewModel.currencies.toList();
// the first options should be the crypto and the default fiat currency
currs.sort((a, b) {
if(a == widget.walletAddressListViewModel.fiatCurrency || a is CryptoCurrency) return -1;
if(b == widget.walletAddressListViewModel.fiatCurrency || a is CryptoCurrency) return 1;
return 0;
});
await showPopUp(
builder: (_) => CurrencyPicker(
selectedAtIndex: currs.indexOf(widget.walletAddressListViewModel.selectedCurrency),
items: currs,
hintText: S.of(context).search_currency,
onItemSelected: widget.walletAddressListViewModel.selectCurrency,
),
void _presentFiatCurrencyPicker(BuildContext context) {
final selected = widget.walletAddressListViewModel.selectedCurrency;
final cryptoOption =
widget.walletAddressListViewModel.currencies.whereType<CryptoCurrency>().firstOrNull;
FiatCurrencyPickerSheet.show(
context: context,
selected: selected,
cryptoOption: cryptoOption,
onSelected: widget.walletAddressListViewModel.selectCurrency,
onCryptoSelected: widget.walletAddressListViewModel.selectCurrency,
);
}
}
@@ -2,6 +2,7 @@ import 'dart:math';
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/wallet_type.dart';
@@ -177,10 +178,9 @@ class InfoboxCurrencyRow extends StatelessWidget {
color: Theme.of(context).colorScheme.surfaceContainer,
width: iconBorder),
borderRadius: BorderRadius.circular(9999999)),
child: CakeImageWidget(
imageUrl: entry.value.iconPath,
width: 24,
height: 24,
child: TokenImageWidget(
imageUrl: entry.value.iconPath ?? '',
size: 24,
),
),
))
@@ -33,8 +33,9 @@ class ReceiveTokenDisplay extends StatelessWidget {
child: Row(
spacing:4,
children: [
CakeImageWidget(imageUrl:
"assets/new-ui/chain_badges/${walletTypeToString(addressListViewModel.wallet.type).toLowerCase()}.svg",
CakeImageWidget(
imageUrl:
"assets/new-ui/chain_badges/${addressListViewModel.wallet.type == WalletType.bsc ? 'bnb' : walletTypeToString(addressListViewModel.wallet.type).toLowerCase()}.svg",
width: 16,
height: 16,
colorFilter: ColorFilter.mode(
@@ -0,0 +1,105 @@
import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart';
import 'package:cake_wallet/wallet_types.g.dart';
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:url_launcher/url_launcher.dart';
class ScanPageNetworkList extends StatelessWidget {
const ScanPageNetworkList({super.key});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.vertical(top: Radius.circular(18)),
color: Theme.of(context).colorScheme.surface),
child: Column(
children: [
ModalTopBar(
title: S.of(context).compatible_services,
leadingIcon: Icon(Icons.arrow_back_ios_new),
onLeadingPressed: Navigator.of(context).pop,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
spacing: 32,
children: [
Column(
spacing: 24,
children: [
CakeImageWidget(
imageUrl: "assets/new-ui/scan_service.svg",
width: 75,
height: 75,
colorFilter:
ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn),
),
Text(
S.of(context).compatible_services_desc,
style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
textAlign: TextAlign.center,
)
],
),
NewListSections(sections: {
"base": [
ListItemRegularRow(
keyValue: "addrs",
label: S.of(context).crypto_addresses,
showArrow: false,
iconPath: "assets/new-ui/navbar/wallets.svg",
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
subtitle: "${availableWalletTypes.length} ${S.of(context).networks}"),
ListItemRegularRow(
keyValue: "invoices",
label: S.of(context).payment_invoices,
showArrow: false,
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
iconPath: "assets/new-ui/payment_invoices.svg"),
ListItemRegularRow(
keyValue: "contacts",
label: S.of(context).contacts,
showArrow: false,
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
iconPath: "assets/new-ui/navbar/contacts.svg"),
ListItemRegularRow(
keyValue: "nodes",
label: S.of(context).nodes,
showArrow: false,
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
iconPath: "assets/new-ui/node_minimal.svg"),
],
"extra": [
ListItemRegularRow(
iconPath: "assets/new-ui/wc_min.svg",
keyValue: "wc",
label: "WalletConnect",
subtitle: S.of(context).wc_desc,
trailingIconPath: "assets/new-ui/external_link.svg",
onTap: () {
launchUrl(Uri.https("walletconnect.com"));
}),
ListItemRegularRow(
iconPath: "assets/new-ui/ocp_min.svg",
keyValue: "ocp",
label: "OpenCryptoPay",
subtitle: S.of(context).ocp_desc,
trailingIconPath: "assets/new-ui/external_link.svg",
onTap: () {
launchUrl(Uri.https("opencryptopay.io"));
})
]
})
],
),
)
],
),
);
}
}
@@ -126,7 +126,7 @@ class _L2ActionWalletSelectorState extends State<L2ActionWalletSelector> {
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: WalletRow(
currencyIconPath:
walletTypeToCryptoCurrency(item.type).iconPath ?? "",
getCryptoCurrencyIconForWalletListItem(item.type),
walletName: item.name,
isCurrent: item.name == widget.sendViewModel.wallet.name,
isSelected: _selectedWalletIndex == index && !textEntered,
@@ -1,3 +1,4 @@
import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
import 'package:cake_wallet/new-ui/widgets/send_page/floating_icon_button.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:flutter/material.dart';
@@ -109,7 +110,7 @@ class _NewSendAmountInputState extends State<NewSendAmountInput> {
spacing: 8,
children: [
if (widget.hasPicker && widget.currencyIconPath.isNotEmpty)
CakeImageWidget(imageUrl: widget.currencyIconPath, width: 24, height: 24),
TokenImageWidget(imageUrl: widget.currencyIconPath, size: 24),
Text(widget.currency),
if (widget.hasPicker)
CakeImageWidget(imageUrl:
@@ -2,17 +2,25 @@ import 'dart:io';
import 'package:cake_wallet/core/address_validator.dart';
import 'package:cake_wallet/core/utilities.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/contact.dart';
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/animated_dropdown.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/transaction_details_modal.dart';
import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
import 'package:cake_wallet/new-ui/widgets/send_page/send_confirm_bottom_widget.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cake_wallet/utils/address_formatter.dart';
import 'package:cake_wallet/view_model/send/send_view_model.dart';
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/crypto_amount_format.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter_svg/flutter_svg.dart';
@@ -31,12 +39,14 @@ class SendConfirmSheet extends StatefulWidget {
}
class _SendConfirmSheetState extends State<SendConfirmSheet> {
bool _committed = false;
void initState() {
super.initState();
reaction((context) => widget.sendViewModel.state, (state) {
if (state is TransactionCommitted) {
Future.delayed(const Duration(seconds: 2), () {
if (mounted) Navigator.of(context).maybePop();
reaction((_)=>widget.sendViewModel.state, (state){
if(state is TransactionCommitted) {
setState(() {
_committed = true;
});
}
});
@@ -60,33 +70,48 @@ class _SendConfirmSheetState extends State<SendConfirmSheet> {
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
child: SafeArea(
child: Observer(
child: Observer(
builder: (_) {
final commited = widget.sendViewModel.state is TransactionCommitted;
return Stack(
fit: StackFit.loose,
children: [
Positioned.fill(
return AnimatedSize(
duration: const Duration(milliseconds: 300),
curve: Curves.easeOutCubic,
alignment: Alignment.topCenter,
clipBehavior: Clip.hardEdge,
child: Stack(
clipBehavior: Clip.none,
children: [
Align(
alignment: Alignment.topCenter,
heightFactor: _committed ? 0.0 : 1.0,
child: AnimatedSlide(
offset: commited ? Offset.zero : const Offset(1, 0),
duration: const Duration(milliseconds: 300),
curve: Curves.easeOutCubic,
child: const TransactionCommitedScreen(),
)),
AnimatedSlide(
offset: commited ? const Offset(-1, 0) : Offset.zero,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOutCubic,
child: SendTransactionDetails(
sendViewModel: widget.sendViewModel,
isPage: widget.isPage,
title: widget.title,
iconPath: widget.iconPath),
),
],
offset: _committed ? const Offset(-1, 0) : Offset.zero,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOutCubic,
child: SendTransactionDetails(
sendViewModel: widget.sendViewModel,
isPage: widget.isPage,
title: widget.title,
iconPath: widget.iconPath,
),
),
),
Align(
alignment: Alignment.topCenter,
heightFactor: _committed ? 1.0 : 0.0,
child: AnimatedSlide(
offset: _committed ? Offset.zero : const Offset(1, 0),
duration: const Duration(milliseconds: 300),
curve: Curves.easeOutCubic,
child: TransactionCommitedScreen(
sendViewModel: widget.sendViewModel,
),
),
),
],
),
);
},
),
)
),
),
),
@@ -178,7 +203,7 @@ class SendTransactionDetails extends StatelessWidget {
}),
sendViewModel.selectedCryptoCurrency)
: sendViewModel.amountParsingProxy.getDisplayCryptoAmount(
formatAmount(transaction.amountFormatted), sendViewModel.selectedCryptoCurrency);
formatAmount(transaction.amountFormatted), sendViewModel.selectedCryptoCurrency).withMaxDecimals(8);
final fee =
"${(transaction == null) ? sendViewModel.amountParsingProxy.getDisplayCryptoStringFromBigInt(sumByBigInt(
@@ -404,29 +429,150 @@ class SendTransactionDetails extends StatelessWidget {
}
}
class TransactionCommitedScreen extends StatelessWidget {
const TransactionCommitedScreen({super.key});
class TransactionCommitedScreen extends StatefulWidget {
const TransactionCommitedScreen({super.key, this.sendViewModel});
final SendViewModel? sendViewModel;
@override
State<TransactionCommitedScreen> createState() => _TransactionCommitedScreenState();
}
class _TransactionCommitedScreenState extends State<TransactionCommitedScreen> {
bool _isNoteButtonLoading = false;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 24.0),
child: Column(
return Observer(
builder: (_) => Column(
spacing: 12,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
SizedBox(height: 12,),
Text(
S.of(context).transaction_sent_new,
style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600),
),
Image.asset(width: 256, height: 256, "assets/images/birthday_cake.png")
SizedBox(),
CakeImageWidget(width: 200, height: 200, imageUrl: "assets/new-ui/birthday_cake.svg"),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
spacing: 12,
children: [
if (widget.sendViewModel != null)
Row(
spacing: 8,
children: [
if (!(widget.sendViewModel!.checkIfAddressIsAContact(
widget.sendViewModel!.outputs.first.address)) &&
!(widget.sendViewModel!.outputs.first.isParsedAddress))
TransactionCommittedScreenActionButton(
text: S.of(context).save_contact,
iconPath: "assets/new-ui/save_contact.svg",
onTap: () {
Navigator.of(context).pushNamed(Routes.addressBookAddContact,
arguments: ContactRecord(
CakeHive.box<Contact>(Contact.boxName),
Contact(
name: "",
address: widget.sendViewModel!.outputs.first.address,
type: widget.sendViewModel!.wallet.currency)));
}),
// lightning has to be hacked in here as it doesn't get added to tx history for a few secs after committing.
if (widget.sendViewModel!.transactionInfo != null ||
widget.sendViewModel!.currency == CryptoCurrency.btcln)
TransactionCommittedScreenActionButton(
text: S.of(context).add_a_note,
iconPath: "assets/new-ui/add_note.svg",
isLoading: _isNoteButtonLoading,
onTap: () async {
setState(() {
_isNoteButtonLoading = true;
});
// for ln, we want to show the button and just have it wait until it appears in tx history
// for other currs this is instant
await asyncWhen((_) => widget.sendViewModel!.transactionInfo != null);
setState(() {
_isNoteButtonLoading = false;
});
final page = getIt.get<TransactionDetailsModal>(
param1: widget.sendViewModel!.transactionInfo!, param2: true);
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) => page);
}),
],
),
NewPrimaryButton(
onPressed: Navigator.of(context).maybePop,
text: S.of(context).done,
color: Theme.of(context).colorScheme.primary,
textColor: Theme.of(context).colorScheme.onPrimary),
SizedBox(height: 12,)
],
),
),
],
),
);
}
}
class TransactionCommittedScreenActionButton extends StatelessWidget {
const TransactionCommittedScreenActionButton(
{super.key,
required this.text,
required this.iconPath,
required this.onTap,
this.isLoading = false});
final String text;
final String iconPath;
final VoidCallback onTap;
final bool isLoading;
@override
Widget build(BuildContext context) {
return Flexible(
child: GestureDetector(
onTap: onTap,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: Theme.of(context).colorScheme.surfaceContainer),
child: Padding(
padding: EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 10,
children: [
isLoading
? CupertinoActivityIndicator()
: CakeImageWidget(
imageUrl: iconPath,
width: 24,
height: 24,
colorFilter: ColorFilter.mode(
Theme.of(context).colorScheme.primary, BlendMode.srcIn),
),
Text(
text,
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.w500),
)
],
),
),
)));
}
}
class MultiSendAddressPreview extends StatefulWidget {
const MultiSendAddressPreview(
@@ -105,7 +105,7 @@ class _SwapAddressSelectionModalState extends State<SwapAddressSelectionModal> {
!widget.exchangeViewModel.isSendFromExternal;
}
final String currencyIconPath = walletTypeToCryptoCurrency(item.type).iconPath ?? "";
final String currencyIconPath = getCryptoCurrencyIconForWalletListItem(item.type);
final bool hasAccounts =
item.type == WalletType.monero && widget.isSelectingReceiver;
@@ -123,18 +123,27 @@ class _SwapSendExternalModalState extends State<SwapSendExternalModal> {
largeQrMode = !largeQrMode;
});
},
child: AnimatedContainer(
child: TweenAnimationBuilder<double>(
duration: const Duration(milliseconds: 400),
curve: Curves.easeOutCubic,
width: resolvedSize,
height: resolvedSize,
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: QrImage(
embeddedImagePath: widget.from.iconPath,
data: uri.toString(),
),
tween: Tween<double>(
begin: resolvedSize,
end: resolvedSize,
),
builder: (context, animatedSize, child) {
return SizedBox(
width: animatedSize,
height: animatedSize,
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: QrImage(
size: animatedSize,
embeddedImagePath: widget.from.iconPath,
data: uri.toString(),
),
),
);
},
),
),
AddressFormatter.buildSegmentedAddress(
+18 -13
View File
@@ -5,6 +5,22 @@ import 'package:cake_wallet/src/screens/wallet_connect/services/chain_service/so
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/evm/evm.dart';
const List<WalletType> walletConnectCompatibleChains = [
WalletType.ethereum,
WalletType.polygon,
WalletType.base,
WalletType.arbitrum,
WalletType.bsc,
WalletType.solana,
];
String walletConnectCompatibleChainsLabel() {
final names = walletConnectCompatibleChains.map(walletTypeToDisplayName).toList();
if (names.length <= 1) return names.join();
final head = names.sublist(0, names.length - 1).join(', ');
return '$head, and ${names.last}';
}
bool isEVMCompatibleChain(WalletType walletType) {
switch (walletType) {
case WalletType.polygon:
@@ -47,19 +63,8 @@ bool isNFTACtivatedChain(WalletType walletType, {int? chainId}) {
}
}
bool isWalletConnectCompatibleChain(WalletType walletType) {
switch (walletType) {
case WalletType.solana:
case WalletType.polygon:
case WalletType.ethereum:
case WalletType.base:
case WalletType.arbitrum:
case WalletType.bsc:
return true;
default:
return false;
}
}
bool isWalletConnectCompatibleChain(WalletType walletType) =>
walletConnectCompatibleChains.contains(walletType);
String getChainNameSpaceAndIdBasedOnWalletType(WalletType walletType, {int? chainId}) {
if (chainId != null) {
+19 -1
View File
@@ -52,4 +52,22 @@ bool hasTokens(WalletType walletType) {
default:
return false;
}
}
}
String tokenStandardFor(WalletType walletType) {
switch (walletType) {
case WalletType.ethereum:
case WalletType.polygon:
case WalletType.arbitrum:
case WalletType.base:
return 'ERC-20';
case WalletType.bsc:
return 'BEP-20';
case WalletType.solana:
return 'SPL';
case WalletType.tron:
return 'TRC-20';
default:
return walletTypeToString(walletType);
}
}
+29
View File
@@ -409,4 +409,33 @@ class CWSolana extends Solana {
await Future.wait(tokenChecks);
} catch (_) {}
}
@override
TransactionInfo getTransactionInfo({
required String id,
required DateTime blockTime,
required String to,
required String from,
String? tokenSymbol,
required TransactionDirection direction,
required double solAmount,
required bool isPending,
required double txFee,
}) =>
SolanaTransactionInfo(
id: id,
blockTime: blockTime,
to: to,
from: from,
tokenSymbol: tokenSymbol ?? "SOL",
direction: direction,
solAmount: solAmount,
isPending: isPending,
txFee: txFee);
@override
double getPendingTransactionAmount(PendingTransaction tx) => (tx as PendingSolanaTransaction).amount;
@override
double getPendingTransactionFee(PendingTransaction tx) => (tx as PendingSolanaTransaction).fee;
}
+28
View File
@@ -3,6 +3,9 @@ import 'package:cake_wallet/core/address_validator.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_sheet.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/fiat_currency_picker_sheet.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/desktop_exchange_cards_section.dart';
@@ -398,6 +401,7 @@ class BuySellPage extends BasePage {
showLimitsField: false,
currencies: buySellViewModel.fiatCurrencies,
onCurrencySelected: (currency) => buySellViewModel.changeFiatCurrency(currency: currency),
onTapCurrencyPicker: _presentFiatPicker,
imageArrow: Image.asset(
'assets/images/arrow_bottom_purple_icon.png',
color: Theme.of(context).colorScheme.primary,
@@ -435,6 +439,7 @@ class BuySellPage extends BasePage {
isMoneroWallet: buySellViewModel.wallet == WalletType.monero,
currencies: buySellViewModel.cryptoCurrencies,
onCurrencySelected: (currency) => buySellViewModel.changeCryptoCurrency(currency: currency),
onTapCurrencyPicker: _presentCryptoPicker,
imageArrow: Image.asset(
'assets/images/arrow_bottom_cake_green.png',
color: Theme.of(context).colorScheme.primary,
@@ -504,6 +509,29 @@ class BuySellPage extends BasePage {
);
}
void _presentFiatPicker(BuildContext context) {
FiatCurrencyPickerSheet.show(
context: context,
selected: buySellViewModel.fiatCurrency,
onSelected: (currency) => buySellViewModel.changeFiatCurrency(currency: currency),
);
}
void _presentCryptoPicker(BuildContext context) {
final items = [...buySellViewModel.cryptoCurrencies];
appendEvmDefaultTokens(items);
CurrencyPickerSheet.show(
context: context,
args: CurrencyPickerArgs(
items: items,
selected: buySellViewModel.cryptoCurrency,
recentsSource: RecentsSource.orders,
onSelected: (currency) => buySellViewModel.changeCryptoCurrency(currency: currency),
symbolResolver: buySellViewModel.amountParsingProxy.getCryptoSymbol,
),
);
}
Future<String> fetchParsedAddress(
BuildContext context,
String domain,
+12 -5
View File
@@ -1,6 +1,7 @@
import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/entities/contact_base.dart';
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/entities/wallet_contact.dart';
import 'package:cake_wallet/entities/wallet_list_order_types.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
@@ -16,6 +17,8 @@ import 'package:cake_wallet/utils/feature_flag.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@@ -281,8 +284,10 @@ class _ContactPageBodyState extends State<ContactPageBody> with SingleTickerProv
}
Widget _buildCurrencyIcon(ContactBase contact) {
final image = contact.type.iconPath;
return image != null
final image = (contact is WalletContact && contact.walletType != null)
? getCryptoCurrencyIconForWalletListItem(contact.walletType!)
: contact.type.iconPath;
return (image != null && image.isNotEmpty)
? CakeImageWidget(imageUrl: image, height: 24, width: 24)
: const SizedBox(height: 24, width: 24);
}
@@ -373,9 +378,11 @@ class _ContactListBodyState extends State<ContactListBody> {
}
Widget generateContactRaw(BuildContext context, ContactRecord contact, bool isLast) {
final image = contact.type.iconPath;
final currencyIcon = image != null
? Image.asset(image, height: 24, width: 24)
final image = contact.type == CryptoCurrency.baseEth
? 'assets/new-ui/crypto_full_icons/base.svg'
: contact.type.iconPath;
final currencyIcon = (image != null && image.isNotEmpty)
? CakeImageWidget(imageUrl: image, height: 24, width: 24)
: const SizedBox(height: 24, width: 24);
return Column(
children: [
@@ -31,26 +31,26 @@ class DesktopWalletSelectionDropDown extends StatefulWidget {
}
class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionDropDown> {
final moneroIcon = Image.asset('assets/images/crypto/monero.webp', height: 24, width: 24);
final bitcoinIcon = Image.asset('assets/images/crypto/bitcoin.webp', height: 24, width: 24);
final moneroIcon = Image.asset('assets/new-ui/crypto_full_icons/monero.svg', height: 24, width: 24);
final bitcoinIcon = Image.asset('assets/new-ui/crypto_full_icons/bitcoin.svg', height: 24, width: 24);
final tBitcoinIcon = Image.asset('assets/images/tbtc.png', height: 24, width: 24);
final litecoinIcon = Image.asset('assets/images/crypto/litecoin.webp', height: 24, width: 24);
final litecoinIcon = Image.asset('assets/new-ui/crypto_full_icons/litecoin.svg', height: 24, width: 24);
final havenIcon = Image.asset('assets/images/haven_logo.webp', height: 24, width: 24);
final ethereumIcon = Image.asset('assets/images/crypto/ethereum.webp', height: 24, width: 24);
final polygonIcon = Image.asset('assets/images/crypto/polygon.webp', height: 24, width: 24);
final bitcoinCashIcon = Image.asset('assets/images/crypto/bitcoin-cash.webp', height: 24, width: 24);
final baseIcon = Image.asset('assets/images/crypto/base_icon.webp', height: 24, width: 24);
final arbitrumIcon = Image.asset('assets/images/crypto/arbitrum.webp', height: 24, width: 24);
final bscIcon = Image.asset('assets/images/crypto/BNB.webp', height: 24, width: 24);
final nanoIcon = Image.asset('assets/images/crypto/nano.webp', height: 24, width: 24);
final bananoIcon = Image.asset('assets/images/crypto/nano.webp', height: 24, width: 24);
final solanaIcon = Image.asset('assets/images/crypto/solana.webp', height: 24, width: 24);
final tronIcon = Image.asset('assets/images/crypto/tron.webp', height: 24, width: 24);
final wowneroIcon = Image.asset('assets/images/crypto/wownero.webp', height: 24, width: 24);
final zanoIcon = Image.asset('assets/images/crypto/zano.webp', height: 24, width: 24);
final decredIcon = Image.asset('assets/images/crypto/decred.webp', height: 24, width: 24);
final dogeIcon = Image.asset('assets/images/crypto/dogecoin.webp', height: 24, width: 24);
final zcashIcon = Image.asset('assets/images/crypto/zcash.webp', height: 24, width: 24);
final ethereumIcon = Image.asset('assets/new-ui/crypto_full_icons/ethereum.svg', height: 24, width: 24);
final polygonIcon = Image.asset('assets/new-ui/crypto_full_icons/polygon.svg', height: 24, width: 24);
final bitcoinCashIcon = Image.asset('assets/new-ui/crypto_full_icons/bitcoin-cash.svg', height: 24, width: 24);
final baseIcon = Image.asset('assets/new-ui/crypto_full_icons/base.svg', height: 24, width: 24);
final arbitrumIcon = Image.asset('assets/new-ui/crypto_full_icons/arbitrum.svg', height: 24, width: 24);
final bscIcon = Image.asset('assets/new-ui/crypto_full_icons/bnb.svg', height: 24, width: 24);
final nanoIcon = Image.asset('assets/new-ui/crypto_full_icons/nano.svg', height: 24, width: 24);
final bananoIcon = Image.asset('assets/new-ui/crypto_full_icons/nano.svg', height: 24, width: 24);
final solanaIcon = Image.asset('assets/new-ui/crypto_full_icons/solana.svg', height: 24, width: 24);
final tronIcon = Image.asset('assets/new-ui/crypto_full_icons/tron.svg', height: 24, width: 24);
final wowneroIcon = Image.asset('assets/new-ui/crypto_full_icons/wownero.svg', height: 24, width: 24);
final zanoIcon = Image.asset('assets/new-ui/crypto_full_icons/zano.svg', height: 24, width: 24);
final decredIcon = Image.asset('assets/new-ui/crypto_full_icons/decred.svg', height: 24, width: 24);
final dogeIcon = Image.asset('assets/new-ui/crypto_full_icons/dogecoin.svg', height: 24, width: 24);
final zcashIcon = Image.asset('assets/new-ui/crypto_full_icons/zcash.svg', height: 24, width: 24);
final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24);
Image _newWalletImage(BuildContext context) => Image.asset(
@@ -1,5 +1,6 @@
import 'package:cake_wallet/core/address_validator.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/address_text_field.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
@@ -396,10 +397,9 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
alignment: Alignment.center,
children: [
ClipOval(
child: CakeImageWidget(
child: TokenImageWidget(
imageUrl: _tokenIconPathController.text,
width: 75,
height: 75,
size: 75,
),
),
if (_isTokenVerified)
@@ -5,14 +5,11 @@ import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular
import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_toggle.dart';
import 'package:cake_wallet/entities/sort_balance_types.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/pages/settings_page.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/dashboard/favorite_token_modal.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
@@ -186,30 +183,25 @@ class HomeSettingsPage extends BasePage {
'token': token,
});
},
leading: Container(
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(shape: BoxShape.circle),
child: CakeImageWidget(
imageUrl: token.iconPath,
height: 40,
width: 40,
errorWidget: Container(
height: 30.0,
width: 30.0,
child: Center(
child: Text(
token.title.substring(0, min(token.title.length, 2)),
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontSize: 11,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).colorScheme.surfaceContainerHighest,
leading: TokenImageWidget(
imageUrl: token.iconPath ?? '',
size: 40,
errorWidget: Container(
height: 30.0,
width: 30.0,
child: Center(
child: Text(
token.title.substring(0, min(token.title.length, 2)),
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontSize: 11,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).colorScheme.surfaceContainerHighest,
),
),
),
decoration: BoxDecoration(
@@ -371,7 +371,7 @@ class CryptoBalanceWidget extends StatelessWidget {
child: InfoCard(
title: S.of(context).synchronizing,
description: S.of(context).decred_info_card_details,
image: 'assets/images/crypto/decred.webp',
image: 'assets/new-ui/crypto_full_icons/decred.svg',
leftButtonTitle: S.of(context).litecoin_mweb_dismiss,
rightButtonTitle: S.of(context).learn_more,
leftButtonAction: () => dashboardViewModel.dismissDecredInfoCard(),
@@ -24,25 +24,25 @@ class MenuWidgetState extends State<MenuWidget> {
this.tileHeight = 60,
this.fromTopEdge = 50,
this.fromBottomEdge = 25,
this.moneroIcon = Image.asset('assets/images/crypto/monero.webp'),
this.bitcoinIcon = Image.asset('assets/images/crypto/bitcoin.webp'),
this.litecoinIcon = Image.asset('assets/images/crypto/litecoin.webp'),
this.moneroIcon = Image.asset('assets/new-ui/crypto_full_icons/monero.svg'),
this.bitcoinIcon = Image.asset('assets/new-ui/crypto_full_icons/bitcoin.svg'),
this.litecoinIcon = Image.asset('assets/new-ui/crypto_full_icons/litecoin.svg'),
this.havenIcon = Image.asset('assets/images/haven_menu.webp'),
this.ethereumIcon = Image.asset('assets/images/crypto/ethereum.webp'),
this.nanoIcon = Image.asset('assets/images/crypto/nano.webp'),
this.bananoIcon = Image.asset('assets/images/crypto/nano.webp'),
this.bitcoinCashIcon = Image.asset('assets/images/crypto/bitcoin-cash.webp'),
this.polygonIcon = Image.asset('assets/images/crypto/polygon.webp'),
this.baseIcon = Image.asset('assets/images/crypto/base_icon.webp'),
this.arbitrumIcon = Image.asset('assets/images/crypto/arbitrum.webp'),
this.bscIcon = Image.asset('assets/images/crypto/BNB.webp'),
this.solanaIcon = Image.asset('assets/images/crypto/solana.webp'),
this.tronIcon = Image.asset('assets/images/crypto/tron.webp'),
this.wowneroIcon = Image.asset('assets/images/crypto/wownero.webp'),
this.zanoIcon = Image.asset('assets/images/crypto/zano.webp'),
this.decredIcon = Image.asset('assets/images/crypto/decred.webp'),
this.dogecoinIcon = Image.asset('assets/images/crypto/dogecoin.webp'),
this.zcashIcon = Image.asset('assets/images/crypto/zcash.webp');
this.ethereumIcon = Image.asset('assets/new-ui/crypto_full_icons/ethereum.svg'),
this.nanoIcon = Image.asset('assets/new-ui/crypto_full_icons/nano.svg'),
this.bananoIcon = Image.asset('assets/new-ui/crypto_full_icons/nano.svg'),
this.bitcoinCashIcon = Image.asset('assets/new-ui/crypto_full_icons/bitcoin-cash.svg'),
this.polygonIcon = Image.asset('assets/new-ui/crypto_full_icons/polygon.svg'),
this.baseIcon = Image.asset('assets/new-ui/crypto_full_icons/base.svg'),
this.arbitrumIcon = Image.asset('assets/new-ui/crypto_full_icons/arbitrum.svg'),
this.bscIcon = Image.asset('assets/new-ui/crypto_full_icons/bnb.svg'),
this.solanaIcon = Image.asset('assets/new-ui/crypto_full_icons/solana.svg'),
this.tronIcon = Image.asset('assets/new-ui/crypto_full_icons/tron.svg'),
this.wowneroIcon = Image.asset('assets/new-ui/crypto_full_icons/wownero.svg'),
this.zanoIcon = Image.asset('assets/new-ui/crypto_full_icons/zano.svg'),
this.decredIcon = Image.asset('assets/new-ui/crypto_full_icons/decred.svg'),
this.dogecoinIcon = Image.asset('assets/new-ui/crypto_full_icons/dogecoin.svg'),
this.zcashIcon = Image.asset('assets/new-ui/crypto_full_icons/zcash.svg');
final largeScreen = 731;
@@ -122,8 +122,8 @@ class MenuWidgetState extends State<MenuWidget> {
}
int itemCount = items.length;
moneroIcon = Image.asset('assets/images/crypto/monero.webp');
bitcoinIcon = Image.asset('assets/images/crypto/bitcoin.webp');
moneroIcon = Image.asset('assets/new-ui/crypto_full_icons/monero.svg');
bitcoinIcon = Image.asset('assets/new-ui/crypto_full_icons/bitcoin.svg');
return Row(
mainAxisSize: MainAxisSize.max,
@@ -48,6 +48,7 @@ class ExchangeCard<T extends Currency> extends StatefulWidget {
this.onPushAddressBookButton,
this.onDispose,
this.useSatoshis = false,
this.onTapCurrencyPicker,
required this.cardInstanceName,
}) : super(key: key);
@@ -84,6 +85,7 @@ class ExchangeCard<T extends Currency> extends StatefulWidget {
final String cardInstanceName;
final Color fillColor;
final bool useSatoshis;
final void Function(BuildContext context)? onTapCurrencyPicker;
@override
ExchangeCardState<T> createState() => ExchangeCardState<T>();
@@ -447,6 +449,10 @@ class ExchangeCardState<T extends Currency> extends State<ExchangeCard<T>> {
}
void _presentPicker(BuildContext context) {
if (widget.onTapCurrencyPicker != null) {
widget.onTapCurrencyPicker!(context);
return;
}
showPopUp<void>(
context: context,
builder: (_) => CurrencyPicker(
@@ -149,7 +149,6 @@ class WalletTypeFormState extends State<WalletTypeForm> {
shrinkWrap: true,
itemBuilder: (context, index) {
final item = filteredTypes[index];
final curr = getCryptoCurrencyForWalletListItem(item);
return GestureDetector(
key: ValueKey('new_wallet_type_${item.name}_button_key'),
@@ -169,7 +168,7 @@ class WalletTypeFormState extends State<WalletTypeForm> {
CakeImageWidget(
height: 24,
width: 24,
imageUrl: curr.iconPath,
imageUrl: getCryptoCurrencyIconForWalletListItem(item),
),
const SizedBox(width: 12),
Text(walletTypeToDisplayName(item)),
+6 -32
View File
@@ -31,38 +31,12 @@ class NodeSharePage extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
spacing: 40,
children: [
Stack(alignment: Alignment.center, children: [
SizedBox(
width: qrSize,
height: qrSize,
child: ClipRRect(
borderRadius: BorderRadius.circular(18),
child: QrImage(
size: qrSize - padding * 2,
embeddedImagePath: "",
data: uri.toString())),
),
SizedBox(
width: 60,
height: 60,
child: Stack(
children: [
CakeImageWidget(
imageUrl: "assets/new-ui/settings_row_icons/nodes.svg",
width: 54,
height: 54,
),
Align(
alignment: Alignment.bottomRight,
child: CakeImageWidget(
imageUrl: currency.iconPath,
width: 24,
height: 24,
))
],
),
),
]),
QrImage(
size: qrSize - padding * 2,
embeddedImagePath: "assets/new-ui/node-qr-icon.svg",
badgeImageOnEmbeddedImagePath: currency.iconPath,
data: uri.toString(),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 36.0),
child: Text(S.of(context).node_share_desc,
+40 -31
View File
@@ -1,7 +1,6 @@
import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:qr_flutter/qr_flutter.dart' as qr;
class QrImage extends StatelessWidget {
@@ -13,6 +12,7 @@ class QrImage extends StatelessWidget {
this.version,
this.errorCorrectionLevel = qr.QrErrorCorrectLevel.H,
this.embeddedImagePath,
this.badgeImageOnEmbeddedImagePath,
});
final double? size;
@@ -22,46 +22,55 @@ class QrImage extends StatelessWidget {
final int? version;
final int errorCorrectionLevel;
final String? embeddedImagePath;
final String? badgeImageOnEmbeddedImagePath;
@override
Widget build(BuildContext context) {
final imagePath = embeddedImagePath ?? 'assets/images/qr-cake.png';
final isSvg = imagePath.endsWith('.svg');
final qrSize = size ?? 100.0;
final logoSize = qrSize * 0.30;
if (isSvg) {
final qrSize = size ?? 100.0;
final logoSize = qrSize * 0.30;
final centerImage = TokenImageWidget(
imageUrl: imagePath,
size: logoSize * 0.8,
);
return Stack(
alignment: Alignment.center,
Widget centerImageToUse;
if (badgeImageOnEmbeddedImagePath != null) {
centerImageToUse = Stack(
children: [
qr.QrImageView(
data: data,
errorCorrectionLevel: errorCorrectionLevel,
version: version ?? qr.QrVersions.auto,
size: qrSize,
foregroundColor: foregroundColor,
backgroundColor: backgroundColor,
padding: const EdgeInsets.all(12.0),
),
CakeImageWidget(imageUrl:
imagePath,
width: logoSize * 0.8,
height: logoSize * 0.8,
centerImage,
Positioned(
bottom: 0,
right: 0,
child: Align(
alignment: Alignment.bottomRight,
child: CakeImageWidget(
imageUrl: badgeImageOnEmbeddedImagePath!,
width: 24,
height: 24,
),
),
),
],
);
} else {
return qr.QrImageView(
data: data,
errorCorrectionLevel: errorCorrectionLevel,
version: version ?? qr.QrVersions.auto,
size: size,
foregroundColor: foregroundColor,
backgroundColor: backgroundColor,
padding: const EdgeInsets.all(12.0),
embeddedImage: AssetImage(imagePath),
);
centerImageToUse = centerImage;
}
return Stack(
alignment: Alignment.center,
children: [
qr.QrImageView(
data: data,
errorCorrectionLevel: errorCorrectionLevel,
version: version ?? qr.QrVersions.auto,
size: qrSize,
foregroundColor: foregroundColor,
backgroundColor: backgroundColor,
padding: const EdgeInsets.all(12.0),
),
centerImageToUse,
],
);
}
}
@@ -1,11 +1,11 @@
import 'package:cake_wallet/entities/bitcoin_amount_display_mode.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/entities/language_service.dart';
import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart';
import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_selector.dart';
import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_toggle.dart';
import 'package:cake_wallet/entities/sync_status_display_mode.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/fiat_currency_picker_sheet.dart';
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_theme_choice.dart';
@@ -179,35 +179,11 @@ class DisplaySettingsPage extends StatelessWidget {
keyValue: "display_settings_fiat_currency",
label: S.of(context).settings_currency,
options: [_displaySettingsViewModel.fiatCurrency.title],
onTap: () async {
final items = FiatCurrency.all;
final selectedAtIndex =
items.indexOf(_displaySettingsViewModel.fiatCurrency);
await showPopUp<void>(
context: context,
builder: (_) => Picker(
items: items,
selectedAtIndex: selectedAtIndex,
mainAxisAlignment: MainAxisAlignment.start,
onItemSelected: (FiatCurrency currency) {
_displaySettingsViewModel.setFiatCurrency(currency);
},
images: FiatCurrency.all
.map((e) => Image.asset("assets/images/flags/${e.countryCode}.png"))
.toList(),
hintText: S.of(context).search_currency,
isGridView: true,
matchingCriteria: (FiatCurrency currency, String searchText) {
return currency.title.toLowerCase().contains(searchText) ||
currency.fullName.toLowerCase().contains(searchText);
},
isSeparated: false,
),
);
}),
onTap: () => FiatCurrencyPickerSheet.show(
context: context,
selected: _displaySettingsViewModel.fiatCurrency,
onSelected: _displaySettingsViewModel.setFiatCurrency,
)),
ListItemSelector(
keyValue: "display_settings_language",
label: S.of(context).settings_change_language,
+3 -3
View File
@@ -87,7 +87,7 @@ class AnimatedURPage extends BasePage {
try {
switch (urQrType) {
case "ur:xmr-txunsigned": // ur:xmr-txsigned
final ur = await presentQRScanner(context);
final ur = await presentQRScanner(context, showManualInput: false);
if (ur == null) return;
final result =
await monero!.commitTransactionUR(animatedURmodel.wallet, ur);
@@ -96,7 +96,7 @@ class AnimatedURPage extends BasePage {
}
break;
case "ur:xmr-output": // xmr-keyimage
final ur = await presentQRScanner(context);
final ur = await presentQRScanner(context, showManualInput: false);
if (ur == null) return;
final result =
await monero!.importKeyImagesUR(animatedURmodel.wallet, ur);
@@ -105,7 +105,7 @@ class AnimatedURPage extends BasePage {
}
break;
case "ur:psbt": // psbt
final ur = await presentQRScanner(context);
final ur = await presentQRScanner(context, showManualInput: false);
if (ur == null) return;
await bitcoin!
.commitPsbtUR(animatedURmodel.wallet, ur.trim().split("\n"));
+1 -1
View File
@@ -79,7 +79,7 @@ class _URQRState extends State<URQR> {
child: QrImage(
data: frames[frame % frames.length],
version: -1,
size: null,
size: MediaQuery.of(context).size.width * 0.7,
),
),
),
+4
View File
@@ -16,6 +16,7 @@ class CakeImageWidget extends StatelessWidget {
this.borderRadius = 24.0,
this.alignment,
this.allowDrawingOutsideViewBox,
this.filterQuality,
});
final String? imageUrl;
@@ -29,6 +30,7 @@ class CakeImageWidget extends StatelessWidget {
final AlignmentGeometry? alignment;
final bool? allowDrawingOutsideViewBox;
final double borderRadius;
final FilterQuality? filterQuality;
@override
Widget build(BuildContext context) {
@@ -69,6 +71,7 @@ class CakeImageWidget extends StatelessWidget {
width: width,
fit: fit,
color: color,
filterQuality: filterQuality ?? FilterQuality.medium,
errorBuilder: (_, __, ___) => _buildErrorWidget(context),
);
}
@@ -93,6 +96,7 @@ class CakeImageWidget extends StatelessWidget {
width: width,
fit: fit ?? BoxFit.cover,
color: color,
filterQuality: filterQuality ?? FilterQuality.medium,
loadingBuilder: (_, Widget child, ImageChunkEvent? progress) {
if (progress == null) return child;
return loadingWidget ?? const Center(child: CircularProgressIndicator());
@@ -26,6 +26,7 @@ class ListItemRegularRowWidget extends StatelessWidget {
this.copyableText,
this.leadingIconErrorWidget,
this.leadingIconSize,
this.iconColor
});
final String keyValue;
@@ -46,6 +47,7 @@ class ListItemRegularRowWidget extends StatelessWidget {
final String? copyableText;
final Widget? leadingIconErrorWidget;
final double? leadingIconSize;
final Color? iconColor;
@override
Widget build(BuildContext context) {
@@ -85,6 +87,9 @@ class ListItemRegularRowWidget extends StatelessWidget {
width: leadingIconSize ?? 24,
height: leadingIconSize ?? 24,
errorWidget: leadingIconErrorWidget,
colorFilter: iconColor == null
? null
: ColorFilter.mode(iconColor!, BlendMode.srcIn),
)),
Flexible(
child: Column(
@@ -109,6 +109,7 @@ class NewListSections extends StatelessWidget {
bottomWidget: item.bottomWidget,
leadingIconErrorWidget: item.leadingIconErrorWidget,
leadingIconSize: item.leadingIconSize,
iconColor: item.iconColor,
);
}
+3 -4
View File
@@ -3,7 +3,7 @@
import 'dart:math';
import 'package:cake_wallet/entities/seed_type.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
import 'package:cake_wallet/src/widgets/search_bar_widget.dart';
import 'package:cake_wallet/themes/core/theme_extension.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
@@ -498,10 +498,9 @@ class _PickerState<Item> extends State<Picker<Item>> {
Widget? _getItemIcon(Item item) {
if (item is Currency) {
if (item.iconPath != null) {
return CakeImageWidget(
return TokenImageWidget(
imageUrl: item.iconPath!,
height: 20.0,
width: 20.0,
size: 20.0,
);
} else {
return Container(
+1 -1
View File
@@ -43,7 +43,7 @@ class SettingActions {
static SettingActions exportOutputsAction = SettingActions._(
key: ValueKey('dashboard_page_menu_widget_export_outputs_settings_button_key'),
name: (context) => S.of(context).export_outputs,
image: 'assets/images/crypto/monero.webp',
image: 'assets/new-ui/crypto_full_icons/monero.svg',
onTap: (BuildContext context) {
Navigator.of(context)
.pushNamed(Routes.urqrAnimatedPage, arguments: {'export-outputs': 'export-outputs'});
+28
View File
@@ -153,4 +153,32 @@ class CWTron extends Tron {
return tronWallet.tronTokenCurrencies
.any((element) => element.contractAddress == contractAddress);
}
@override
TransactionInfo getTransactionInfo({
required String id,
required BigInt tronAmount,
int? txFee,
String? tokenSymbol,
required TransactionDirection direction,
required DateTime blockTime,
String? to,
String? from,
required bool isPending,
}) =>
TronTransactionInfo(id: id,
tronAmount: tronAmount,
tokenSymbol: tokenSymbol ?? "TRX",
txFee: txFee,
direction: direction,
blockTime: blockTime,
to: to,
from: from,
isPending: isPending);
@override
String getPendingTransactionAmount(PendingTransaction tx) => (tx as PendingTronTransaction).amount;
@override
String getPendingTransactionFee(PendingTransaction tx) => (tx as PendingTronTransaction).amount;
}
@@ -5,6 +5,7 @@ import 'package:cake_wallet/buy/buy_quote.dart';
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
import 'package:cake_wallet/buy/payment_method.dart';
import 'package:cake_wallet/buy/sell_buy_states.dart';
import 'package:cake_wallet/core/amount_parsing_proxy.dart';
import 'package:cake_wallet/core/selectable_option.dart';
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
@@ -87,6 +88,8 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S
final AppStore _appStore;
AmountParsingProxy get amountParsingProxy => _appStore.amountParsingProxy;
Quote? bestRateQuote;
Quote? selectedQuote;
@@ -326,6 +326,10 @@ abstract class DashboardViewModelBase with Store {
appStore.wallet!.transactionHistory.transactions.values.last.confirmations +
1;
} catch (_) {}
} else {
confirmations = appStore.wallet!.transactionHistory.transactions.values
.map((item) => item.isPending)
.fold(0, (val, pending) => pending ? val + 1 : val);
}
return length * confirmations;
}, _transactionDisposerCallback, delay: 300);
@@ -490,16 +494,23 @@ abstract class DashboardViewModelBase with Store {
}
// printV("Transaction disposer callback (relevantTxs: ${relevantTxs.length} current: ${transactions.length})");
// TODO(malik) update this in a saner way during the vm refactor
String _txIdentityString(String txHash, TransactionDirection direction) => "${txHash}_$direction";
String _txIdentityStringConfirmations(String txHash, TransactionDirection direction, int confirmations) => "${txHash}_${direction}_$confirmations";
String _txIdentityStringConfirmations(
String txHash, TransactionDirection direction, int confirmations, bool isPending) =>
"${txHash}_${direction}_${confirmations}_$isPending";
final existingKeys = transactions
.map((item) => _txIdentityStringConfirmations(item.transaction.txHash, item.transaction.direction, item.transaction.confirmations))
.map((item) => _txIdentityStringConfirmations(
item.transaction.txHash,
item.transaction.direction,
item.transaction.confirmations,
item.transaction.isPending))
.toSet();
final newTransactions = relevantTxs
.where((tx) => !existingKeys.contains(_txIdentityStringConfirmations(tx.txHash, tx.direction, tx.confirmations)))
.where((tx) => !existingKeys.contains(_txIdentityStringConfirmations(
tx.txHash, tx.direction, tx.confirmations, tx.isPending)))
.map((tx) => TransactionListItem(
transaction: tx,
balanceViewModel: balanceViewModel,
@@ -1291,6 +1302,10 @@ abstract class DashboardViewModelBase with Store {
appStore.wallet!.transactionHistory.transactions.values.last.confirmations +
1;
} catch (_) {}
} else {
confirmations = appStore.wallet!.transactionHistory.transactions.values
.map((item) => item.isPending)
.fold(0, (val, pending) => pending ? val + 1 : val);
}
return length * confirmations;
}, _transactionDisposerCallback, delay: 300);
@@ -218,7 +218,7 @@ class TransactionListItem extends ActionListItem with Keyable {
case WalletType.base:
case WalletType.arbitrum:
case WalletType.bsc:
final asset = evm!.assetOfTransaction(balanceViewModel.wallet, transaction);
final asset = assetOfTransaction;
final price = balanceViewModel.fiatConversionStore.prices[asset];
amount = calculateFiatAmountRaw(
cryptoAmount: evm!.formatterEVMAmountToDouble(transaction: transaction),
@@ -38,11 +38,9 @@ import 'package:cake_wallet/exchange/provider/xoswap_exchange_provider.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/exchange/trade_request.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
import 'package:cake_wallet/new-ui/widgets/currency_picker/fiat_currency_picker_sheet.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/utils/exchange_provider_logger.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cw_core/currency.dart';
import "package:cw_core/wallet_info.dart";
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
import 'package:cake_wallet/store/dashboard/trades_store.dart';
@@ -270,17 +268,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
}
void showFiatCurrencyPicker(BuildContext context) {
showPopUp<void>(
FiatCurrencyPickerSheet.show(
context: context,
builder: (_) => CurrencyPicker(
key: ValueKey('send_page_currency_picker_dialog_button_key'),
selectedAtIndex: FiatCurrency.all.indexOf(fiat),
items: FiatCurrency.all,
hintText: S.of(context).search_currency,
onItemSelected: (Currency cur) async {
_settingsStore.fiatCurrency = (cur as FiatCurrency);
},
),
selected: fiat,
onSelected: (cur) => _settingsStore.fiatCurrency = cur,
);
}
@@ -1034,6 +1025,39 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
@action
Future<void> createTrade() async {
final depositAmountValue =
double.tryParse(_depositAmount.replaceAll(',', '.')) ?? 0.0;
final receiveAmountValue =
double.tryParse(_receiveAmount.replaceAll(',', '.')) ?? 0.0;
if (depositAmountValue <= 0 || receiveAmountValue <= 0) {
ExchangeProviderLogger.logError(
provider: forcedProvider?.description ?? bestRateProvider?.description,
function: 'createTrade',
error: 'Invalid swap amount: deposit=$_depositAmount receive=$_receiveAmount',
requestData: {
'from': depositCurrency.title,
'to': receiveCurrency.title,
'fromAmount': _depositAmount,
'toAmount': _receiveAmount,
'isFixedRateMode': isFixedRateMode,
'forcedProvider': forcedProvider?.title,
'bestRateProvider': bestRateProvider?.title,
},
);
final invalidAmountError = depositAmountValue <= 0
? '$depositAmountValue is not a valid amount for depositAmount'
: '$receiveAmountValue is not a valid amount for receiveAmount';
tradeState = TradeIsCreatedFailure(
title: S.current.trade_not_created,
error: invalidAmountError,
);
return;
}
if (isSendAllEnabled) {
await calculateDepositAllAmount();
final amount = double.tryParse(_depositAmount);
+1 -1
View File
@@ -34,7 +34,7 @@ class LinkViewModel {
String? getRouteToGo() {
if (isWalletConnectLink) {
if (!isWalletConnectCompatibleChain(appStore.wallet!.type)) {
_errorToast(S.current.switchToEVMCompatibleWallet);
_errorToast(S.current.switchToWCCompatibleWallet(walletConnectCompatibleChainsLabel()));
return null;
}
return Routes.walletConnectConnectionsListing;
+63
View File
@@ -8,6 +8,7 @@ import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/core/open_crypto_pay/exceptions.dart';
import 'package:cake_wallet/core/open_crypto_pay/models.dart';
import 'package:cake_wallet/core/open_crypto_pay/open_cryptopay_service.dart';
import 'package:cake_wallet/core/utilities.dart';
import 'package:cake_wallet/core/validator.dart';
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
import 'package:cake_wallet/decred/decred.dart';
@@ -59,6 +60,7 @@ import 'package:cw_core/exceptions.dart';
import 'package:cw_core/lnurl.dart';
import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/unspent_coin_type.dart';
@@ -488,6 +490,25 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
bool get shouldDisplayTOTP2FAForSendsToInternalWallet =>
_settingsStore.shouldRequireTOTP2FAForSendsToInternalWallets;
@computed
TransactionInfo? get transactionInfo {
if(isEVMCompatibleChain(walletType)) {
return wallet.transactionHistory.transactions[pendingTransaction?.evmTxHashFromRawHex];
}
if (walletType == WalletType.monero) {
// monero tx history keys are in format txhash_amount_accountindex_addressindex
return wallet.transactionHistory.transactions[wallet.transactionHistory.transactions.keys
.firstWhereOrNull((item) => item.split("_").first == pendingTransaction?.id)];
}
if (walletType == WalletType.zcash) {
// zcash has a txHash getter, but the pendingTransaction id contains quotation marks for whatever reason?
final txIdNoQuotes = pendingTransaction?.id.replaceAll("\"", "");
return wallet.transactionHistory.transactions.values
.firstWhereOrNull((item) => item.txHash == txIdNoQuotes);
}
return wallet.transactionHistory.transactions[pendingTransaction?.id];
}
//* Still open to further optimize these checks
//* It works but can be made better
@action
@@ -1078,6 +1099,48 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
});
}
// FIXME(malik) ideally, this should be done wallet-side.
// it is required because evm, solana and tron don't actually save the transaction info when you send something.
// instead, they rely on the tx to eventually get fetched at sync time, which can take a while
if(isEVMWallet) {
wallet.transactionHistory.addOne(evm!.getTransactionInfo(
id: pendingTransaction!.evmTxHashFromRawHex!,
height: 0,
// FIXME(malik) this is even more critical given all the issues we had with decimal point parsing. with money it'll be easy
ethAmount: selectedCryptoCurrency.parseAmount(outputs.first.cryptoAmount),
ethFee: CryptoCurrency.eth.parseAmount(pendingTransaction!.feeFormattedValue),
tokenSymbol: selectedCryptoCurrency.title,
direction: TransactionDirection.outgoing,
isPending: true,
date: DateTime.now(),
confirmations: 0,
chainId: wallet.chainId ?? 0));
}
if(walletType == WalletType.solana) {
wallet.transactionHistory.addOne(solana!.getTransactionInfo(
id: pendingTransaction!.id,
blockTime: DateTime.now(),
tokenSymbol: selectedCryptoCurrency.title,
to: "",
from: "",
direction: TransactionDirection.outgoing,
solAmount: solana!.getPendingTransactionAmount(pendingTransaction!),
isPending: true,
txFee: solana!.getPendingTransactionFee(pendingTransaction!)));
}
if (walletType == WalletType.tron) {
wallet.transactionHistory.addOne(tron!.getTransactionInfo(
id: pendingTransaction!.id,
blockTime: DateTime.now(),
tokenSymbol: selectedCryptoCurrency.title,
direction: TransactionDirection.outgoing,
tronAmount: selectedCryptoCurrency.parseAmount(outputs.first.cryptoAmount),
isPending: true,
txFee: CryptoCurrency.trx.parseAmount(outputs.first.estimatedFee).toInt()));
}
if (pendingTransaction!.id.isNotEmpty) {
_addTransactionDescription();
}
@@ -127,6 +127,7 @@ class TxDetailRowDefinition {
.firstOrNull ??
"";
case WalletType.tron:
if(vm.transactionInfo.to != null)
ret = tron!.getTronBase58Address(vm.transactionInfo.to!, vm.wallet);
default:
break;
@@ -315,6 +316,7 @@ abstract class TransactionDetailsViewModelBase with Store {
TransactionPriority? transactionPriority;
CryptoCurrency get transactionAsset {
if (isEVMCompatibleChain(wallet.type)) {
return evm!.assetOfTransaction(wallet, transactionInfo);
}
@@ -2,6 +2,7 @@ import 'dart:core';
import 'dart:developer' as dev;
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/core/amount_parsing_proxy.dart';
import 'package:cake_wallet/core/fiat_conversion_service.dart';
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
import 'package:cake_wallet/decred/decred.dart';
@@ -167,6 +168,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
bool get hasPayjoin =>
wallet.type == WalletType.bitcoin && !isLightning && !isSilentPayments && uri.toString().contains("payjo.in");
AmountParsingProxy get amountParsingProxy => _appStore.amountParsingProxy;
@computed
FiatCurrency get fiatCurrency => _appStore.settingsStore.fiatCurrency;
@@ -478,7 +481,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
];
case 137:
return [
'assets/images/crypto/polygon.webp',
'assets/new-ui/crypto_full_icons/polygon.svg',
'assets/images/eth_pol_icon.svg',
'assets/images/usdc_icon.svg',
'assets/images/usdt_wallet_icon.svg',
@@ -492,20 +495,20 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
];
case 42161:
return [
'assets/images/crypto/arbitrum.webp',
'assets/new-ui/crypto_full_icons/arbitrum.svg',
'assets/images/usdc_icon.svg',
'assets/images/more_tokens.svg',
];
case 56:
return [
'assets/images/crypto/BNB.webp',
'assets/new-ui/crypto_full_icons/bnb.svg',
'assets/images/usdc_icon.svg',
'assets/images/usdt_wallet_icon.svg',
'assets/images/more_tokens.svg',
];
default:
return [
'assets/images/crypto/ethereum.webp',
'assets/new-ui/crypto_full_icons/ethereum.svg',
'assets/images/usdc_icon.svg',
'assets/images/usdt_wallet_icon.svg',
];
+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.275 12.475L11.525 8.7L14.3 5.95L13.575 5.225L8.1 10.7L6.7 9.3L12.15 3.825C12.55 3.425 13.0208 3.225 13.5625 3.225C14.1042 3.225 14.575 3.425 14.975 3.825L15.7 4.55L16.95 3.3C17.15 3.1 17.3875 3 17.6625 3C17.9375 3 18.175 3.1 18.375 3.3L20.7 5.625C20.9 5.825 21 6.0625 21 6.3375C21 6.6125 20.9 6.85 20.7 7.05L15.275 12.475ZM6.75 21H3V17.25L10.1 10.125L13.875 13.875L6.75 21Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 510 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="12" height="20" viewBox="0 0 12 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 20V12H0V0H10L8 7H12L3 20Z" fill="#E3E3E3"/>
</svg>

After

Width:  |  Height:  |  Size: 163 B

+10
View File
@@ -0,0 +1,10 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_20409_705448)">
<path d="M8 17C7.45 17 6.97917 16.8042 6.5875 16.4125C6.19583 16.0208 6 15.55 6 15V9C6 8.45 6.19583 7.97917 6.5875 7.5875C6.97917 7.19583 7.45 7 8 7H9L10 6H14L15 7H16C16.55 7 17.0208 7.19583 17.4125 7.5875C17.8042 7.97917 18 8.45 18 9V15C18 15.55 17.8042 16.0208 17.4125 16.4125C17.0208 16.8042 16.55 17 16 17H8ZM12 14C12.55 14 13.0208 13.8042 13.4125 13.4125C13.8042 13.0208 14 12.55 14 12C14 11.45 13.8042 10.9792 13.4125 10.5875C13.0208 10.1958 12.55 10 12 10C11.45 10 10.9792 10.1958 10.5875 10.5875C10.1958 10.9792 10 11.45 10 12C10 12.55 10.1958 13.0208 10.5875 13.4125C10.9792 13.8042 11.45 14 12 14ZM8.55 0.5C9.11667 0.316667 9.6875 0.1875 10.2625 0.1125C10.8375 0.0375 11.4167 0 12 0C13.5667 0 15.0458 0.279167 16.4375 0.8375C17.8292 1.39583 19.0625 2.17083 20.1375 3.1625C21.2125 4.15417 22.0917 5.32083 22.775 6.6625C23.4583 8.00417 23.8667 9.45 24 11H22C21.8833 9.8 21.5667 8.67917 21.05 7.6375C20.5333 6.59583 19.8708 5.675 19.0625 4.875C18.2542 4.075 17.325 3.42917 16.275 2.9375C15.225 2.44583 14.1 2.15 12.9 2.05L14.45 3.6L13.05 5L8.55 0.5ZM15.45 23.5C14.8833 23.6833 14.3125 23.8125 13.7375 23.8875C13.1625 23.9625 12.5833 24 12 24C10.4333 24 8.95417 23.7208 7.5625 23.1625C6.17083 22.6042 4.9375 21.8292 3.8625 20.8375C2.7875 19.8458 1.90833 18.6792 1.225 17.3375C0.541667 15.9958 0.133333 14.55 0 13H2C2.13333 14.2 2.45417 15.3208 2.9625 16.3625C3.47083 17.4042 4.12917 18.325 4.9375 19.125C5.74583 19.925 6.675 20.5708 7.725 21.0625C8.775 21.5542 9.9 21.85 11.1 21.95L9.55 20.4L10.95 19L15.45 23.5Z" fill="#E3E3E3"/>
</g>
<defs>
<clipPath id="clip0_20409_705448">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+10
View File
@@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_20667_718695)">
<path d="M5.20517 14.2083C4.86839 14.2083 4.58333 14.0917 4.35 13.8583C4.11667 13.625 4 13.3399 4 13.0032V4.08017C4 3.74339 4.11667 3.45833 4.35 3.225C4.58333 2.99167 4.86839 2.875 5.20517 2.875H9.41017V3.875H5.20517C5.15383 3.875 5.10683 3.89639 5.06417 3.93917C5.02139 3.98183 5 4.02883 5 4.08017V13.0032C5 13.0545 5.02139 13.1015 5.06417 13.1442C5.10683 13.1869 5.15383 13.2083 5.20517 13.2083H14.1282C14.1795 13.2083 14.2265 13.1869 14.2692 13.1442C14.3119 13.1015 14.3333 13.0545 14.3333 13.0032V8.79817H15.3333V13.0032C15.3333 13.3399 15.2167 13.625 14.9833 13.8583C14.75 14.0917 14.4649 14.2083 14.1282 14.2083H5.20517ZM8.14617 10.7647L7.44367 10.0622L13.6308 3.875H11V2.875H15.3333V7.20833H14.3333V4.5775L8.14617 10.7647Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_20667_718695">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.2787 17.5V16H13.7115V17.5H10.2787ZM6.404 12.75V11.25H17.5865V12.75H6.404ZM3.5 8V6.5H20.5V8H3.5Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 232 B

+21
View File
@@ -0,0 +1,21 @@
<svg width="69" height="69" viewBox="0 0 69 69" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_20948_375067)">
<path d="M0 23C0 14.9492 0 10.9239 1.56678 7.84889C2.94496 5.14406 5.14406 2.94496 7.84889 1.56678C10.9239 0 14.9492 0 23 0H46C54.0508 0 58.0761 0 61.1511 1.56678C63.8559 2.94496 66.055 5.14406 67.4332 7.84889C69 10.9239 69 14.9492 69 23V46C69 54.0508 69 58.0761 67.4332 61.1511C66.055 63.8559 63.8559 66.055 61.1511 67.4332C58.0761 69 54.0508 69 46 69H23C14.9492 69 10.9239 69 7.84889 67.4332C5.14406 66.055 2.94496 63.8559 1.56678 61.1511C0 58.0761 0 54.0508 0 46V23Z" fill="url(#paint0_linear_20948_375067)"/>
<path d="M46 66V69H23V66H46ZM66 46V23C66 18.9251 65.998 16.0493 65.8145 13.8027C65.6338 11.5912 65.2918 10.255 64.7598 9.21094C63.6692 7.07068 61.9293 5.33078 59.7891 4.24023C58.745 3.70825 57.4088 3.36624 55.1973 3.18555C52.9507 3.002 50.0749 3 46 3H23C18.9251 3 16.0493 3.002 13.8027 3.18555C11.5912 3.36624 10.255 3.70825 9.21094 4.24023C7.07068 5.33078 5.33078 7.07068 4.24023 9.21094C3.70825 10.255 3.36624 11.5912 3.18555 13.8027C3.002 16.0493 3 18.9251 3 23V46C3 50.0749 3.002 52.9507 3.18555 55.1973C3.36624 57.4088 3.70825 58.745 4.24023 59.7891C5.33078 61.9293 7.07068 63.6692 9.21094 64.7598C10.255 65.2918 11.5912 65.6338 13.8027 65.8145C16.0493 65.998 18.9251 66 23 66V69L20.1641 68.9971C14.1792 68.9764 10.7869 68.8161 8.13965 67.5752L7.84863 67.4336C5.31293 66.1416 3.22204 64.1276 1.83496 61.6523L1.56641 61.1514C-0.000375032 58.0764 0 54.0508 0 46V23C0 14.9492 -0.000375032 10.9236 1.56641 7.84863C2.94458 5.14384 5.14384 2.94458 7.84863 1.56641C10.9236 -0.000375032 14.9492 0 23 0H46C54.0508 0 58.0764 -0.000375032 61.1514 1.56641C63.8562 2.94458 66.0554 5.14384 67.4336 7.84863C69.0004 10.9236 69 14.9492 69 23V46C69 54.0508 69.0004 58.0764 67.4336 61.1514L67.165 61.6523C65.778 64.1276 63.6871 66.1416 61.1514 67.4336L60.8604 67.5752C57.821 68.9999 53.7996 69 46 69V66C50.0749 66 52.9507 65.998 55.1973 65.8145C57.4088 65.6338 58.745 65.2918 59.7891 64.7598C61.9293 63.6692 63.6692 61.9293 64.7598 59.7891C65.2918 58.745 65.6338 57.4088 65.8145 55.1973C65.998 52.9507 66 50.0749 66 46Z" fill="black"/>
<path d="M19.5051 55.4285C17.6211 55.4285 16.0074 54.7569 14.6642 53.4137C13.321 52.0705 12.6494 50.4569 12.6494 48.5728C12.6494 46.6887 13.321 45.0751 14.6642 43.7319C16.0074 42.3887 17.6211 41.7171 19.5051 41.7171C20.1798 41.7171 20.8357 41.8181 21.4728 42.0201C22.1103 42.2221 22.6943 42.506 23.2248 42.8717L32.7744 33.3221V25.2054C31.2944 24.8251 30.0692 24.0266 29.099 22.8099C28.1288 21.5928 27.6437 20.1843 27.6437 18.5842C27.6437 16.7002 28.3153 15.0865 29.6585 13.7433C31.0017 12.4001 32.6153 11.7285 34.4994 11.7285C36.3835 11.7285 37.9971 12.4001 39.3403 13.7433C40.6835 15.0865 41.3551 16.7002 41.3551 18.5842C41.3551 20.1843 40.8737 21.5928 39.9107 22.8099C38.9482 24.0266 37.7194 24.8251 36.2244 25.2054V33.3221L45.8091 42.8717C46.34 42.506 46.9169 42.2221 47.5398 42.0201C48.1628 41.8181 48.814 41.7171 49.4937 41.7171C51.3778 41.7171 52.9914 42.3887 54.3346 43.7319C55.6778 45.0751 56.3494 46.6887 56.3494 48.5728C56.3494 50.4569 55.6778 52.0705 54.3346 53.4137C52.9914 54.7569 51.3778 55.4285 49.4937 55.4285C47.6096 55.4285 45.996 54.7569 44.6528 53.4137C43.3096 52.0705 42.638 50.4569 42.638 48.5728C42.638 48.0001 42.7058 47.4433 42.8415 46.9024C42.9772 46.3619 43.1762 45.8557 43.4384 45.3838L34.4994 36.4449L25.5605 45.3838C25.8227 45.8557 26.0216 46.3619 26.1573 46.9024C26.293 47.4433 26.3609 48.0001 26.3609 48.5728C26.3609 50.4569 25.6893 52.0705 24.3461 53.4137C23.0029 54.7569 21.3892 55.4285 19.5051 55.4285ZM49.4908 51.9785C50.4361 51.9785 51.2405 51.6477 51.9041 50.9861C52.5676 50.3244 52.8994 49.5208 52.8994 48.5751C52.8994 47.6298 52.5686 46.8256 51.907 46.1624C51.2453 45.4988 50.4417 45.1671 49.496 45.1671C48.5507 45.1671 47.7465 45.4979 47.0833 46.1595C46.4197 46.8211 46.088 47.6246 46.088 48.5699C46.088 49.5152 46.4188 50.3196 47.0804 50.9832C47.742 51.6467 48.5455 51.9785 49.4908 51.9785ZM34.4965 21.99C35.4422 21.99 36.2466 21.6591 36.9098 20.9975C37.5734 20.3359 37.9051 19.5324 37.9051 18.5871C37.9051 17.6418 37.5743 16.8374 36.9127 16.1738C36.2511 15.5103 35.4476 15.1785 34.5023 15.1785C33.5566 15.1785 32.7522 15.5093 32.089 16.171C31.4255 16.8326 31.0937 17.6363 31.0937 18.5819C31.0937 19.5272 31.4245 20.3315 32.0861 20.9946C32.7478 21.6582 33.5512 21.99 34.4965 21.99ZM19.5028 51.9785C20.4481 51.9785 21.2524 51.6477 21.9155 50.9861C22.5791 50.3244 22.9109 49.5208 22.9109 48.5751C22.9109 47.6298 22.58 46.8256 21.9184 46.1624C21.2568 45.4988 20.4533 45.1671 19.508 45.1671C18.5627 45.1671 17.7583 45.4979 17.0947 46.1595C16.4312 46.8211 16.0994 47.6246 16.0994 48.5699C16.0994 49.5152 16.4302 50.3196 17.0919 50.9832C17.7535 51.6467 18.5572 51.9785 19.5028 51.9785Z" fill="white"/>
<path d="M19.5051 55.4285C17.6211 55.4285 16.0074 54.7569 14.6642 53.4137C13.321 52.0705 12.6494 50.4569 12.6494 48.5728C12.6494 46.6887 13.321 45.0751 14.6642 43.7319C16.0074 42.3887 17.6211 41.7171 19.5051 41.7171C20.1798 41.7171 20.8357 41.8181 21.4728 42.0201C22.1103 42.2221 22.6943 42.506 23.2248 42.8717L32.7744 33.3221V25.2054C31.2944 24.8251 30.0692 24.0266 29.099 22.8099C28.1288 21.5928 27.6437 20.1843 27.6437 18.5842C27.6437 16.7002 28.3153 15.0865 29.6585 13.7433C31.0017 12.4001 32.6153 11.7285 34.4994 11.7285C36.3835 11.7285 37.9971 12.4001 39.3403 13.7433C40.6835 15.0865 41.3551 16.7002 41.3551 18.5842C41.3551 20.1843 40.8737 21.5928 39.9107 22.8099C38.9482 24.0266 37.7194 24.8251 36.2244 25.2054V33.3221L45.8091 42.8717C46.34 42.506 46.9169 42.2221 47.5398 42.0201C48.1628 41.8181 48.814 41.7171 49.4937 41.7171C51.3778 41.7171 52.9914 42.3887 54.3346 43.7319C55.6778 45.0751 56.3494 46.6887 56.3494 48.5728C56.3494 50.4569 55.6778 52.0705 54.3346 53.4137C52.9914 54.7569 51.3778 55.4285 49.4937 55.4285C47.6096 55.4285 45.996 54.7569 44.6528 53.4137C43.3096 52.0705 42.638 50.4569 42.638 48.5728C42.638 48.0001 42.7058 47.4433 42.8415 46.9024C42.9772 46.3619 43.1762 45.8557 43.4384 45.3838L34.4994 36.4449L25.5605 45.3838C25.8227 45.8557 26.0216 46.3619 26.1573 46.9024C26.293 47.4433 26.3609 48.0001 26.3609 48.5728C26.3609 50.4569 25.6893 52.0705 24.3461 53.4137C23.0029 54.7569 21.3892 55.4285 19.5051 55.4285ZM49.4908 51.9785C50.4361 51.9785 51.2405 51.6477 51.9041 50.9861C52.5676 50.3244 52.8994 49.5208 52.8994 48.5751C52.8994 47.6298 52.5686 46.8256 51.907 46.1624C51.2453 45.4988 50.4417 45.1671 49.496 45.1671C48.5507 45.1671 47.7465 45.4979 47.0833 46.1595C46.4197 46.8211 46.088 47.6246 46.088 48.5699C46.088 49.5152 46.4188 50.3196 47.0804 50.9832C47.742 51.6467 48.5455 51.9785 49.4908 51.9785ZM34.4965 21.99C35.4422 21.99 36.2466 21.6591 36.9098 20.9975C37.5734 20.3359 37.9051 19.5324 37.9051 18.5871C37.9051 17.6418 37.5743 16.8374 36.9127 16.1738C36.2511 15.5103 35.4476 15.1785 34.5023 15.1785C33.5566 15.1785 32.7522 15.5093 32.089 16.171C31.4255 16.8326 31.0937 17.6363 31.0937 18.5819C31.0937 19.5272 31.4245 20.3315 32.0861 20.9946C32.7478 21.6582 33.5512 21.99 34.4965 21.99ZM19.5028 51.9785C20.4481 51.9785 21.2524 51.6477 21.9155 50.9861C22.5791 50.3244 22.9109 49.5208 22.9109 48.5751C22.9109 47.6298 22.58 46.8256 21.9184 46.1624C21.2568 45.4988 20.4533 45.1671 19.508 45.1671C18.5627 45.1671 17.7583 45.4979 17.0947 46.1595C16.4312 46.8211 16.0994 47.6246 16.0994 48.5699C16.0994 49.5152 16.4302 50.3196 17.0919 50.9832C17.7535 51.6467 18.5572 51.9785 19.5028 51.9785Z" fill="url(#paint1_linear_20948_375067)"/>
</g>
<defs>
<linearGradient id="paint0_linear_20948_375067" x1="34.5" y1="0" x2="34.5" y2="69" gradientUnits="userSpaceOnUse">
<stop stop-color="#8F8F8F"/>
<stop offset="1" stop-color="#6D6D6D"/>
</linearGradient>
<linearGradient id="paint1_linear_20948_375067" x1="34.4994" y1="11.7285" x2="34.4994" y2="55.4285" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#DADADA"/>
</linearGradient>
<clipPath id="clip0_20948_375067">
<rect width="69" height="69" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.8 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="19" height="19" viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.876 18.124C0.292 17.54 0 16.8384 0 16.0193C0 15.2001 0.292 14.4985 0.876 13.9145C1.46 13.3305 2.16158 13.0385 2.98075 13.0385C3.27442 13.0385 3.55967 13.0824 3.8365 13.1702C4.1135 13.2581 4.36733 13.3815 4.598 13.5405L8.75 9.3885V5.8595C8.1065 5.69417 7.57383 5.34742 7.152 4.81925C6.73017 4.29108 6.51925 3.67825 6.51925 2.98075C6.51925 2.16158 6.81125 1.46 7.39525 0.875999C7.97925 0.291999 8.68083 0 9.5 0C10.3192 0 11.0208 0.291999 11.6048 0.875999C12.1888 1.46 12.4808 2.16158 12.4808 2.98075C12.4808 3.67825 12.2714 4.29108 11.8527 4.81925C11.4342 5.34742 10.9 5.69417 10.25 5.8595V9.3885L14.4173 13.5405C14.6481 13.3815 14.8994 13.2581 15.1712 13.1702C15.4429 13.0824 15.7256 13.0385 16.0193 13.0385C16.8384 13.0385 17.54 13.3305 18.124 13.9145C18.708 14.4985 19 15.2001 19 16.0193C19 16.8384 18.708 17.54 18.124 18.124C17.54 18.708 16.8384 19 16.0193 19C15.2001 19 14.4985 18.708 13.9145 18.124C13.3305 17.54 13.0385 16.8384 13.0385 16.0193C13.0385 15.7706 13.068 15.5286 13.127 15.2933C13.186 15.0581 13.2725 14.8379 13.3865 14.6328L9.5 10.7463L5.6135 14.6328C5.7275 14.8379 5.814 15.0581 5.873 15.2933C5.932 15.5286 5.9615 15.7706 5.9615 16.0193C5.9615 16.8384 5.6695 17.54 5.0855 18.124C4.5015 18.708 3.79992 19 2.98075 19C2.16158 19 1.46 18.708 0.876 18.124ZM17.0673 17.0673C17.3558 16.7787 17.5 16.4294 17.5 16.0193C17.5 15.6089 17.3558 15.2596 17.0673 14.9712C16.7787 14.6827 16.4294 14.5385 16.0193 14.5385C15.6089 14.5385 15.2596 14.6827 14.9712 14.9712C14.6827 15.2596 14.5385 15.6089 14.5385 16.0193C14.5385 16.4294 14.6827 16.7787 14.9712 17.0673C15.2596 17.3558 15.6089 17.5 16.0193 17.5C16.4294 17.5 16.7787 17.3558 17.0673 17.0673ZM10.548 4.02875C10.8365 3.74042 10.9808 3.39108 10.9808 2.98075C10.9808 2.57058 10.8365 2.22125 10.548 1.93275C10.2597 1.64425 9.91033 1.5 9.5 1.5C9.08967 1.5 8.74033 1.64425 8.452 1.93275C8.1635 2.22125 8.01925 2.57058 8.01925 2.98075C8.01925 3.39108 8.1635 3.74042 8.452 4.02875C8.74033 4.31725 9.08967 4.4615 9.5 4.4615C9.91033 4.4615 10.2597 4.31725 10.548 4.02875ZM4.02875 17.0673C4.31725 16.7787 4.4615 16.4294 4.4615 16.0193C4.4615 15.6089 4.31725 15.2596 4.02875 14.9712C3.74042 14.6827 3.39108 14.5385 2.98075 14.5385C2.57058 14.5385 2.22125 14.6827 1.93275 14.9712C1.64425 15.2596 1.5 15.6089 1.5 16.0193C1.5 16.4294 1.64425 16.7787 1.93275 17.0673C2.22125 17.3558 2.57058 17.5 2.98075 17.5C3.39108 17.5 3.74042 17.3558 4.02875 17.0673Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="24" height="24" rx="4.8" fill="#2231EF"/>
<path d="M15.5209 17.6564L20.678 12.4993C20.8111 12.3661 20.8111 12.15 20.678 12.0149L14.5135 5.85229L12.2109 8.15482L16.0729 12.0168C16.2061 12.15 16.2061 12.3661 16.0729 12.5012L13.2203 15.3538L15.5229 17.6564H15.5209Z" fill="white"/>
<path d="M14.0849 19.099L7.48811 12.5022C7.35494 12.369 7.35494 12.1528 7.48811 12.0177L14.0849 5.4209H9.62079C9.53008 5.4209 9.44323 5.45757 9.37954 5.52126L2.88113 12.0177C2.74796 12.1509 2.74796 12.3671 2.88113 12.5022L9.37761 18.9987C9.4413 19.0623 9.52815 19.099 9.61886 19.099H14.083H14.0849Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 722 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="17" height="19" viewBox="0 0 17 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.5 18.8078C1.80133 18.8078 1.21 18.5658 0.726 18.0818C0.242 17.5978 0 17.0064 0 16.3078V13.8077H3V0L4.38475 1.1925L5.7885 0L7.19225 1.1925L8.59625 0L10 1.1925L11.4038 0L12.8077 1.1925L14.2115 0L15.6152 1.1925L17 0V16.3078C17 17.0064 16.758 17.5978 16.274 18.0818C15.79 18.5658 15.1987 18.8078 14.5 18.8078H2.5ZM14.5 17.3078C14.7833 17.3078 15.0208 17.2119 15.2125 17.0203C15.4042 16.8286 15.5 16.5911 15.5 16.3078V2.30775H4.5V13.8077H13.5V16.3078C13.5 16.5911 13.5958 16.8286 13.7875 17.0203C13.9792 17.2119 14.2167 17.3078 14.5 17.3078ZM5.69225 6.05775V4.55775H11.3652V6.05775H5.69225ZM5.69225 9.05775V7.55775H11.3652V9.05775H5.69225ZM13.4423 6.19225C13.1974 6.19225 12.9888 6.10608 12.8163 5.93375C12.6439 5.76125 12.5577 5.55258 12.5577 5.30775C12.5577 5.06292 12.6439 4.85425 12.8163 4.68175C12.9888 4.50942 13.1974 4.42325 13.4423 4.42325C13.6871 4.42325 13.8958 4.50942 14.0682 4.68175C14.2407 4.85425 14.327 5.06292 14.327 5.30775C14.327 5.55258 14.2407 5.76125 14.0682 5.93375C13.8958 6.10608 13.6871 6.19225 13.4423 6.19225ZM13.4423 9.19225C13.1974 9.19225 12.9888 9.10608 12.8163 8.93375C12.6439 8.76125 12.5577 8.55258 12.5577 8.30775C12.5577 8.06292 12.6439 7.85425 12.8163 7.68175C12.9888 7.50942 13.1974 7.42325 13.4423 7.42325C13.6871 7.42325 13.8958 7.50942 14.0682 7.68175C14.2407 7.85425 14.327 8.06292 14.327 8.30775C14.327 8.55258 14.2407 8.76125 14.0682 8.93375C13.8958 9.10608 13.6871 9.19225 13.4423 9.19225ZM2.5 17.3078H12V15.3077H1.5V16.3078C1.5 16.5911 1.59583 16.8286 1.7875 17.0203C1.97917 17.2119 2.21667 17.3078 2.5 17.3078Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.175 10.825C8.39167 10.0417 8 9.1 8 8C8 6.9 8.39167 5.95833 9.175 5.175C9.95833 4.39167 10.9 4 12 4C13.1 4 14.0417 4.39167 14.825 5.175C15.6083 5.95833 16 6.9 16 8C16 9.1 15.6083 10.0417 14.825 10.825C14.0417 11.6083 13.1 12 12 12C10.9 12 9.95833 11.6083 9.175 10.825ZM4 20V17.2C4 16.6333 4.14583 16.1125 4.4375 15.6375C4.72917 15.1625 5.11667 14.8 5.6 14.55C6.63333 14.0333 7.68333 13.6458 8.75 13.3875C9.81667 13.1292 10.9 13 12 13C13.1 13 14.1833 13.1292 15.25 13.3875C16.3167 13.6458 17.3667 14.0333 18.4 14.55C18.8833 14.8 19.2708 15.1625 19.5625 15.6375C19.8542 16.1125 20 16.6333 20 17.2V20H4Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 735 B

+14
View File
@@ -0,0 +1,14 @@
<svg width="75" height="75" viewBox="0 0 75 75" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_20667_718582)">
<path d="M15.625 37.5H59.375" stroke="white" stroke-width="6.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.375 21.875V15.625C9.375 13.9674 10.0335 12.3777 11.2056 11.2056C12.3777 10.0335 13.9674 9.375 15.625 9.375H21.875" stroke="white" stroke-width="6.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.375 53.125V59.375C9.375 61.0326 10.0335 62.6223 11.2056 63.7944C12.3777 64.9665 13.9674 65.625 15.625 65.625H21.875" stroke="white" stroke-width="6.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M53.125 9.375H59.375C61.0326 9.375 62.6223 10.0335 63.7944 11.2056C64.9665 12.3777 65.625 13.9674 65.625 15.625V21.875" stroke="white" stroke-width="6.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M53.125 65.625H59.375C61.0326 65.625 62.6223 64.9665 63.7944 63.7944C64.9665 62.6223 65.625 61.0326 65.625 59.375V53.125" stroke="white" stroke-width="6.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_20667_718582">
<rect width="75" height="75" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.25128 22.0193L4.20703 20.95L7.40703 17.75H4.79153V16.25H9.95128V21.4095H8.45128V18.8193L5.25128 22.0193ZM12.1435 21.5V20H17.6915C17.7814 20 17.8551 19.9712 17.9128 19.9135C17.9704 19.8558 17.9993 19.7821 17.9993 19.6923V8.5H13.4993V4H6.30703C6.2172 4 6.14345 4.02883 6.08578 4.0865C6.02811 4.14417 5.99928 4.21792 5.99928 4.30775V14.0577H4.49928V4.30775C4.49928 3.80908 4.67586 3.38308 5.02903 3.02975C5.38236 2.67658 5.80836 2.5 6.30703 2.5H14.2493L19.4993 7.75V19.6923C19.4993 20.1909 19.3227 20.6169 18.9695 20.9703C18.6162 21.3234 18.1902 21.5 17.6915 21.5H12.1435Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 705 B

+5
View File
@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="24" height="24" rx="4.8" fill="#3396FF"/>
<path d="M15.9495 10.9809L17.4329 9.50916C14.0804 6.18314 9.91496 6.18314 6.5625 9.50916L8.04587 10.9809C10.5954 8.45117 13.4016 8.45117 15.9515 10.9809H15.9495Z" fill="white"/>
<path d="M15.4568 14.4112L11.9979 10.979L8.53847 14.4112L5.07908 10.979L3.59766 12.4488L8.53847 17.353L11.9979 13.9209L15.4568 17.353L20.3977 12.4492L18.9162 10.9794L15.4568 14.4112Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 547 B

Some files were not shown because too many files have changed in this diff Show More