Merge branch 'breez' of https://github.com/cake-tech/cake_wallet into wownero_breez

 Conflicts:
	configure_cake_wallet.sh
	cw_core/lib/amount_converter.dart
	cw_core/lib/wallet_type.dart
	cw_monero/lib/monero_wallet.dart
	lib/view_model/unspent_coins/unspent_coins_list_view_model.dart
	scripts/android/pubspec_gen.sh
	scripts/ios/app_config.sh
	scripts/macos/app_config.sh
	tool/configure.dart
This commit is contained in:
OmarHatem 2024-06-30 05:11:38 +03:00
commit 90508b8726
133 changed files with 4714 additions and 459 deletions

View file

@ -158,6 +158,9 @@ jobs:
echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> lib/.secrets.g.dart echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> lib/.secrets.g.dart
echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const breezApiKey = '${{ secrets.BREEZ_API_KEY }}';" >> cw_lightning/lib/.secrets.g.dart
echo "const greenlightCert = '${{ secrets.GREENLIGHT_CERTIFICATE }}';" >> cw_lightning/lib/.secrets.g.dart
echo "const greenlightKey = '${{ secrets.GREENLIGHT_KEY }}';" >> cw_lightning/lib/.secrets.g.dart
echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
echo "const testCakePayApiKey = '${{ secrets.TEST_CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart echo "const testCakePayApiKey = '${{ secrets.TEST_CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart
echo "const cakePayApiKey = '${{ secrets.CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart echo "const cakePayApiKey = '${{ secrets.CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart

1
.gitignore vendored
View file

@ -128,6 +128,7 @@ cw_haven/android/.externalNativeBuild/
cw_haven/android/.cxx/ cw_haven/android/.cxx/
lib/bitcoin/bitcoin.dart lib/bitcoin/bitcoin.dart
lib/lightning/lightning.dart
lib/monero/monero.dart lib/monero/monero.dart
lib/haven/haven.dart lib/haven/haven.dart
lib/ethereum/ethereum.dart lib/ethereum/ethereum.dart

View file

@ -23,6 +23,7 @@ if (flutterVersionName == null) {
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
apply plugin: 'kotlinx-serialization'
def keystoreProperties = new Properties() def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties') def keystorePropertiesFile = rootProject.file('key.properties')

View file

@ -9,6 +9,8 @@
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<!--bibo01 : hardware option--> <!--bibo01 : hardware option-->
@ -69,6 +71,9 @@
<data android:scheme="bitcoin" /> <data android:scheme="bitcoin" />
<data android:scheme="bitcoin-wallet" /> <data android:scheme="bitcoin-wallet" />
<data android:scheme="bitcoin_wallet" /> <data android:scheme="bitcoin_wallet" />
<data android:scheme="lightning" />
<data android:scheme="lightning-wallet" />
<data android:scheme="lightning_wallet" />
<data android:scheme="monero" /> <data android:scheme="monero" />
<data android:scheme="monero-wallet" /> <data android:scheme="monero-wallet" />
<data android:scheme="monero_wallet" /> <data android:scheme="monero_wallet" />
@ -115,6 +120,24 @@
android:name="android.support.FILE_PROVIDER_PATHS" android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" /> android:resource="@xml/provider_paths" />
</provider> </provider>
<!-- service to handle messaging events -->
<!-- commented out until we decide what to do -->
<!-- <service
android:name=".NotificationFcmService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service> -->
<!-- service to handle the foreground service -->
<service
android:name=".NotificationForegroundService"
android:foregroundServiceType="shortService"
android:exported="false"
android:stopWithTask="false">
</service>
</application> </application>
<queries> <queries>

View file

@ -9,6 +9,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:7.3.0' classpath 'com.android.tools.build:gradle:7.3.0'
classpath 'com.google.gms:google-services:4.3.8' classpath 'com.google.gms:google-services:4.3.8'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View file

@ -0,0 +1,10 @@
<?xml version="1.0" standalone="no"?>
<!-- Generator: Adobe Fireworks 10, Export SVG Extension by Aaron Beall (http://fireworks.abeall.com) . Version: 0.6.1 -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg id="bitcoin_lightning_icon.fw-Page%201" viewBox="0 0 280 280" style="background-color:#ffffff00" version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"
x="0px" y="0px" width="280px" height="280px"
>
<path id="Ellipse" d="M 7 140.5 C 7 66.769 66.769 7 140.5 7 C 214.231 7 274 66.769 274 140.5 C 274 214.231 214.231 274 140.5 274 C 66.769 274 7 214.231 7 140.5 Z" fill="#f7931a"/>
<path d="M 161.1943 51.5 C 153.2349 72.1607 145.2756 94.4107 135.7244 116.6607 C 135.7244 116.6607 135.7244 119.8393 138.9081 119.8393 L 204.1747 119.8393 C 204.1747 119.8393 204.1747 121.4286 205.7667 123.0179 L 110.2545 229.5 C 108.6626 227.9107 108.6626 226.3214 108.6626 224.7321 L 142.0919 153.2143 L 142.0919 146.8571 L 75.2333 146.8571 L 75.2333 140.5 L 156.4187 51.5 L 161.1943 51.5 Z" fill="#ffffff"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
assets/images/warning.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View file

@ -31,5 +31,4 @@ source ./app_env.sh cakewallet
./app_config.sh ./app_config.sh
cd ../.. && flutter pub get cd ../.. && flutter pub get
#flutter packages pub run tool/generate_localization.dart #flutter packages pub run tool/generate_localization.dart
./model_generator.sh ./model_generator.sh
#cd macos && pod install

View file

@ -3,12 +3,13 @@ import 'package:cw_core/crypto_amount_format.dart';
const bitcoinAmountLength = 8; const bitcoinAmountLength = 8;
const bitcoinAmountDivider = 100000000; const bitcoinAmountDivider = 100000000;
const lightningAmountDivider = 1;
final bitcoinAmountFormat = NumberFormat() final bitcoinAmountFormat = NumberFormat()
..maximumFractionDigits = bitcoinAmountLength ..maximumFractionDigits = bitcoinAmountLength
..minimumFractionDigits = 1; ..minimumFractionDigits = 1;
String bitcoinAmountToString({required int amount}) => bitcoinAmountFormat.format( String bitcoinAmountToString({required int amount}) =>
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider)); bitcoinAmountFormat.format(cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider));
double bitcoinAmountToDouble({required int amount}) => double bitcoinAmountToDouble({required int amount}) =>
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider); cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider);
@ -24,3 +25,9 @@ int stringDoubleToBitcoinAmount(String amount) {
return result; return result;
} }
String bitcoinAmountToLightningString({required int amount}) {
String formattedAmount = bitcoinAmountFormat
.format(cryptoAmountToDouble(amount: amount, divider: lightningAmountDivider));
return formattedAmount.substring(0, formattedAmount.length - 2);
}

View file

@ -73,6 +73,7 @@ abstract class ElectrumWalletBase
getAccountHDWallet(currency, networkType, seedBytes, xpub, walletInfo.derivationInfo), getAccountHDWallet(currency, networkType, seedBytes, xpub, walletInfo.derivationInfo),
syncStatus = NotConnectedSyncStatus(), syncStatus = NotConnectedSyncStatus(),
_password = password, _password = password,
_mnemonic = mnemonic,
_feeRates = <int>[], _feeRates = <int>[],
_isTransactionUpdating = false, _isTransactionUpdating = false,
isEnabledAutoGenerateSubaddress = true, isEnabledAutoGenerateSubaddress = true,
@ -91,7 +92,6 @@ abstract class ElectrumWalletBase
this.unspentCoinsInfo = unspentCoinsInfo, this.unspentCoinsInfo = unspentCoinsInfo,
this.network = _getNetwork(networkType, currency), this.network = _getNetwork(networkType, currency),
this.isTestnet = networkType == bitcoin.testnet, this.isTestnet = networkType == bitcoin.testnet,
this._mnemonic = mnemonic,
super(walletInfo) { super(walletInfo) {
this.electrumClient = electrumClient ?? ElectrumClient(); this.electrumClient = electrumClient ?? ElectrumClient();
this.walletInfo = walletInfo; this.walletInfo = walletInfo;
@ -1138,7 +1138,7 @@ abstract class ElectrumWalletBase
} }
@override @override
Future<void> close() async { Future<void> close({bool? switchingToSameWalletType}) async {
try { try {
await electrumClient.close(); await electrumClient.close();
} catch (_) {} } catch (_) {}

View file

@ -33,15 +33,16 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
Map<String, int>? initialRegularAddressIndex, Map<String, int>? initialRegularAddressIndex,
Map<String, int>? initialChangeAddressIndex, Map<String, int>? initialChangeAddressIndex,
}) : super( }) : super(
mnemonic: mnemonic, mnemonic: mnemonic,
password: password, password: password,
walletInfo: walletInfo, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo, unspentCoinsInfo: unspentCoinsInfo,
networkType: litecoinNetwork, networkType: litecoinNetwork,
initialAddresses: initialAddresses, initialAddresses: initialAddresses,
initialBalance: initialBalance, initialBalance: initialBalance,
seedBytes: seedBytes, seedBytes: seedBytes,
currency: CryptoCurrency.ltc) { currency: CryptoCurrency.ltc,
) {
walletAddresses = LitecoinWalletAddresses( walletAddresses = LitecoinWalletAddresses(
walletInfo, walletInfo,
initialAddresses: initialAddresses, initialAddresses: initialAddresses,

View file

@ -27,7 +27,7 @@ dependencies:
git: git:
url: https://github.com/cake-tech/bitbox-flutter.git url: https://github.com/cake-tech/bitbox-flutter.git
ref: Add-Support-For-OP-Return-data ref: Add-Support-For-OP-Return-data
rxdart: ^0.27.5 rxdart: ^0.28.0
cryptography: ^2.0.5 cryptography: ^2.0.5
bitcoin_base: bitcoin_base:
git: git:
@ -53,7 +53,7 @@ dev_dependencies:
build_runner: ^2.4.7 build_runner: ^2.4.7
build_resolvers: ^2.0.9 build_resolvers: ^2.0.9
mobx_codegen: ^2.0.7 mobx_codegen: ^2.0.7
hive_generator: ^1.1.3 hive_generator: ^2.0.1
dependency_overrides: dependency_overrides:
watcher: ^1.1.0 watcher: ^1.1.0

View file

@ -35,15 +35,16 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
Map<String, int>? initialRegularAddressIndex, Map<String, int>? initialRegularAddressIndex,
Map<String, int>? initialChangeAddressIndex, Map<String, int>? initialChangeAddressIndex,
}) : super( }) : super(
mnemonic: mnemonic, mnemonic: mnemonic,
password: password, password: password,
walletInfo: walletInfo, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo, unspentCoinsInfo: unspentCoinsInfo,
networkType: bitcoin.bitcoin, networkType: bitcoin.bitcoin,
initialAddresses: initialAddresses, initialAddresses: initialAddresses,
initialBalance: initialBalance, initialBalance: initialBalance,
seedBytes: seedBytes, seedBytes: seedBytes,
currency: CryptoCurrency.bch) { currency: CryptoCurrency.bch,
) {
walletAddresses = BitcoinCashWalletAddresses( walletAddresses = BitcoinCashWalletAddresses(
walletInfo, walletInfo,
initialAddresses: initialAddresses, initialAddresses: initialAddresses,

View file

@ -43,7 +43,7 @@ dev_dependencies:
sdk: flutter sdk: flutter
build_runner: ^2.4.7 build_runner: ^2.4.7
mobx_codegen: ^2.0.7 mobx_codegen: ^2.0.7
hive_generator: ^1.1.3 hive_generator: ^2.0.1
dependency_overrides: dependency_overrides:
watcher: ^1.1.0 watcher: ^1.1.0

View file

@ -6,6 +6,7 @@ class AmountConverter {
static const _moneroAmountDivider = 1000000000000; static const _moneroAmountDivider = 1000000000000;
static const _wowneroAmountLength = 11; static const _wowneroAmountLength = 11;
static const _wowneroAmountDivider = 100000000000; static const _wowneroAmountDivider = 100000000000;
static const _ethereumAmountDivider = 1000000000000000000;
static const _bitcoinAmountDivider = 100000000; static const _bitcoinAmountDivider = 100000000;
static const _bitcoinAmountLength = 8; static const _bitcoinAmountLength = 8;
static final _bitcoinAmountFormat = NumberFormat() static final _bitcoinAmountFormat = NumberFormat()
@ -18,6 +19,30 @@ class AmountConverter {
..maximumFractionDigits = _wowneroAmountLength ..maximumFractionDigits = _wowneroAmountLength
..minimumFractionDigits = 1; ..minimumFractionDigits = 1;
static int amountStringToInt(CryptoCurrency cryptoCurrency, String amount) {
switch (cryptoCurrency) {
case CryptoCurrency.xmr:
return _moneroParseAmount(amount);
case CryptoCurrency.xhv:
case CryptoCurrency.xag:
case CryptoCurrency.xau:
case CryptoCurrency.xaud:
case CryptoCurrency.xbtc:
case CryptoCurrency.xcad:
case CryptoCurrency.xchf:
case CryptoCurrency.xcny:
case CryptoCurrency.xeur:
case CryptoCurrency.xgbp:
case CryptoCurrency.xjpy:
case CryptoCurrency.xnok:
case CryptoCurrency.xnzd:
case CryptoCurrency.xusd:
return _moneroParseAmount(amount);
default:
return 0;
}
}
static String amountIntToString(CryptoCurrency cryptoCurrency, int amount) { static String amountIntToString(CryptoCurrency cryptoCurrency, int amount) {
switch (cryptoCurrency) { switch (cryptoCurrency) {
case CryptoCurrency.xmr: case CryptoCurrency.xmr:
@ -28,6 +53,8 @@ class AmountConverter {
case CryptoCurrency.bch: case CryptoCurrency.bch:
case CryptoCurrency.ltc: case CryptoCurrency.ltc:
return _bitcoinAmountToString(amount); return _bitcoinAmountToString(amount);
case CryptoCurrency.btcln:
return _lightningAmountToString(amount);
case CryptoCurrency.xhv: case CryptoCurrency.xhv:
case CryptoCurrency.xag: case CryptoCurrency.xag:
case CryptoCurrency.xau: case CryptoCurrency.xau:
@ -57,6 +84,11 @@ class AmountConverter {
static String _bitcoinAmountToString(int amount) => _bitcoinAmountFormat static String _bitcoinAmountToString(int amount) => _bitcoinAmountFormat
.format(cryptoAmountToDouble(amount: amount, divider: _bitcoinAmountDivider)); .format(cryptoAmountToDouble(amount: amount, divider: _bitcoinAmountDivider));
static String _lightningAmountToString(int amount) {
String formattedAmount = _bitcoinAmountFormat.format(amount);
return formattedAmount.substring(0, formattedAmount.length - 2);
}
static String _wowneroAmountToString(int amount) => _wowneroAmountFormat static String _wowneroAmountToString(int amount) => _wowneroAmountFormat
.format(cryptoAmountToDouble(amount: amount, divider: _wowneroAmountDivider)); .format(cryptoAmountToDouble(amount: amount, divider: _wowneroAmountDivider));
} }

View file

@ -103,6 +103,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
CryptoCurrency.kaspa, CryptoCurrency.kaspa,
CryptoCurrency.digibyte, CryptoCurrency.digibyte,
CryptoCurrency.usdtSol, CryptoCurrency.usdtSol,
CryptoCurrency.btcln,
CryptoCurrency.usdcTrc20, CryptoCurrency.usdcTrc20,
CryptoCurrency.tbtc, CryptoCurrency.tbtc,
CryptoCurrency.wow, CryptoCurrency.wow,
@ -190,7 +191,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
static const scrt = CryptoCurrency(title: 'SCRT', fullName: 'Secret Network', raw: 59, name: 'scrt', iconPath: 'assets/images/scrt_icon.png', decimals: 6); static const scrt = CryptoCurrency(title: 'SCRT', fullName: 'Secret Network', raw: 59, name: 'scrt', iconPath: 'assets/images/scrt_icon.png', decimals: 6);
static const uni = CryptoCurrency(title: 'UNI', tag: 'ETH', fullName: 'Uniswap', raw: 60, name: 'uni', iconPath: 'assets/images/uni_icon.png', decimals: 18); static const uni = CryptoCurrency(title: 'UNI', tag: 'ETH', fullName: 'Uniswap', raw: 60, name: 'uni', iconPath: 'assets/images/uni_icon.png', decimals: 18);
static const stx = CryptoCurrency(title: 'STX', fullName: 'Stacks', raw: 61, name: 'stx', iconPath: 'assets/images/stx_icon.png', decimals: 8); static const stx = CryptoCurrency(title: 'STX', fullName: 'Stacks', raw: 61, name: 'stx', iconPath: 'assets/images/stx_icon.png', decimals: 8);
static const btcln = CryptoCurrency(title: 'BTC', tag: 'LN', fullName: 'Bitcoin Lightning Network', raw: 62, name: 'btcln', iconPath: 'assets/images/btc.png', decimals: 8); static const btcln = CryptoCurrency(title: 'sats', tag: 'LN', fullName: 'Bitcoin Lightning Network', raw: 62, name: 'sats', iconPath: 'assets/images/lightning_logo.png', decimals: 8);
static const shib = CryptoCurrency(title: 'SHIB', tag: 'ETH', fullName: 'Shiba Inu', raw: 63, name: 'shib', iconPath: 'assets/images/shib_icon.png', decimals: 18); static const shib = CryptoCurrency(title: 'SHIB', tag: 'ETH', fullName: 'Shiba Inu', raw: 63, name: 'shib', iconPath: 'assets/images/shib_icon.png', decimals: 18);
static const aave = CryptoCurrency(title: 'AAVE', tag: 'ETH', fullName: 'Aave', raw: 64, name: 'aave', iconPath: 'assets/images/aave_icon.png', decimals: 18); static const aave = CryptoCurrency(title: 'AAVE', tag: 'ETH', fullName: 'Aave', raw: 64, name: 'aave', iconPath: 'assets/images/aave_icon.png', decimals: 18);
static const arb = CryptoCurrency(title: 'ARB', fullName: 'Arbitrum', raw: 65, name: 'arb', iconPath: 'assets/images/arb_icon.png', decimals: 18); static const arb = CryptoCurrency(title: 'ARB', fullName: 'Arbitrum', raw: 65, name: 'arb', iconPath: 'assets/images/arb_icon.png', decimals: 18);
@ -224,7 +225,6 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
static const tbtc = CryptoCurrency(title: 'tBTC', fullName: 'Testnet Bitcoin', raw: 93, name: 'tbtc', iconPath: 'assets/images/tbtc.png', decimals: 8); 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/wownero_icon.png', decimals: 11); static const wow = CryptoCurrency(title: 'WOW', fullName: 'Wownero', raw: 94, name: 'wow', iconPath: 'assets/images/wownero_icon.png', decimals: 11);
static final Map<int, CryptoCurrency> _rawCurrencyMap = static final Map<int, CryptoCurrency> _rawCurrencyMap =
[...all, ...havenCurrencies].fold<Map<int, CryptoCurrency>>(<int, CryptoCurrency>{}, (acc, item) { [...all, ...havenCurrencies].fold<Map<int, CryptoCurrency>>(<int, CryptoCurrency>{}, (acc, item) {
acc.addAll({item.raw: item}); acc.addAll({item.raw: item});

View file

@ -8,6 +8,8 @@ CryptoCurrency currencyForWalletType(WalletType type, {bool? isTestnet}) {
return CryptoCurrency.tbtc; return CryptoCurrency.tbtc;
} }
return CryptoCurrency.btc; return CryptoCurrency.btc;
case WalletType.lightning:
return CryptoCurrency.btcln;
case WalletType.monero: case WalletType.monero:
return CryptoCurrency.xmr; return CryptoCurrency.xmr;
case WalletType.litecoin: case WalletType.litecoin:

View file

@ -80,7 +80,7 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
Future<void> rescan({required int height}); Future<void> rescan({required int height});
void close(); void close({bool? switchingToSameWalletType});
Future<void> changePassword(String password); Future<void> changePassword(String password);

View file

@ -7,6 +7,7 @@ part 'wallet_type.g.dart';
const walletTypes = [ const walletTypes = [
WalletType.monero, WalletType.monero,
WalletType.bitcoin, WalletType.bitcoin,
WalletType.lightning,
WalletType.litecoin, WalletType.litecoin,
WalletType.haven, WalletType.haven,
WalletType.ethereum, WalletType.ethereum,
@ -16,6 +17,7 @@ const walletTypes = [
WalletType.polygon, WalletType.polygon,
WalletType.solana, WalletType.solana,
WalletType.tron, WalletType.tron,
WalletType.wownero,
]; ];
@HiveType(typeId: WALLET_TYPE_TYPE_ID) @HiveType(typeId: WALLET_TYPE_TYPE_ID)
@ -58,6 +60,9 @@ enum WalletType {
@HiveField(12) @HiveField(12)
wownero, wownero,
@HiveField(13)
lightning,
} }
int serializeToInt(WalletType type) { int serializeToInt(WalletType type) {
@ -86,6 +91,8 @@ int serializeToInt(WalletType type) {
return 10; return 10;
case WalletType.wownero: case WalletType.wownero:
return 11; return 11;
case WalletType.lightning:
return 12;
case WalletType.none: case WalletType.none:
return -1; return -1;
} }
@ -117,6 +124,8 @@ WalletType deserializeFromInt(int raw) {
return WalletType.tron; return WalletType.tron;
case 11: case 11:
return WalletType.wownero; return WalletType.wownero;
case 12:
return WalletType.lightning;
default: default:
throw Exception('Unexpected token: $raw for WalletType deserializeFromInt'); throw Exception('Unexpected token: $raw for WalletType deserializeFromInt');
} }
@ -142,6 +151,8 @@ String walletTypeToString(WalletType type) {
return 'Banano'; return 'Banano';
case WalletType.polygon: case WalletType.polygon:
return 'Polygon'; return 'Polygon';
case WalletType.lightning:
return 'Lightning';
case WalletType.solana: case WalletType.solana:
return 'Solana'; return 'Solana';
case WalletType.tron: case WalletType.tron:
@ -173,6 +184,8 @@ String walletTypeToDisplayName(WalletType type) {
return 'Banano (BAN)'; return 'Banano (BAN)';
case WalletType.polygon: case WalletType.polygon:
return 'Polygon (MATIC)'; return 'Polygon (MATIC)';
case WalletType.lightning:
return 'Bitcoin (Lightning)';
case WalletType.solana: case WalletType.solana:
return 'Solana (SOL)'; return 'Solana (SOL)';
case WalletType.tron: case WalletType.tron:
@ -207,6 +220,8 @@ CryptoCurrency walletTypeToCryptoCurrency(WalletType type, {bool isTestnet = fal
return CryptoCurrency.banano; return CryptoCurrency.banano;
case WalletType.polygon: case WalletType.polygon:
return CryptoCurrency.maticpoly; return CryptoCurrency.maticpoly;
case WalletType.lightning:
return CryptoCurrency.btcln;
case WalletType.solana: case WalletType.solana:
return CryptoCurrency.sol; return CryptoCurrency.sol;
case WalletType.tron: case WalletType.tron:

View file

@ -191,7 +191,7 @@ abstract class EVMChainWalletBase
} }
@override @override
void close() { void close({bool? switchingToSameWalletType}) {
_client.stop(); _client.stop();
_transactionsUpdateTimer?.cancel(); _transactionsUpdateTimer?.cancel();
} }

View file

@ -43,7 +43,7 @@ dev_dependencies:
sdk: flutter sdk: flutter
build_runner: ^2.4.7 build_runner: ^2.4.7
mobx_codegen: ^2.0.7 mobx_codegen: ^2.0.7
hive_generator: ^1.1.3 hive_generator: ^2.0.1
flutter_lints: ^2.0.0 flutter_lints: ^2.0.0
flutter: flutter:

View file

@ -106,7 +106,7 @@ abstract class HavenWalletBase
Future<void>? updateBalance() => null; Future<void>? updateBalance() => null;
@override @override
void close() { void close({bool? switchingToSameWalletType}) {
_listener?.stop(); _listener?.stop();
_onAccountChangeReaction?.reaction.dispose(); _onAccountChangeReaction?.reaction.dispose();
_autoSaveTimer?.cancel(); _autoSaveTimer?.cancel();

View file

@ -27,7 +27,7 @@ dev_dependencies:
build_runner: ^2.4.7 build_runner: ^2.4.7
mobx_codegen: ^2.0.7 mobx_codegen: ^2.0.7
build_resolvers: ^2.0.9 build_resolvers: ^2.0.9
hive_generator: ^1.1.3 hive_generator: ^2.0.1
dependency_overrides: dependency_overrides:
watcher: ^1.1.0 watcher: ^1.1.0

74
cw_lightning/.gitignore vendored Normal file
View file

@ -0,0 +1,74 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
build/
# Android related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Flutter.podspec
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/Flutter/flutter_export_environment.sh
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3

10
cw_lightning/.metadata Normal file
View file

@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: b1395592de68cc8ac4522094ae59956dd21a91db
channel: stable
project_type: package

View file

@ -0,0 +1,3 @@
## [0.0.1] - TODO: Add release date.
* TODO: Describe initial release.

1
cw_lightning/LICENSE Normal file
View file

@ -0,0 +1 @@
TODO: Add your license here.

14
cw_lightning/README.md Normal file
View file

@ -0,0 +1,14 @@
# cw_bitcoin
A new Flutter package project.
## Getting Started
This project is a starting point for a Dart
[package](https://flutter.dev/developing-packages/),
a library module containing code that can be shared easily across
multiple Flutter or Dart projects.
For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View file

@ -0,0 +1,7 @@
library cw_lightning;
/// A Calculator.
class Calculator {
/// Returns [value] plus 1.
int addOne(int value) => value + 1;
}

View file

@ -0,0 +1,45 @@
import 'dart:convert';
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
import 'package:cw_bitcoin/electrum_balance.dart';
import 'package:cw_core/balance.dart';
class LightningBalance extends ElectrumBalance {
LightningBalance({required this.confirmed, required this.unconfirmed, required this.frozen})
: super(
confirmed: confirmed,
unconfirmed: unconfirmed,
frozen: frozen,
);
static LightningBalance? fromJSON(String? jsonSource) {
if (jsonSource == null) {
return null;
}
final decoded = json.decode(jsonSource) as Map;
return LightningBalance(
confirmed: decoded['confirmed'] as int? ?? 0,
unconfirmed: decoded['unconfirmed'] as int? ?? 0,
frozen: decoded['frozen'] as int? ?? 0);
}
final int confirmed;
final int unconfirmed;
final int frozen;
@override
String get formattedAvailableBalance => bitcoinAmountToLightningString(amount: confirmed);
@override
String get formattedAdditionalBalance => bitcoinAmountToLightningString(amount: unconfirmed);
@override
String get formattedUnAvailableBalance {
final frozenFormatted = bitcoinAmountToLightningString(amount: frozen);
return frozenFormatted == '0.0' ? '' : frozenFormatted;
}
String toJSON() =>
json.encode({'confirmed': confirmed, 'unconfirmed': unconfirmed, 'frozen': frozen});
}

View file

@ -0,0 +1,19 @@
import 'package:cw_core/receive_page_option.dart';
class LightningReceivePageOption implements ReceivePageOption {
static const lightningOnchain = LightningReceivePageOption._('lightningOnchain');
static const lightningInvoice = LightningReceivePageOption._('lightningInvoice');
const LightningReceivePageOption._(this.value);
final String value;
String toString() {
return value;
}
static const all = [
LightningReceivePageOption.lightningInvoice,
LightningReceivePageOption.lightningOnchain
];
}

View file

@ -0,0 +1,57 @@
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
import 'package:cw_bitcoin/electrum_transaction_info.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/format_amount.dart';
import 'package:cw_core/wallet_type.dart';
class LightningTransactionInfo extends ElectrumTransactionInfo {
LightningTransactionInfo({
required String id,
required int amount,
int? fee,
required TransactionDirection direction,
required bool isPending,
required DateTime date,
}) : super(
WalletType.lightning,
amount: amount,
fee: fee,
direction: direction,
date: date,
isPending: isPending,
id: id,
confirmations: 0,
height: 0,
) {}
@override
String amountFormatted() =>
'${formatAmount(bitcoinAmountToLightningString(amount: amount))} ${walletTypeToCryptoCurrency(type).title}';
@override
String? feeFormatted() => fee != null
? '${formatAmount(bitcoinAmountToLightningString(amount: fee!))} ${walletTypeToCryptoCurrency(type).title}'
: '';
factory LightningTransactionInfo.fromJson(Map<String, dynamic> data, WalletType type) {
return LightningTransactionInfo(
id: data['id'] as String,
amount: data['amount'] as int,
fee: data['fee'] as int,
direction: parseTransactionDirectionFromInt(data['direction'] as int),
date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
isPending: data['isPending'] as bool,
);
}
Map<String, dynamic> toJson() {
final m = <String, dynamic>{};
m['id'] = id;
m['amount'] = amount;
m['direction'] = direction.index;
m['date'] = date.millisecondsSinceEpoch;
m['isPending'] = isPending;
m['fee'] = fee;
return m;
}
}

View file

@ -0,0 +1,422 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:bitbox/bitbox.dart';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:breez_sdk/breez_sdk.dart';
import 'package:breez_sdk/bridge_generated.dart';
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
import 'package:cw_bitcoin/electrum_wallet_snapshot.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/pathForWallet.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/unspent_coins_info.dart';
import 'package:cw_lightning/lightning_balance.dart';
import 'package:cw_lightning/lightning_transaction_info.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:flutter/foundation.dart';
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
import 'package:cw_core/wallet_info.dart';
import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart';
import 'package:path_provider/path_provider.dart';
import 'package:cw_lightning/.secrets.g.dart' as secrets;
import 'package:cw_bitcoin/electrum_wallet.dart';
import 'package:bip39/bip39.dart' as bip39;
part 'lightning_wallet.g.dart';
class LightningWallet = LightningWalletBase with _$LightningWallet;
abstract class LightningWalletBase extends ElectrumWallet with Store {
bool _isTransactionUpdating;
@override
@observable
SyncStatus syncStatus;
LightningWalletBase({
required String mnemonic,
required String password,
required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required Uint8List seedBytes,
String? addressPageType,
List<BitcoinAddressRecord>? initialAddresses,
LightningBalance? initialBalance,
Map<String, int>? initialRegularAddressIndex,
Map<String, int>? initialChangeAddressIndex,
}) : _isTransactionUpdating = false,
syncStatus = NotConnectedSyncStatus(),
_balance = ObservableMap<CryptoCurrency, LightningBalance>(),
mnemonic = mnemonic,
super(
password: password,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo,
networkType: bitcoin.bitcoin,
initialAddresses: initialAddresses,
initialBalance: initialBalance,
seedBytes: seedBytes,
currency: CryptoCurrency.btcln,
) {
_balance[CryptoCurrency.btcln] =
initialBalance ?? LightningBalance(confirmed: 0, unconfirmed: 0, frozen: 0);
String derivationPath = walletInfo.derivationInfo!.derivationPath!;
String sideDerivationPath = derivationPath.substring(0, derivationPath.length - 1) + "1";
final hd = bitcoin.HDWallet.fromSeed(seedBytes, network: networkType);
walletAddresses = BitcoinWalletAddresses(
walletInfo,
initialAddresses: initialAddresses,
initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex,
mainHd: hd.derivePath(derivationPath),
sideHd: hd.derivePath(sideDerivationPath),
network: network,
);
// initialize breez:
try {
setupBreez(seedBytes);
} catch (e) {
print("Error initializing Breez: $e");
}
autorun((_) {
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
});
}
late final ObservableMap<CryptoCurrency, LightningBalance> _balance;
StreamSubscription<List<Payment>>? _paymentsSub;
StreamSubscription<NodeState?>? _nodeStateSub;
StreamSubscription<LogEntry>? _logStream;
@override
@computed
ObservableMap<CryptoCurrency, LightningBalance> get balance => _balance;
static Future<LightningWallet> create(
{required String mnemonic,
required String password,
required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
String? addressPageType,
List<BitcoinAddressRecord>? initialAddresses,
LightningBalance? initialBalance,
Map<String, int>? initialRegularAddressIndex,
Map<String, int>? initialChangeAddressIndex}) async {
late final Uint8List seedBytes;
// electrum:
if (validateMnemonic(mnemonic)) {
seedBytes = await mnemonicToSeedBytes(mnemonic);
// bip39:
} else if (bip39.validateMnemonic(mnemonic)) {
seedBytes = await bip39.mnemonicToSeed(mnemonic);
} else {
throw Exception("Invalid mnemonic!");
}
return LightningWallet(
mnemonic: mnemonic,
password: password,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: initialAddresses,
initialBalance: initialBalance,
seedBytes: seedBytes,
initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex,
addressPageType: addressPageType,
);
}
static Future<LightningWallet> open({
required String name,
required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required String password,
}) async {
final snp =
await ElectrumWalletSnapshot.load(name, walletInfo.type, password, BitcoinNetwork.mainnet);
print("OPENING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
return LightningWallet(
mnemonic: snp.mnemonic!,
password: password,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: snp.addresses,
initialBalance: LightningBalance(
confirmed: snp.balance.confirmed,
unconfirmed: snp.balance.unconfirmed,
frozen: snp.balance.frozen,
),
seedBytes: await Mnemonic.toSeed(snp.mnemonic!),
initialRegularAddressIndex: snp.regularAddressIndex,
initialChangeAddressIndex: snp.changeAddressIndex,
addressPageType: snp.addressPageType,
);
}
Future<void> _handleNodeState(NodeState? nodeState) async {
if (nodeState == null) return;
_balance[CryptoCurrency.btcln] = LightningBalance(
confirmed: nodeState.maxPayableMsat ~/ 1000,
unconfirmed: nodeState.maxReceivableMsat ~/ 1000,
frozen: 0,
);
}
Future<void> _handlePayments(List<Payment> payments) async {
_isTransactionUpdating = true;
final txs = convertToTxInfo(payments);
transactionHistory.addMany(txs);
_isTransactionUpdating = false;
}
@override
Future<void> renameWalletFiles(String newWalletName) async {
await stopBreez(true);
await super.renameWalletFiles(newWalletName);
await setupBreez(await Mnemonic.toSeed(mnemonic));
}
void _logSdkEntries(LogEntry entry) {
switch (entry.level) {
case "ERROR":
case "WARN":
case "INFO":
// case "DEBUG":
// case "TRACE":
print("BREEZ:${entry.level}: ${entry.line}");
break;
}
}
Future<void> setupBreez(Uint8List seedBytes) async {
final sdk = await BreezSDK();
_logStream?.cancel();
_logStream = sdk.logStream.listen(_logSdkEntries);
try {
if (!(await sdk.isInitialized())) {
sdk.initialize();
}
} catch (e) {
print("Error initializing Breez: $e");
return;
}
GreenlightCredentials greenlightCredentials = GreenlightCredentials(
developerKey: base64.decode(secrets.greenlightKey),
developerCert: base64.decode(secrets.greenlightCert),
);
NodeConfig breezNodeConfig = NodeConfig.greenlight(
config: GreenlightNodeConfig(
partnerCredentials: greenlightCredentials,
inviteCode: null,
),
);
Config breezConfig = await sdk.defaultConfig(
envType: EnvironmentType.Production,
apiKey: secrets.breezApiKey,
nodeConfig: breezNodeConfig,
);
String workingDir = await pathForWalletDir(name: walletInfo.name, type: type);
workingDir = "$workingDir/breez/";
new Directory(workingDir).createSync(recursive: true);
breezConfig = breezConfig.copyWith(workingDir: workingDir);
// disconnect if already connected
try {
if (await sdk.isInitialized()) {
await sdk.disconnect();
}
} catch (e, s) {
print("ERROR disconnecting from Breez: $e\n$s");
}
try {
await sdk.connect(
req: ConnectRequest(
config: breezConfig,
seed: seedBytes,
),
);
} catch (e, s) {
print("Error connecting to Breez: $e\n$s");
}
await _nodeStateSub?.cancel();
_nodeStateSub = sdk.nodeStateStream.listen((event) {
_handleNodeState(event);
});
await _handleNodeState(await sdk.nodeInfo());
await _paymentsSub?.cancel();
_paymentsSub = sdk.paymentsStream.listen((List<Payment> payments) {
_handlePayments(payments);
});
await _handlePayments(await sdk.listPayments(req: ListPaymentsRequest()));
print("initialized breez: ${(await sdk.isInitialized())}");
}
Future<void> stopBreez(bool disconnect) async {
if (disconnect) {
final sdk = await BreezSDK();
if (await sdk.isInitialized()) {
await sdk.disconnect();
}
}
await _nodeStateSub?.cancel();
await _paymentsSub?.cancel();
}
@action
@override
Future<void> startSync() async {
try {
syncStatus = AttemptingSyncStatus();
await updateTransactions();
syncStatus = SyncedSyncStatus();
} catch (e) {
print(e);
syncStatus = FailedSyncStatus();
rethrow;
}
}
@override
Future<void> changePassword(String password) {
throw UnimplementedError("changePassword");
}
@action
@override
Future<void> connectToNode({required Node node}) async {
try {
syncStatus = ConnectingSyncStatus();
await updateTransactions();
syncStatus = ConnectedSyncStatus();
} catch (e) {
print(e);
syncStatus = FailedSyncStatus();
}
}
@override
Future<PendingTransaction> createTransaction(Object credentials) async {
throw UnimplementedError("createTransaction");
}
Future<bool> updateTransactions() async {
try {
if (_isTransactionUpdating) {
return false;
}
_isTransactionUpdating = true;
final transactions = await fetchTransactions();
transactionHistory.addMany(transactions);
await transactionHistory.save();
_isTransactionUpdating = false;
return true;
} catch (_) {
_isTransactionUpdating = false;
return false;
}
}
Map<String, LightningTransactionInfo> convertToTxInfo(List<Payment> payments) {
Map<String, LightningTransactionInfo> transactions = {};
for (Payment tx in payments) {
if (tx.paymentType == PaymentType.ClosedChannel) {
continue;
}
bool isSend = tx.paymentType == PaymentType.Sent;
transactions[tx.id] = LightningTransactionInfo(
isPending: false,
id: tx.id,
amount: tx.amountMsat ~/ 1000,
fee: tx.feeMsat ~/ 1000,
date: DateTime.fromMillisecondsSinceEpoch(tx.paymentTime * 1000),
direction: isSend ? TransactionDirection.outgoing : TransactionDirection.incoming,
);
}
return transactions;
}
@override
Future<Map<String, LightningTransactionInfo>> fetchTransactions() async {
final sdk = await BreezSDK();
final payments = await sdk.listPayments(req: ListPaymentsRequest());
final transactions = convertToTxInfo(payments);
return transactions;
}
@override
Future<void> rescan({
required int height,
int? chainTip,
ScanData? scanData,
bool? doSingleScan,
bool? usingElectrs,
}) async {
updateTransactions();
}
Future<void> init() async {
await walletAddresses.init();
await transactionHistory.init();
await save();
}
String toJSON() => json.encode({
'mnemonic': mnemonic,
'account_index': walletAddresses.currentReceiveAddressIndexByType,
'change_address_index': walletAddresses.currentChangeAddressIndexByType,
'addresses': walletAddresses.allAddresses.map((addr) => addr.toJSON()).toList(),
'address_page_type': walletInfo.addressPageType == null
? SegwitAddresType.p2wpkh.toString()
: walletInfo.addressPageType.toString(),
'balance': balance[currency]?.toJSON(),
'network_type': network == BitcoinNetwork.testnet ? 'testnet' : 'mainnet',
});
Future<void> updateBalance() async {
// balance is updated automatically
}
@override
String mnemonic;
@override
String get seed => mnemonic;
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
@override
Future<void> close({bool? switchingToSameWalletType}) async {
try {
await electrumClient.close();
} catch (_) {}
try {
bool shouldDisconnect = switchingToSameWalletType == null || !switchingToSameWalletType;
await stopBreez(shouldDisconnect);
} catch (e, s) {
print("Error stopping breez: $e\n$s");
}
}
}

View file

@ -0,0 +1,134 @@
import 'dart:io';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
import 'package:cw_bitcoin/mnemonic_is_incorrect_exception.dart';
import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_lightning/lightning_wallet.dart';
import 'package:hive/hive.dart';
import 'package:collection/collection.dart';
import 'package:bip39/bip39.dart' as bip39;
class LightningWalletService extends WalletService<
BitcoinNewWalletCredentials,
BitcoinRestoreWalletFromSeedCredentials,
BitcoinRestoreWalletFromWIFCredentials,
BitcoinRestoreWalletFromHardware> {
LightningWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
final Box<WalletInfo> walletInfoSource;
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
@override
WalletType getType() => WalletType.lightning;
@override
Future<LightningWallet> create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async {
final wallet = await LightningWalletBase.create(
mnemonic: bip39.generateMnemonic(),
password: credentials.password!,
walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource,
);
await wallet.save();
await wallet.init();
return wallet;
}
@override
Future<bool> isWalletExit(String name) async =>
File(await pathForWallet(name: name, type: getType())).existsSync();
@override
Future<LightningWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
try {
final wallet = await LightningWalletBase.open(
password: password,
name: name,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
);
await wallet.init();
saveBackup(name);
return wallet;
} catch (_) {
await restoreWalletFilesFromBackup(name);
final wallet = await LightningWalletBase.open(
password: password,
name: name,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
);
await wallet.init();
return wallet;
}
}
@override
Future<LightningWallet> restoreFromHardwareWallet(BitcoinRestoreWalletFromHardware credentials,
{bool? isTestnet}) {
throw UnimplementedError(
"Restoring a Lightning wallet from a hardware wallet is not yet supported!");
}
@override
Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
}
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
final currentWallet = await LightningWalletBase.open(
password: password,
name: currentName,
walletInfo: currentWalletInfo,
unspentCoinsInfo: unspentCoinsInfoSource);
await currentWallet.renameWalletFiles(newName);
await saveBackup(newName);
final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
}
@override
Future<LightningWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials,
{bool? isTestnet}) async =>
throw UnimplementedError();
@override
Future<LightningWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials,
{bool? isTestnet}) async {
if (!bip39.validateMnemonic(credentials.mnemonic) && !validateMnemonic(credentials.mnemonic)) {
throw BitcoinMnemonicIsIncorrectException();
}
final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet;
credentials.walletInfo?.network = network.value;
final wallet = await LightningWalletBase.create(
password: credentials.password!,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource,
);
await wallet.save();
await wallet.init();
return wallet;
}
}

920
cw_lightning/pubspec.lock Normal file
View file

@ -0,0 +1,920 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a
url: "https://pub.dev"
source: hosted
version: "61.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562
url: "https://pub.dev"
source: hosted
version: "5.13.0"
archive:
dependency: transitive
description:
name: archive
sha256: "20071638cbe4e5964a427cfa0e86dce55d060bc7d82d56f3554095d7239a8765"
url: "https://pub.dev"
source: hosted
version: "3.4.2"
args:
dependency: transitive
description:
name: args
sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
asn1lib:
dependency: transitive
description:
name: asn1lib
sha256: ab96a1cb3beeccf8145c52e449233fe68364c9641623acd3adad66f8184f1039
url: "https://pub.dev"
source: hosted
version: "1.4.0"
async:
dependency: transitive
description:
name: async
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
url: "https://pub.dev"
source: hosted
version: "2.11.0"
bech32:
dependency: transitive
description:
path: "."
ref: "cake-0.2.2"
resolved-ref: "05755063b593aa6cca0a4820a318e0ce17de6192"
url: "https://github.com/cake-tech/bech32.git"
source: git
version: "0.2.2"
bip32:
dependency: transitive
description:
name: bip32
sha256: "54787cd7a111e9d37394aabbf53d1fc5e2e0e0af2cd01c459147a97c0e3f8a97"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
bip39:
dependency: transitive
description:
name: bip39
sha256: de1ee27ebe7d96b84bb3a04a4132a0a3007dcdd5ad27dd14aa87a29d97c45edc
url: "https://pub.dev"
source: hosted
version: "1.0.6"
bitbox:
dependency: "direct main"
description:
path: "."
ref: Add-Support-For-OP-Return-data
resolved-ref: "57b78afb85bd2c30d3cdb9f7884f3878a62be442"
url: "https://github.com/cake-tech/bitbox-flutter.git"
source: git
version: "1.0.1"
bitcoin_base:
dependency: "direct main"
description:
path: "."
ref: cake-update-v2
resolved-ref: "3fd81d238b990bb767fc7a4fdd5053a22a142e2e"
url: "https://github.com/cake-tech/bitcoin_base.git"
source: git
version: "4.2.0"
bitcoin_flutter:
dependency: "direct main"
description:
path: "."
ref: cake-update-v4
resolved-ref: e19ffb7e7977278a75b27e0479b3c6f4034223b3
url: "https://github.com/cake-tech/bitcoin_flutter.git"
source: git
version: "2.1.0"
blockchain_utils:
dependency: transitive
description:
name: blockchain_utils
sha256: "38ef5f4a22441ac4370aed9071dc71c460acffc37c79b344533f67d15f24c13c"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
breez_sdk:
dependency: "direct main"
description:
path: "."
ref: "v0.4.0-rc2"
resolved-ref: "8762a59b1f823d3c37ee04b95bfe4eb88ea4eb6c"
url: "https://github.com/breez/breez-sdk-flutter.git"
source: git
version: "0.4.0-rc2"
bs58check:
dependency: transitive
description:
name: bs58check
sha256: c4a164d42b25c2f6bc88a8beccb9fc7d01440f3c60ba23663a20a70faf484ea9
url: "https://pub.dev"
source: hosted
version: "1.0.2"
build:
dependency: transitive
description:
name: build
sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777"
url: "https://pub.dev"
source: hosted
version: "2.3.1"
build_cli_annotations:
dependency: transitive
description:
name: build_cli_annotations
sha256: b59d2769769efd6c9ff6d4c4cede0be115a566afc591705c2040b707534b1172
url: "https://pub.dev"
source: hosted
version: "2.1.0"
build_config:
dependency: transitive
description:
name: build_config
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
url: "https://pub.dev"
source: hosted
version: "1.1.1"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
build_resolvers:
dependency: "direct dev"
description:
name: build_resolvers
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
url: "https://pub.dev"
source: hosted
version: "2.0.10"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727
url: "https://pub.dev"
source: hosted
version: "2.3.3"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292"
url: "https://pub.dev"
source: hosted
version: "7.2.7"
built_collection:
dependency: transitive
description:
name: built_collection
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
url: "https://pub.dev"
source: hosted
version: "5.1.1"
built_value:
dependency: transitive
description:
name: built_value
sha256: "169565c8ad06adb760c3645bf71f00bff161b00002cace266cad42c5d22a7725"
url: "https://pub.dev"
source: hosted
version: "8.4.3"
characters:
dependency: transitive
description:
name: characters
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
clock:
dependency: transitive
description:
name: clock
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
url: "https://pub.dev"
source: hosted
version: "1.1.1"
code_builder:
dependency: transitive
description:
name: code_builder
sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe"
url: "https://pub.dev"
source: hosted
version: "4.4.0"
collection:
dependency: transitive
description:
name: collection
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev"
source: hosted
version: "1.18.0"
convert:
dependency: transitive
description:
name: convert
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
crypto:
dependency: transitive
description:
name: crypto
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
url: "https://pub.dev"
source: hosted
version: "3.0.2"
cryptography:
dependency: "direct main"
description:
name: cryptography
sha256: e0e37f79665cd5c86e8897f9abe1accfe813c0cc5299dab22256e22fddc1fef8
url: "https://pub.dev"
source: hosted
version: "2.0.5"
cw_bitcoin:
dependency: "direct main"
description:
path: "../cw_bitcoin"
relative: true
source: path
version: "0.0.1"
cw_core:
dependency: "direct main"
description:
path: "../cw_core"
relative: true
source: path
version: "0.0.1"
dart_style:
dependency: transitive
description:
name: dart_style
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
url: "https://pub.dev"
source: hosted
version: "2.2.4"
encrypt:
dependency: transitive
description:
name: encrypt
sha256: "4fd4e4fdc21b9d7d4141823e1e6515cd94e7b8d84749504c232999fba25d9bbb"
url: "https://pub.dev"
source: hosted
version: "5.0.1"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
ffi:
dependency: transitive
description:
name: ffi
sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
file:
dependency: transitive
description:
name: file
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_mobx:
dependency: "direct main"
description:
name: flutter_mobx
sha256: "0da4add0016387a7bf309a0d0c41d36c6b3ae25ed7a176409267f166509e723e"
url: "https://pub.dev"
source: hosted
version: "2.0.6+5"
flutter_rust_bridge:
dependency: transitive
description:
name: flutter_rust_bridge
sha256: "02720226035257ad0b571c1256f43df3e1556a499f6bcb004849a0faaa0e87f0"
url: "https://pub.dev"
source: hosted
version: "1.82.6"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
freezed:
dependency: transitive
description:
name: freezed
sha256: "57247f692f35f068cae297549a46a9a097100685c6780fe67177503eea5ed4e5"
url: "https://pub.dev"
source: hosted
version: "2.4.7"
freezed_annotation:
dependency: transitive
description:
name: freezed_annotation
sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d
url: "https://pub.dev"
source: hosted
version: "2.4.1"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612"
url: "https://pub.dev"
source: hosted
version: "3.2.0"
glob:
dependency: transitive
description:
name: glob
sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
graphs:
dependency: transitive
description:
name: graphs
sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2
url: "https://pub.dev"
source: hosted
version: "2.2.0"
hex:
dependency: transitive
description:
name: hex
sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a"
url: "https://pub.dev"
source: hosted
version: "0.2.0"
hive:
dependency: transitive
description:
name: hive
sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
url: "https://pub.dev"
source: hosted
version: "2.2.3"
hive_generator:
dependency: "direct dev"
description:
name: hive_generator
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
http:
dependency: "direct main"
description:
name: http
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
intl:
dependency: "direct main"
description:
name: intl
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
url: "https://pub.dev"
source: hosted
version: "0.18.1"
io:
dependency: transitive
description:
name: io
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
js:
dependency: transitive
description:
name: js
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.dev"
source: hosted
version: "0.6.7"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317
url: "https://pub.dev"
source: hosted
version: "4.8.0"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
url: "https://pub.dev"
source: hosted
version: "10.0.0"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
url: "https://pub.dev"
source: hosted
version: "2.0.1"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
url: "https://pub.dev"
source: hosted
version: "2.0.1"
logging:
dependency: transitive
description:
name: logging
sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
matcher:
dependency: transitive
description:
name: matcher
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev"
source: hosted
version: "0.12.16+1"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
url: "https://pub.dev"
source: hosted
version: "0.8.0"
meta:
dependency: transitive
description:
name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
url: "https://pub.dev"
source: hosted
version: "1.11.0"
mime:
dependency: transitive
description:
name: mime
sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
url: "https://pub.dev"
source: hosted
version: "1.0.4"
mobx:
dependency: "direct main"
description:
name: mobx
sha256: f1862bd92c6a903fab67338f27e2f731117c3cb9ea37cee1a487f9e4e0de314a
url: "https://pub.dev"
source: hosted
version: "2.1.3+1"
mobx_codegen:
dependency: "direct dev"
description:
name: mobx_codegen
sha256: "86122e410d8ea24dda0c69adb5c2a6ccadd5ce02ad46e144764e0d0184a06181"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
package_config:
dependency: transitive
description:
name: package_config
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
path:
dependency: transitive
description:
name: path
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev"
source: hosted
version: "1.9.0"
path_provider:
dependency: "direct main"
description:
name: path_provider
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
url: "https://pub.dev"
source: hosted
version: "2.1.1"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
url: "https://pub.dev"
source: hosted
version: "2.2.1"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
url: "https://pub.dev"
source: hosted
version: "2.3.1"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
source: hosted
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
url: "https://pub.dev"
source: hosted
version: "5.4.0"
platform:
dependency: transitive
description:
name: platform
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a
url: "https://pub.dev"
source: hosted
version: "2.1.3"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346
url: "https://pub.dev"
source: hosted
version: "3.6.2"
pool:
dependency: transitive
description:
name: pool
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
url: "https://pub.dev"
source: hosted
version: "1.5.1"
process:
dependency: transitive
description:
name: process
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
url: "https://pub.dev"
source: hosted
version: "4.2.4"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17"
url: "https://pub.dev"
source: hosted
version: "2.1.3"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: "75f6614d6dde2dc68948dffbaa4fe5dae32cd700eb9fb763fe11dfb45a3c4d0a"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
puppeteer:
dependency: transitive
description:
name: puppeteer
sha256: "59e723cc5b69537159a7c34efd645dc08a6a1ac4647d7d7823606802c0f93cdb"
url: "https://pub.dev"
source: hosted
version: "3.2.0"
rxdart:
dependency: "direct main"
description:
name: rxdart
sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb"
url: "https://pub.dev"
source: hosted
version: "0.27.7"
shelf:
dependency: transitive
description:
name: shelf
sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c
url: "https://pub.dev"
source: hosted
version: "1.4.0"
shelf_static:
dependency: transitive
description:
name: shelf_static
sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e
url: "https://pub.dev"
source: hosted
version: "1.1.2"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8
url: "https://pub.dev"
source: hosted
version: "1.0.3"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
socks5_proxy:
dependency: transitive
description:
name: socks5_proxy
sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
url: "https://pub.dev"
source: hosted
version: "1.2.6"
source_helper:
dependency: transitive
description:
name: source_helper
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
url: "https://pub.dev"
source: hosted
version: "1.3.3"
source_span:
dependency: transitive
description:
name: source_span
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
url: "https://pub.dev"
source: hosted
version: "1.10.0"
sprintf:
dependency: transitive
description:
name: sprintf
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev"
source: hosted
version: "1.11.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev"
source: hosted
version: "2.1.2"
stream_transform:
dependency: transitive
description:
name: stream_transform
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
url: "https://pub.dev"
source: hosted
version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
url: "https://pub.dev"
source: hosted
version: "0.6.1"
timing:
dependency: transitive
description:
name: timing
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
tuple:
dependency: transitive
description:
name: tuple
sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
url: "https://pub.dev"
source: hosted
version: "2.0.2"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
unorm_dart:
dependency: "direct main"
description:
name: unorm_dart
sha256: "5b35bff83fce4d76467641438f9e867dc9bcfdb8c1694854f230579d68cd8f4b"
url: "https://pub.dev"
source: hosted
version: "0.2.0"
uuid:
dependency: transitive
description:
name: uuid
sha256: "22c94e5ad1e75f9934b766b53c742572ee2677c56bc871d850a57dad0f82127f"
url: "https://pub.dev"
source: hosted
version: "4.2.2"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
url: "https://pub.dev"
source: hosted
version: "13.0.0"
watcher:
dependency: transitive
description:
name: watcher
sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b
url: "https://pub.dev"
source: hosted
version: "2.3.0"
win32:
dependency: transitive
description:
name: win32
sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46
url: "https://pub.dev"
source: hosted
version: "3.1.3"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86
url: "https://pub.dev"
source: hosted
version: "0.2.0+3"
yaml:
dependency: transitive
description:
name: yaml
sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
sdks:
dart: ">=3.3.0 <4.0.0"
flutter: ">=3.19.0"

88
cw_lightning/pubspec.yaml Normal file
View file

@ -0,0 +1,88 @@
name: cw_lightning
description: A new Flutter package project.
version: 0.0.1
publish_to: none
author: Cake Wallet
homepage: https://cakewallet.com
environment:
sdk: ">=2.17.5 <3.0.0"
flutter: ">=1.20.0"
dependencies:
flutter:
sdk: flutter
path_provider: ^2.0.11
http: ^1.1.0
mobx: ^2.0.7+4
flutter_mobx: ^2.0.6+1
intl: ^0.18.0
cw_core:
path: ../cw_core
cw_bitcoin:
path: ../cw_bitcoin
bitcoin_flutter:
git:
url: https://github.com/cake-tech/bitcoin_flutter.git
ref: cake-update-v4
bitbox:
git:
url: https://github.com/cake-tech/bitbox-flutter.git
ref: Add-Support-For-OP-Return-data
breez_sdk:
git:
url: https://github.com/breez/breez-sdk-flutter.git
ref: v0.4.3-rc1
cryptography: ^2.0.5
bitcoin_base:
git:
url: https://github.com/cake-tech/bitcoin_base
ref: cake-update-v3
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.1.11
build_resolvers: ^2.0.9
mobx_codegen: ^2.0.7
hive_generator: ^2.0.1
dependency_overrides:
watcher: ^1.1.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# To add assets to your package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/assets-and-images/#from-packages
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# To add custom fonts to your package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/custom-fonts/#from-packages

View file

@ -0,0 +1,12 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:cw_lightning/cw_lightning.dart';
void main() {
test('adds one to input values', () {
final calculator = Calculator();
expect(calculator.addOne(2), 3);
expect(calculator.addOne(-7), -6);
expect(calculator.addOne(0), 1);
});
}

View file

@ -161,7 +161,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
Future<void>? updateBalance() => null; Future<void>? updateBalance() => null;
@override @override
void close() async { void close({bool? switchingToSameWalletType}) {
_listener?.stop(); _listener?.stop();
_onAccountChangeReaction?.reaction.dispose(); _onAccountChangeReaction?.reaction.dispose();
_autoSaveTimer?.cancel(); _autoSaveTimer?.cancel();

View file

@ -34,7 +34,7 @@ dev_dependencies:
build_runner: ^2.4.7 build_runner: ^2.4.7
build_resolvers: ^2.0.9 build_resolvers: ^2.0.9
mobx_codegen: ^2.0.7 mobx_codegen: ^2.0.7
hive_generator: ^1.1.3 hive_generator: ^2.0.1
dependency_overrides: dependency_overrides:
watcher: ^1.1.0 watcher: ^1.1.0

View file

@ -138,7 +138,7 @@ abstract class NanoWalletBase
} }
@override @override
void close() { void close({bool? switchingToSameWalletType}) {
_client.stop(); _client.stop();
_receiveTimer?.cancel(); _receiveTimer?.cancel();
} }

View file

@ -34,7 +34,7 @@ dev_dependencies:
sdk: flutter sdk: flutter
build_runner: ^2.4.7 build_runner: ^2.4.7
mobx_codegen: ^2.0.7 mobx_codegen: ^2.0.7
hive_generator: ^1.1.3 hive_generator: ^2.0.1
dependency_overrides: dependency_overrides:
watcher: ^1.1.0 watcher: ^1.1.0

View file

@ -165,7 +165,7 @@ abstract class SolanaWalletBase
Future<void> changePassword(String password) => throw UnimplementedError("changePassword"); Future<void> changePassword(String password) => throw UnimplementedError("changePassword");
@override @override
void close() { void close({bool? switchingToSameWalletType}) {
_client.stop(); _client.stop();
_transactionsUpdateTimer?.cancel(); _transactionsUpdateTimer?.cancel();
} }

View file

@ -186,7 +186,7 @@ abstract class TronWalletBase
} }
@override @override
void close() { void close({bool? switchingToSameWalletType}) {
_transactionsUpdateTimer?.cancel(); _transactionsUpdateTimer?.cancel();
} }

View file

@ -60,6 +60,26 @@
<string>bitcoin-wallet</string> <string>bitcoin-wallet</string>
</array> </array>
</dict> </dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>lightning</string>
<key>CFBundleURLSchemes</key>
<array>
<string>lightning</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>lightning-wallet</string>
<key>CFBundleURLSchemes</key>
<array>
<string>lightning-wallet</string>
</array>
</dict>
<dict> <dict>
<key>CFBundleTypeRole</key> <key>CFBundleTypeRole</key>
<string>Editor</string> <string>Editor</string>

View file

@ -36,4 +36,10 @@ class CWBitcoinCash extends BitcoinCash {
@override @override
TransactionPriority getBitcoinCashTransactionPrioritySlow() => TransactionPriority getBitcoinCashTransactionPrioritySlow() =>
BitcoinCashTransactionPriority.slow; BitcoinCashTransactionPriority.slow;
@override
String getMnemonic(int? strength) => throw UnimplementedError();
@override
Uint8List getSeedFromMnemonic(String seed) => throw UnimplementedError();
} }

View file

@ -279,7 +279,8 @@ class AddressValidator extends TextValidator {
'|([^0-9a-zA-Z]|^)((bc|tb)1q[ac-hj-np-z02-9]{40,80})([^0-9a-zA-Z]|\$)' //P2wshAddress type '|([^0-9a-zA-Z]|^)((bc|tb)1q[ac-hj-np-z02-9]{40,80})([^0-9a-zA-Z]|\$)' //P2wshAddress type
'|([^0-9a-zA-Z]|^)((bc|tb)1p([ac-hj-np-z02-9]{39}|[ac-hj-np-z02-9]{59}|[ac-hj-np-z02-9]{8,89}))([^0-9a-zA-Z]|\$)' //P2trAddress type '|([^0-9a-zA-Z]|^)((bc|tb)1p([ac-hj-np-z02-9]{39}|[ac-hj-np-z02-9]{59}|[ac-hj-np-z02-9]{8,89}))([^0-9a-zA-Z]|\$)' //P2trAddress type
'|${SilentPaymentAddress.regex.pattern}\$'; '|${SilentPaymentAddress.regex.pattern}\$';
case CryptoCurrency.btcln:
return '(lnbc|LNBC)([0-9]{1,}[a-zA-Z0-9]+)([^0-9a-zA-Z]|\$)';
case CryptoCurrency.ltc: case CryptoCurrency.ltc:
return '([^0-9a-zA-Z]|^)^L[a-zA-Z0-9]{26,33}([^0-9a-zA-Z]|\$)' return '([^0-9a-zA-Z]|^)^L[a-zA-Z0-9]{26,33}([^0-9a-zA-Z]|\$)'
'|([^0-9a-zA-Z]|^)[LM][a-km-zA-HJ-NP-Z1-9]{26,33}([^0-9a-zA-Z]|\$)' '|([^0-9a-zA-Z]|^)[LM][a-km-zA-HJ-NP-Z1-9]{26,33}([^0-9a-zA-Z]|\$)'

View file

@ -24,7 +24,7 @@ class SeedValidator extends Validator<MnemonicItem> {
static List<String> getWordList({required WalletType type, required String language}) { static List<String> getWordList({required WalletType type, required String language}) {
switch (type) { switch (type) {
case WalletType.bitcoin: case WalletType.bitcoin:
return getBitcoinWordList(language); case WalletType.lightning:
case WalletType.litecoin: case WalletType.litecoin:
return getBitcoinWordList(language); return getBitcoinWordList(language);
case WalletType.monero: case WalletType.monero:

View file

@ -79,6 +79,7 @@ class WalletCreationService {
case WalletType.polygon: case WalletType.polygon:
case WalletType.solana: case WalletType.solana:
case WalletType.tron: case WalletType.tron:
case WalletType.lightning:
return true; return true;
case WalletType.monero: case WalletType.monero:
case WalletType.wownero: case WalletType.wownero:

View file

@ -1,3 +1,4 @@
import 'package:breez_sdk/bridge_generated.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/anonpay/anonpay_api.dart'; import 'package:cake_wallet/anonpay/anonpay_api.dart';
import 'package:cake_wallet/anonpay/anonpay_info_base.dart'; import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
@ -28,16 +29,24 @@ import 'package:cake_wallet/entities/contact.dart';
import 'package:cake_wallet/entities/contact_record.dart'; import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.dart'; import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/src/screens/cake_pay/cake_pay.dart';
import 'package:cake_wallet/view_model/lightning_send_view_model.dart';
import 'package:cake_wallet/view_model/link_view_model.dart';
import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
import 'package:cw_core/nano_account.dart';
import 'package:cw_core/receive_page_option.dart';
import 'package:cake_wallet/entities/qr_view_data.dart'; import 'package:cake_wallet/entities/qr_view_data.dart';
import 'package:cake_wallet/entities/template.dart'; import 'package:cake_wallet/entities/template.dart';
import 'package:cake_wallet/entities/transaction_description.dart'; import 'package:cake_wallet/entities/transaction_description.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/lightning/lightning.dart';
import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/cake_pay/cake_pay_card.dart'; import 'package:cake_wallet/cake_pay/cake_pay_card.dart';
import 'package:cake_wallet/exchange/exchange_template.dart'; import 'package:cake_wallet/exchange/exchange_template.dart';
import 'package:cake_wallet/exchange/trade.dart'; import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/haven/haven.dart'; import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/reactions/on_authentication_state_change.dart'; import 'package:cake_wallet/reactions/on_authentication_state_change.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
@ -77,12 +86,16 @@ import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart'; import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart'; import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart'; import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
import 'package:cake_wallet/src/screens/receive/lightning_invoice_page.dart';
import 'package:cake_wallet/src/screens/receive/lightning_receive_page.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_choose_derivation.dart';
import 'package:cake_wallet/src/screens/send/lightning_send_confirm_page.dart';
import 'package:cake_wallet/src/screens/send/lightning_send_page.dart';
import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart'; import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart';
import 'package:cake_wallet/src/screens/receive/receive_page.dart'; import 'package:cake_wallet/src/screens/receive/receive_page.dart';
import 'package:cake_wallet/src/screens/rescan/rescan_page.dart'; import 'package:cake_wallet/src/screens/rescan/rescan_page.dart';
import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart'; import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
import 'package:cake_wallet/src/screens/restore/restore_options_page.dart'; import 'package:cake_wallet/src/screens/restore/restore_options_page.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_choose_derivation.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart'; import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart'; import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart'; import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart';
@ -122,13 +135,14 @@ import 'package:cake_wallet/view_model/anonpay_details_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
import 'package:cake_wallet/view_model/lightning_invoice_page_view_model.dart';
import 'package:cake_wallet/view_model/lightning_view_model.dart';
import 'package:cake_wallet/view_model/cake_pay/cake_pay_auth_view_model.dart'; import 'package:cake_wallet/view_model/cake_pay/cake_pay_auth_view_model.dart';
import 'package:cake_wallet/view_model/cake_pay/cake_pay_buy_card_view_model.dart'; import 'package:cake_wallet/view_model/cake_pay/cake_pay_buy_card_view_model.dart';
import 'package:cake_wallet/cake_pay/cake_pay_service.dart'; import 'package:cake_wallet/cake_pay/cake_pay_service.dart';
import 'package:cake_wallet/cake_pay/cake_pay_api.dart'; import 'package:cake_wallet/cake_pay/cake_pay_api.dart';
import 'package:cake_wallet/cake_pay/cake_pay_vendor.dart'; import 'package:cake_wallet/cake_pay/cake_pay_vendor.dart';
import 'package:cake_wallet/src/screens/cake_pay/auth/cake_pay_account_page.dart'; import 'package:cake_wallet/src/screens/cake_pay/auth/cake_pay_account_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/cake_pay.dart';
import 'package:cake_wallet/view_model/cake_pay/cake_pay_account_view_model.dart'; import 'package:cake_wallet/view_model/cake_pay/cake_pay_account_view_model.dart';
import 'package:cake_wallet/view_model/cake_pay/cake_pay_cards_list_view_model.dart'; import 'package:cake_wallet/view_model/cake_pay/cake_pay_cards_list_view_model.dart';
import 'package:cake_wallet/view_model/cake_pay/cake_pay_purchase_view_model.dart'; import 'package:cake_wallet/view_model/cake_pay/cake_pay_purchase_view_model.dart';
@ -136,6 +150,7 @@ import 'package:cake_wallet/view_model/nano_account_list/nano_account_edit_or_cr
import 'package:cake_wallet/view_model/nano_account_list/nano_account_list_view_model.dart'; import 'package:cake_wallet/view_model/nano_account_list/nano_account_list_view_model.dart';
import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart'; import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart';
import 'package:cake_wallet/view_model/seed_type_view_model.dart'; import 'package:cake_wallet/view_model/seed_type_view_model.dart';
import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart'; import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart'; import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart'; import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
@ -148,13 +163,12 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_i
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:cake_wallet/view_model/wallet_restore_choose_derivation_view_model.dart'; import 'package:cake_wallet/view_model/wallet_restore_choose_derivation_view_model.dart';
import 'package:cw_core/nano_account.dart';
import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/node.dart'; import 'package:cw_core/node.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart'; import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart';
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart'; import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart'; import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart'; import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
@ -174,7 +188,6 @@ import 'package:cake_wallet/store/templates/exchange_template_store.dart';
import 'package:cake_wallet/store/templates/send_template_store.dart'; import 'package:cake_wallet/store/templates/send_template_store.dart';
import 'package:cake_wallet/store/wallet_list_store.dart'; import 'package:cake_wallet/store/wallet_list_store.dart';
import 'package:cake_wallet/store/yat/yat_store.dart'; import 'package:cake_wallet/store/yat/yat_store.dart';
import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/view_model/auth_view_model.dart'; import 'package:cake_wallet/view_model/auth_view_model.dart';
import 'package:cake_wallet/view_model/backup_view_model.dart'; import 'package:cake_wallet/view_model/backup_view_model.dart';
import 'package:cake_wallet/view_model/buy/buy_amount_view_model.dart'; import 'package:cake_wallet/view_model/buy/buy_amount_view_model.dart';
@ -188,7 +201,6 @@ import 'package:cake_wallet/view_model/edit_backup_password_view_model.dart';
import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart'; import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart';
import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart'; import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart'; import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cake_wallet/view_model/link_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart'; import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart'; import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart'; import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart';
@ -217,8 +229,6 @@ import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
import 'package:cake_wallet/view_model/wallet_seed_view_model.dart'; import 'package:cake_wallet/view_model/wallet_seed_view_model.dart';
import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/receive_page_option.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@ -376,17 +386,19 @@ Future<void> setup({
fiatConvertationStore: getIt.get<FiatConversionStore>())); fiatConvertationStore: getIt.get<FiatConversionStore>()));
getIt.registerFactory(() => DashboardViewModel( getIt.registerFactory(() => DashboardViewModel(
balanceViewModel: getIt.get<BalanceViewModel>(), balanceViewModel: getIt.get<BalanceViewModel>(),
appStore: getIt.get<AppStore>(), appStore: getIt.get<AppStore>(),
tradesStore: getIt.get<TradesStore>(), tradesStore: getIt.get<TradesStore>(),
tradeFilterStore: getIt.get<TradeFilterStore>(), tradeFilterStore: getIt.get<TradeFilterStore>(),
transactionFilterStore: getIt.get<TransactionFilterStore>(), transactionFilterStore: getIt.get<TransactionFilterStore>(),
settingsStore: settingsStore, settingsStore: settingsStore,
yatStore: getIt.get<YatStore>(), yatStore: getIt.get<YatStore>(),
ordersStore: getIt.get<OrdersStore>(), ordersStore: getIt.get<OrdersStore>(),
anonpayTransactionsStore: getIt.get<AnonpayTransactionsStore>(), anonpayTransactionsStore: getIt.get<AnonpayTransactionsStore>(),
sharedPreferences: getIt.get<SharedPreferences>(), sharedPreferences: getIt.get<SharedPreferences>(),
keyService: getIt.get<KeyService>())); keyService: getIt.get<KeyService>(),
lightningViewModel: getIt.get<LightningViewModel>(),
));
getIt.registerFactory<AuthService>( getIt.registerFactory<AuthService>(
() => AuthService( () => AuthService(
@ -624,7 +636,6 @@ Future<void> setup({
authService: getIt.get<AuthService>(), authService: getIt.get<AuthService>(),
initialPaymentRequest: initialPaymentRequest, initialPaymentRequest: initialPaymentRequest,
)); ));
getIt.registerFactory( getIt.registerFactory(
() => SendTemplatePage(sendTemplateViewModel: getIt.get<SendTemplateViewModel>())); () => SendTemplatePage(sendTemplateViewModel: getIt.get<SendTemplateViewModel>()));
@ -905,6 +916,8 @@ Future<void> setup({
return nano!.createNanoWalletService(_walletInfoSource); return nano!.createNanoWalletService(_walletInfoSource);
case WalletType.polygon: case WalletType.polygon:
return polygon!.createPolygonWalletService(_walletInfoSource); return polygon!.createPolygonWalletService(_walletInfoSource);
case WalletType.lightning:
return lightning!.createLightningWalletService(_walletInfoSource, _unspentCoinsInfoSource);
case WalletType.solana: case WalletType.solana:
return solana!.createSolanaWalletService(_walletInfoSource); return solana!.createSolanaWalletService(_walletInfoSource);
case WalletType.tron: case WalletType.tron:
@ -1192,5 +1205,62 @@ Future<void> setup({
getIt.registerFactory(() => NFTViewModel(appStore, getIt.get<BottomSheetService>())); getIt.registerFactory(() => NFTViewModel(appStore, getIt.get<BottomSheetService>()));
getIt.registerFactory<TorPage>(() => TorPage(getIt.get<AppStore>())); getIt.registerFactory<TorPage>(() => TorPage(getIt.get<AppStore>()));
getIt.registerFactory<LightningViewModel>(
() => LightningViewModel(),
);
getIt.registerFactory<LightningSendViewModel>(
() => LightningSendViewModel(
settingsStore: getIt.get<SettingsStore>(),
fiatConversionStore: getIt.get<FiatConversionStore>(),
),
);
getIt.registerFactoryParam<LightningInvoicePageViewModel, void, void>((_, __) {
return LightningInvoicePageViewModel(
getIt.get<SettingsStore>(),
getIt.get<AppStore>().wallet!,
getIt.get<SharedPreferences>(),
getIt.get<LightningViewModel>(),
);
});
getIt.registerFactoryParam<LightningReceiveOnchainPage, void, void>((_, __) {
return LightningReceiveOnchainPage(
addressListViewModel: getIt.get<WalletAddressListViewModel>(),
lightningViewModel: getIt.get<LightningViewModel>(),
receiveOptionViewModel:
getIt.get<ReceiveOptionViewModel>(param1: lightning!.getOptionOnchain()),
);
});
getIt.registerFactoryParam<LightningInvoicePage, void, void>((_, __) {
return LightningInvoicePage(
lightningInvoicePageViewModel: getIt.get<LightningInvoicePageViewModel>(),
receiveOptionViewModel:
getIt.get<ReceiveOptionViewModel>(param1: lightning!.getOptionInvoice()),
);
});
getIt.registerFactory<LightningSendPage>(() {
return LightningSendPage(
output: Output(
getIt.get<AppStore>().wallet!,
getIt.get<SettingsStore>(),
getIt.get<FiatConversionStore>(),
() => CryptoCurrency.btcln,
),
authService: getIt.get<AuthService>(),
lightningSendViewModel: getIt.get<LightningSendViewModel>(),
);
});
getIt.registerFactoryParam<LightningSendConfirmPage, LNInvoice, void>((LNInvoice invoice, _) {
return LightningSendConfirmPage(
invoice: invoice,
lightningSendViewModel: getIt.get<LightningSendViewModel>(),
);
});
_isSetupFinished = true; _isSetupFinished = true;
} }

View file

@ -1,8 +1,10 @@
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/lightning/lightning.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class MainActions { class MainActions {
@ -11,8 +13,7 @@ class MainActions {
final bool Function(DashboardViewModel viewModel)? isEnabled; final bool Function(DashboardViewModel viewModel)? isEnabled;
final bool Function(DashboardViewModel viewModel)? canShow; final bool Function(DashboardViewModel viewModel)? canShow;
final Future<void> Function( final Future<void> Function(BuildContext context, DashboardViewModel viewModel) onTap;
BuildContext context, DashboardViewModel viewModel) onTap;
MainActions._({ MainActions._({
required this.name, required this.name,
@ -55,6 +56,10 @@ class MainActions {
name: (context) => S.of(context).receive, name: (context) => S.of(context).receive,
image: 'assets/images/received.png', image: 'assets/images/received.png',
onTap: (BuildContext context, DashboardViewModel viewModel) async { onTap: (BuildContext context, DashboardViewModel viewModel) async {
if (viewModel.wallet.type == WalletType.lightning) {
Navigator.pushNamed(context, Routes.lightningInvoice);
return;
}
Navigator.pushNamed(context, Routes.addressPage); Navigator.pushNamed(context, Routes.addressPage);
}, },
); );
@ -75,6 +80,10 @@ class MainActions {
name: (context) => S.of(context).send, name: (context) => S.of(context).send,
image: 'assets/images/upload.png', image: 'assets/images/upload.png',
onTap: (BuildContext context, DashboardViewModel viewModel) async { onTap: (BuildContext context, DashboardViewModel viewModel) async {
if (viewModel.wallet.type == WalletType.lightning) {
Navigator.pushNamed(context, Routes.lightningSend);
return;
}
Navigator.pushNamed(context, Routes.send); Navigator.pushNamed(context, Routes.send);
}, },
); );
@ -114,4 +123,4 @@ class MainActions {
}, },
); );
} }
} }

View file

@ -78,6 +78,7 @@ class ProvidersHelper {
ProviderType.moonpay, ProviderType.moonpay,
]; ];
case WalletType.none: case WalletType.none:
case WalletType.lightning:
case WalletType.haven: case WalletType.haven:
return []; return [];
} }
@ -113,6 +114,7 @@ class ProvidersHelper {
case WalletType.monero: case WalletType.monero:
case WalletType.nano: case WalletType.nano:
case WalletType.banano: case WalletType.banano:
case WalletType.lightning:
case WalletType.none: case WalletType.none:
case WalletType.haven: case WalletType.haven:
case WalletType.wownero: case WalletType.wownero:

View file

@ -11,6 +11,14 @@ import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
double lightningDoubleToBitcoinDouble({required double amount}) {
return amount / 100000000;
}
double bitcoinDoubleToLightningDouble({required double amount}) {
return amount * 100000000;
}
class TrocadorExchangeProvider extends ExchangeProvider { class TrocadorExchangeProvider extends ExchangeProvider {
TrocadorExchangeProvider({this.useTorOnly = false, this.providerStates = const {}}) TrocadorExchangeProvider({this.useTorOnly = false, this.providerStates = const {}})
: _lastUsedRateId = '', : _lastUsedRateId = '',
@ -106,6 +114,14 @@ class TrocadorExchangeProvider extends ExchangeProvider {
final coinJson = responseJSON.first as Map<String, dynamic>; final coinJson = responseJSON.first as Map<String, dynamic>;
// trocador treats btcln as just bitcoin amounts:
if (from == CryptoCurrency.btcln) {
return Limits(
min: bitcoinDoubleToLightningDouble(amount: (coinJson['minimum'] as double)),
max: bitcoinDoubleToLightningDouble(amount: (coinJson['maximum'] as double)),
);
}
return Limits( return Limits(
min: coinJson['minimum'] as double, min: coinJson['minimum'] as double,
max: coinJson['maximum'] as double, max: coinJson['maximum'] as double,
@ -122,14 +138,20 @@ class TrocadorExchangeProvider extends ExchangeProvider {
try { try {
if (amount == 0) return 0.0; if (amount == 0) return 0.0;
double amt = amount;
if (from == CryptoCurrency.btcln) {
amt = lightningDoubleToBitcoinDouble(amount: amount);
}
final params = <String, String>{ final params = <String, String>{
'api_key': apiKey, 'api_key': apiKey,
'ticker_from': _normalizeCurrency(from), 'ticker_from': _normalizeCurrency(from),
'ticker_to': _normalizeCurrency(to), 'ticker_to': _normalizeCurrency(to),
'network_from': _networkFor(from), 'network_from': _networkFor(from),
'network_to': _networkFor(to), 'network_to': _networkFor(to),
if (!isFixedRateMode) 'amount_from': amount.toString(), if (!isFixedRateMode) 'amount_from': amt.toString(),
if (isFixedRateMode) 'amount_to': amount.toString(), if (isFixedRateMode) 'amount_to': amt.toString(),
'payment': isFixedRateMode ? 'True' : 'False', 'payment': isFixedRateMode ? 'True' : 'False',
'min_kycrating': 'C', 'min_kycrating': 'C',
'markup': markup, 'markup': markup,
@ -160,6 +182,14 @@ class TrocadorExchangeProvider extends ExchangeProvider {
required bool isFixedRateMode, required bool isFixedRateMode,
required bool isSendAll, required bool isSendAll,
}) async { }) async {
double fromAmt = double.parse(request.fromAmount);
double toAmt = double.parse(request.toAmount);
if (request.fromCurrency == CryptoCurrency.btcln) {
fromAmt = lightningDoubleToBitcoinDouble(amount: fromAmt);
}
if (request.toCurrency == CryptoCurrency.btcln) {
toAmt = lightningDoubleToBitcoinDouble(amount: toAmt);
}
final params = { final params = {
'api_key': apiKey, 'api_key': apiKey,
'ticker_from': _normalizeCurrency(request.fromCurrency), 'ticker_from': _normalizeCurrency(request.fromCurrency),
@ -169,17 +199,22 @@ class TrocadorExchangeProvider extends ExchangeProvider {
'payment': isFixedRateMode ? 'True' : 'False', 'payment': isFixedRateMode ? 'True' : 'False',
'min_kycrating': 'C', 'min_kycrating': 'C',
'markup': markup, 'markup': markup,
if (!isFixedRateMode) 'amount_from': request.fromAmount, if (!isFixedRateMode) 'amount_from': fromAmt.toString(),
if (isFixedRateMode) 'amount_to': request.toAmount, if (isFixedRateMode) 'amount_to': toAmt.toString(),
'address': request.toAddress, 'address': request.toAddress,
'refund': request.refundAddress 'refund': request.refundAddress
}; };
double amt = double.tryParse(request.toAmount) ?? 0;
if (request.fromCurrency == CryptoCurrency.btcln) {
amt = lightningDoubleToBitcoinDouble(amount: amt);
}
if (isFixedRateMode) { if (isFixedRateMode) {
await fetchRate( await fetchRate(
from: request.fromCurrency, from: request.fromCurrency,
to: request.toCurrency, to: request.toCurrency,
amount: double.tryParse(request.toAmount) ?? 0, amount: amt,
isFixedRateMode: true, isFixedRateMode: true,
isReceiveAmount: true, isReceiveAmount: true,
); );
@ -225,6 +260,13 @@ class TrocadorExchangeProvider extends ExchangeProvider {
final providerId = responseJSON['id_provider'] as String; final providerId = responseJSON['id_provider'] as String;
final providerName = responseJSON['provider'] as String; final providerName = responseJSON['provider'] as String;
String? responseAmount = responseJSON['amount_from']?.toString();
if (request.fromCurrency == CryptoCurrency.btcln && responseAmount != null) {
responseAmount =
bitcoinDoubleToLightningDouble(amount: double.parse(responseAmount)).toString();
}
responseAmount ??= fromAmt.toString();
return Trade( return Trade(
id: id, id: id,
from: request.fromCurrency, from: request.fromCurrency,
@ -237,7 +279,7 @@ class TrocadorExchangeProvider extends ExchangeProvider {
providerId: providerId, providerId: providerId,
providerName: providerName, providerName: providerName,
createdAt: DateTime.tryParse(date)?.toLocal(), createdAt: DateTime.tryParse(date)?.toLocal(),
amount: responseJSON['amount_from']?.toString() ?? request.fromAmount, amount: responseAmount,
payoutAddress: payoutAddress, payoutAddress: payoutAddress,
isSendAll: isSendAll); isSendAll: isSendAll);
} }
@ -290,6 +332,8 @@ class TrocadorExchangeProvider extends ExchangeProvider {
return 'MATIC'; return 'MATIC';
case CryptoCurrency.zec: case CryptoCurrency.zec:
return 'Mainnet'; return 'Mainnet';
case CryptoCurrency.btcln:
return 'Lightning';
default: default:
return currency.tag != null ? _normalizeTag(currency.tag!) : 'Mainnet'; return currency.tag != null ? _normalizeTag(currency.tag!) : 'Mainnet';
} }
@ -301,6 +345,8 @@ class TrocadorExchangeProvider extends ExchangeProvider {
return 'zec'; return 'zec';
case CryptoCurrency.usdcEPoly: case CryptoCurrency.usdcEPoly:
return 'usdce'; return 'usdce';
case CryptoCurrency.btcln:
return 'btc';
default: default:
return currency.title.toLowerCase(); return currency.title.toLowerCase();
} }

View file

@ -0,0 +1,69 @@
part of 'lightning.dart';
class CWLightning extends Lightning {
@override
String formatterLightningAmountToString({required int amount}) =>
bitcoinAmountToString(amount: amount * 100000000);
@override
double formatterLightningAmountToDouble({required int amount}) =>
bitcoinAmountToDouble(amount: amount * 100000000);
@override
int formatterStringDoubleToLightningAmount(String amount) =>
stringDoubleToBitcoinAmount(amount * 100000000);
WalletService createLightningWalletService(
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) {
return LightningWalletService(walletInfoSource, unspentCoinSource);
}
@override
List<LightningReceivePageOption> getLightningReceivePageOptions() =>
LightningReceivePageOption.all;
@override
ReceivePageOption getOptionInvoice() => LightningReceivePageOption.lightningInvoice;
@override
ReceivePageOption getOptionOnchain() => LightningReceivePageOption.lightningOnchain;
@override
String satsToLightningString(int sats) {
const bitcoinAmountLength = 8;
const bitcoinAmountDivider = 100000000;
const lightningAmountDivider = 1;
final bitcoinAmountFormat = NumberFormat()
..maximumFractionDigits = bitcoinAmountLength
..minimumFractionDigits = 1;
String formattedAmount = bitcoinAmountFormat.format(sats);
return formattedAmount.substring(0, formattedAmount.length - 2);
}
@override
String bitcoinAmountToLightningString({required int amount}) {
final bitcoinAmountFormat = NumberFormat()
..maximumFractionDigits = bitcoinAmountLength
..minimumFractionDigits = 1;
String formattedAmount =
bitcoinAmountFormat.format(cryptoAmountToDouble(amount: amount, divider: 1));
return formattedAmount.substring(0, formattedAmount.length - 2);
}
@override
int bitcoinAmountToLightningAmount({required int amount}) {
return amount * 100000000;
}
@override
double bitcoinDoubleToLightningDouble({required double amount}) {
return amount * 100000000;
}
@override
double lightningDoubleToBitcoinDouble({required double amount}) {
return amount / 100000000;
}
}

View file

@ -30,46 +30,49 @@ Future<void> startFiatRateUpdate(
if (appStore.wallet!.type == WalletType.haven) { if (appStore.wallet!.type == WalletType.haven) {
await updateHavenRate(fiatConversionStore); await updateHavenRate(fiatConversionStore);
} else { return;
fiatConversionStore.prices[appStore.wallet!.currency] =
await FiatConversionService.fetchPrice(
crypto: appStore.wallet!.currency,
fiat: settingsStore.fiatCurrency,
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly);
} }
Iterable<CryptoCurrency>? currencies; Iterable<CryptoCurrency>? currencies = [];
if (appStore.wallet!.type == WalletType.ethereum) { switch (appStore.wallet!.type) {
currencies = case WalletType.ethereum:
ethereum!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled); currencies =
ethereum!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
break;
case WalletType.polygon:
currencies =
polygon!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled);
break;
case WalletType.solana:
currencies =
solana!.getSPLTokenCurrencies(appStore.wallet!).where((element) => element.enabled);
break;
case WalletType.tron:
currencies =
tron!.getTronTokenCurrencies(appStore.wallet!).where((element) => element.enabled);
break;
case WalletType.lightning:
currencies = [CryptoCurrency.btc];
break;
default:
currencies = [appStore.wallet!.currency];
break;
} }
if (appStore.wallet!.type == WalletType.polygon) { for (final currency in currencies) {
currencies = () async {
polygon!.getERC20Currencies(appStore.wallet!).where((element) => element.enabled); fiatConversionStore.prices[currency] = await FiatConversionService.fetchPrice(
crypto: currency,
fiat: settingsStore.fiatCurrency,
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly,
);
}.call();
} }
if (appStore.wallet!.type == WalletType.solana) { // keep btcln price in sync with btc (since the fiat api only returns btc and not btcln)
currencies = // (btcln price is just the btc price divided by 100000000)
solana!.getSPLTokenCurrencies(appStore.wallet!).where((element) => element.enabled); fiatConversionStore.prices[CryptoCurrency.btcln] =
} (fiatConversionStore.prices[CryptoCurrency.btc] ?? 0) / 100000000;
if (appStore.wallet!.type == WalletType.tron) {
currencies =
tron!.getTronTokenCurrencies(appStore.wallet!).where((element) => element.enabled);
}
if (currencies != null) {
for (final currency in currencies) {
() async {
fiatConversionStore.prices[currency] = await FiatConversionService.fetchPrice(
crypto: currency,
fiat: settingsStore.fiatCurrency,
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly);
}.call();
}
}
} catch (e) { } catch (e) {
print(e); print(e);
} }

View file

@ -73,7 +73,8 @@ void startCurrentWalletChangeReaction(
if (wallet.type == WalletType.monero || if (wallet.type == WalletType.monero ||
wallet.type == WalletType.bitcoin || wallet.type == WalletType.bitcoin ||
wallet.type == WalletType.litecoin || wallet.type == WalletType.litecoin ||
wallet.type == WalletType.bitcoinCash) { wallet.type == WalletType.bitcoinCash ||
wallet.type == WalletType.lightning) {
_setAutoGenerateSubaddressStatus(wallet, settingsStore); _setAutoGenerateSubaddressStatus(wallet, settingsStore);
} }

View file

@ -42,6 +42,9 @@ import 'package:cake_wallet/src/screens/new_wallet/new_wallet_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart'; import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart'; import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart'; import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart';
import 'package:cake_wallet/src/screens/receive/lightning_invoice_page.dart';
import 'package:cake_wallet/src/screens/receive/lightning_receive_page.dart';
import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart';
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart'; import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart'; import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart'; import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
@ -53,6 +56,8 @@ import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
import 'package:cake_wallet/src/screens/restore/restore_options_page.dart'; import 'package:cake_wallet/src/screens/restore/restore_options_page.dart';
import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart'; import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_choose_derivation.dart'; import 'package:cake_wallet/src/screens/restore/wallet_restore_choose_derivation.dart';
import 'package:cake_wallet/src/screens/send/lightning_send_confirm_page.dart';
import 'package:cake_wallet/src/screens/send/lightning_send_page.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart'; import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart'; import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart'; import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart';
@ -651,6 +656,25 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.torPage: case Routes.torPage:
return MaterialPageRoute<void>(builder: (_) => getIt.get<TorPage>()); return MaterialPageRoute<void>(builder: (_) => getIt.get<TorPage>());
case Routes.lightningSend:
return CupertinoPageRoute<void>(
fullscreenDialog: true, builder: (_) => getIt.get<LightningSendPage>());
case Routes.lightningSendConfirm:
return CupertinoPageRoute<void>(
fullscreenDialog: true,
builder: (_) => getIt.get<LightningSendConfirmPage>(param1: settings.arguments));
case Routes.lightningReceiveOnchain:
final args = settings.arguments as List;
return CupertinoPageRoute<void>(
fullscreenDialog: true,
builder: (_) => getIt.get<LightningReceiveOnchainPage>(param1: args));
case Routes.lightningInvoice:
return CupertinoPageRoute<void>(
fullscreenDialog: true, builder: (_) => getIt.get<LightningInvoicePage>());
case Routes.connectDevices: case Routes.connectDevices:
final params = settings.arguments as ConnectDevicePageParams; final params = settings.arguments as ConnectDevicePageParams;
return MaterialPageRoute<void>( return MaterialPageRoute<void>(

View file

@ -101,5 +101,10 @@ class Routes {
static const nftDetailsPage = '/nft_details_page'; static const nftDetailsPage = '/nft_details_page';
static const importNFTPage = '/import_nft_page'; static const importNFTPage = '/import_nft_page';
static const torPage = '/tor_page'; static const torPage = '/tor_page';
static const lightningSend = '/lightning_send';
static const lightningSendConfirm = '/lightning_send_confirm';
static const lightningInvoice = '/lightning_invoice';
static const lightningReceiveOnchain = '/lightning_receive_onchain';
static const lightningSettings = '/lightning_settings';
static const connectDevices = '/device/connect'; static const connectDevices = '/device/connect';
} }

View file

@ -30,6 +30,7 @@ class DesktopWalletSelectionDropDown extends StatefulWidget {
class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionDropDown> { class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionDropDown> {
final moneroIcon = Image.asset('assets/images/monero_logo.png', height: 24, width: 24); final moneroIcon = Image.asset('assets/images/monero_logo.png', height: 24, width: 24);
final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24); final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
final lightningIcon = Image.asset('assets/images/lightning_logo.png', height: 24, width: 24);
final tBitcoinIcon = Image.asset('assets/images/tbtc.png', height: 24, width: 24); final tBitcoinIcon = Image.asset('assets/images/tbtc.png', height: 24, width: 24);
final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24); final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24);
final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24); final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
@ -150,6 +151,8 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
return tBitcoinIcon; return tBitcoinIcon;
} }
return bitcoinIcon; return bitcoinIcon;
case WalletType.lightning:
return lightningIcon;
case WalletType.monero: case WalletType.monero:
return moneroIcon; return moneroIcon;
case WalletType.litecoin: case WalletType.litecoin:

View file

@ -186,6 +186,20 @@ class CryptoBalanceWidget extends StatelessWidget {
), ),
], ],
)), )),
Observer(builder: (_) {
final serviceMessage = dashboardViewModel.serviceMessage;
if (serviceMessage.isEmpty) {
return const SizedBox();
}
return Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
child: DashBoardRoundedCardWidget(
onTap: () => null,
title: S.of(context).warning,
subTitle: serviceMessage,
),
);
}),
Observer( Observer(
builder: (_) { builder: (_) {
if (dashboardViewModel.balanceViewModel.isShowCard && if (dashboardViewModel.balanceViewModel.isShowCard &&

View file

@ -28,6 +28,7 @@ class MenuWidgetState extends State<MenuWidget> {
this.fromBottomEdge = 25, this.fromBottomEdge = 25,
this.moneroIcon = Image.asset('assets/images/monero_menu.png'), this.moneroIcon = Image.asset('assets/images/monero_menu.png'),
this.bitcoinIcon = Image.asset('assets/images/bitcoin_menu.png'), this.bitcoinIcon = Image.asset('assets/images/bitcoin_menu.png'),
this.lightningIcon = Image.asset('assets/images/lightning_logo.png'),
this.litecoinIcon = Image.asset('assets/images/litecoin_menu.png'), this.litecoinIcon = Image.asset('assets/images/litecoin_menu.png'),
this.havenIcon = Image.asset('assets/images/haven_menu.png'), this.havenIcon = Image.asset('assets/images/haven_menu.png'),
this.ethereumIcon = Image.asset('assets/images/eth_icon.png'), this.ethereumIcon = Image.asset('assets/images/eth_icon.png'),
@ -52,6 +53,7 @@ class MenuWidgetState extends State<MenuWidget> {
Image moneroIcon; Image moneroIcon;
Image bitcoinIcon; Image bitcoinIcon;
Image lightningIcon;
Image litecoinIcon; Image litecoinIcon;
Image havenIcon; Image havenIcon;
Image ethereumIcon; Image ethereumIcon;
@ -103,6 +105,7 @@ class MenuWidgetState extends State<MenuWidget> {
color: Theme.of(context).extension<CakeMenuTheme>()!.iconColor); color: Theme.of(context).extension<CakeMenuTheme>()!.iconColor);
bitcoinIcon = Image.asset('assets/images/bitcoin_menu.png', bitcoinIcon = Image.asset('assets/images/bitcoin_menu.png',
color: Theme.of(context).extension<CakeMenuTheme>()!.iconColor); color: Theme.of(context).extension<CakeMenuTheme>()!.iconColor);
lightningIcon = Image.asset('assets/images/lightning_logo.png');
return Row( return Row(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
@ -220,6 +223,8 @@ class MenuWidgetState extends State<MenuWidget> {
return moneroIcon; return moneroIcon;
case WalletType.bitcoin: case WalletType.bitcoin:
return bitcoinIcon; return bitcoinIcon;
case WalletType.lightning:
return lightningIcon;
case WalletType.litecoin: case WalletType.litecoin:
return litecoinIcon; return litecoinIcon;
case WalletType.haven: case WalletType.haven:

View file

@ -638,6 +638,7 @@ class ExchangePage extends BasePage {
initialIsAddressEditable: exchangeViewModel.isDepositAddressEnabled, initialIsAddressEditable: exchangeViewModel.isDepositAddressEnabled,
isAmountEstimated: false, isAmountEstimated: false,
hasRefundAddress: true, hasRefundAddress: true,
hasAddress: exchangeViewModel.hasAddress,
isMoneroWallet: exchangeViewModel.isMoneroWallet, isMoneroWallet: exchangeViewModel.isMoneroWallet,
currencies: exchangeViewModel.depositCurrencies, currencies: exchangeViewModel.depositCurrencies,
onCurrencySelected: (currency) { onCurrencySelected: (currency) {

View file

@ -33,6 +33,7 @@ class ExchangeCard extends StatefulWidget {
this.title = '', this.title = '',
this.initialIsAddressEditable = true, this.initialIsAddressEditable = true,
this.hasRefundAddress = false, this.hasRefundAddress = false,
this.hasAddress = true,
this.isMoneroWallet = false, this.isMoneroWallet = false,
this.currencyButtonColor = Colors.transparent, this.currencyButtonColor = Colors.transparent,
this.addressButtonsColor = Colors.transparent, this.addressButtonsColor = Colors.transparent,
@ -57,6 +58,7 @@ class ExchangeCard extends StatefulWidget {
final bool initialIsAddressEditable; final bool initialIsAddressEditable;
final bool isAmountEstimated; final bool isAmountEstimated;
final bool hasRefundAddress; final bool hasRefundAddress;
final bool hasAddress;
final bool isMoneroWallet; final bool isMoneroWallet;
final Image imageArrow; final Image imageArrow;
final Color currencyButtonColor; final Color currencyButtonColor;
@ -272,9 +274,7 @@ class ExchangeCardState extends State<ExchangeCard> {
color: Theme.of(context) color: Theme.of(context)
.extension<ExchangePageTheme>()! .extension<ExchangePageTheme>()!
.hintTextColor), .hintTextColor),
validator: _isAmountEditable validator: _isAmountEditable ? widget.currencyValueValidator : null),
? widget.currencyValueValidator
: null),
), ),
), ),
if (widget.hasAllAmount) if (widget.hasAllAmount)
@ -330,138 +330,142 @@ class ExchangeCardState extends State<ExchangeCard> {
: Offstage(), : Offstage(),
])), ])),
), ),
!_isAddressEditable && widget.hasRefundAddress if (widget.hasAddress) ...[
? Padding( !_isAddressEditable && widget.hasRefundAddress
padding: EdgeInsets.only(top: 20), ? Padding(
child: Text(
S.of(context).refund_address,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor),
))
: Offstage(),
_isAddressEditable
? FocusTraversalOrder(
order: NumericFocusOrder(2),
child: Padding(
padding: EdgeInsets.only(top: 20), padding: EdgeInsets.only(top: 20),
child: AddressTextField( child: Text(
focusNode: widget.addressFocusNode, S.of(context).refund_address,
controller: addressController, style: TextStyle(
onURIScanned: (uri) { fontSize: 14,
final paymentRequest = PaymentRequest.fromUri(uri); fontWeight: FontWeight.w500,
addressController.text = paymentRequest.address; color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor),
))
: Offstage(),
_isAddressEditable
? FocusTraversalOrder(
order: NumericFocusOrder(2),
child: Padding(
padding: EdgeInsets.only(top: 20),
child: AddressTextField(
focusNode: widget.addressFocusNode,
controller: addressController,
onURIScanned: (uri) {
final paymentRequest = PaymentRequest.fromUri(uri);
addressController.text = paymentRequest.address;
if (amountController.text.isNotEmpty) { if (amountController.text.isNotEmpty) {
_showAmountPopup(context, paymentRequest); _showAmountPopup(context, paymentRequest);
return; return;
} }
widget.amountFocusNode?.requestFocus(); widget.amountFocusNode?.requestFocus();
amountController.text = paymentRequest.amount; amountController.text = paymentRequest.amount;
}, },
placeholder: widget.hasRefundAddress ? S.of(context).refund_address : null, placeholder: widget.hasRefundAddress ? S.of(context).refund_address : null,
options: [ options: [
AddressTextFieldOption.paste, AddressTextFieldOption.paste,
AddressTextFieldOption.qrCode, AddressTextFieldOption.qrCode,
AddressTextFieldOption.addressBook, AddressTextFieldOption.addressBook,
], ],
isBorderExist: false, isBorderExist: false,
textStyle: textStyle: TextStyle(
TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white), fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white),
hintStyle: TextStyle( hintStyle: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor), color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor),
buttonColor: widget.addressButtonsColor, buttonColor: widget.addressButtonsColor,
validator: widget.addressTextFieldValidator, validator: widget.addressTextFieldValidator,
onPushPasteButton: widget.onPushPasteButton, onPushPasteButton: widget.onPushPasteButton,
onPushAddressBookButton: widget.onPushAddressBookButton, onPushAddressBookButton: widget.onPushAddressBookButton,
selectedCurrency: _selectedCurrency), selectedCurrency: _selectedCurrency),
), ),
) )
: Padding( : Padding(
padding: EdgeInsets.only(top: 10), padding: EdgeInsets.only(top: 10),
child: Builder( child: Builder(
builder: (context) => Stack(children: <Widget>[ builder: (context) => Stack(children: <Widget>[
FocusTraversalOrder( FocusTraversalOrder(
order: NumericFocusOrder(3), order: NumericFocusOrder(3),
child: BaseTextFormField( child: BaseTextFormField(
controller: addressController, controller: addressController,
borderColor: Colors.transparent, borderColor: Colors.transparent,
suffixIcon: SizedBox(width: _isMoneroWallet ? 80 : 36), suffixIcon: SizedBox(width: _isMoneroWallet ? 80 : 36),
textStyle: TextStyle( textStyle: TextStyle(
fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white), fontSize: 16,
validator: widget.addressTextFieldValidator), fontWeight: FontWeight.w600,
), color: Colors.white),
Positioned( validator: widget.addressTextFieldValidator),
top: 2, ),
right: 0, Positioned(
child: SizedBox( top: 2,
width: _isMoneroWallet ? 80 : 36, right: 0,
child: Row(children: <Widget>[ child: SizedBox(
if (_isMoneroWallet) width: _isMoneroWallet ? 80 : 36,
child: Row(children: <Widget>[
if (_isMoneroWallet)
Padding(
padding: EdgeInsets.only(left: 10),
child: Container(
width: 34,
height: 34,
padding: EdgeInsets.only(top: 0),
child: Semantics(
label: S.of(context).address_book,
child: InkWell(
onTap: () async {
final contact =
await Navigator.of(context).pushNamed(
Routes.pickerAddressBook,
arguments: widget.initialCurrency,
);
if (contact is ContactBase) {
setState(() =>
addressController.text = contact.address);
widget.onPushAddressBookButton?.call(context);
}
},
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: widget.addressButtonsColor,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/open_book.png',
color: Theme.of(context)
.extension<SendPageTheme>()!
.textFieldButtonIconColor,
)),
),
)),
),
Padding( Padding(
padding: EdgeInsets.only(left: 10), padding: EdgeInsets.only(left: 2),
child: Container( child: Container(
width: 34, width: 34,
height: 34, height: 34,
padding: EdgeInsets.only(top: 0), padding: EdgeInsets.only(top: 0),
child: Semantics( child: Semantics(
label: S.of(context).address_book, label: S.of(context).copy_address,
child: InkWell( child: InkWell(
onTap: () async { onTap: () {
final contact = Clipboard.setData(ClipboardData(
await Navigator.of(context).pushNamed( text: addressController.text));
Routes.pickerAddressBook, showBar<void>(
arguments: widget.initialCurrency, context, S.of(context).copied_to_clipboard);
); },
child: Container(
if (contact is ContactBase) { padding: EdgeInsets.fromLTRB(8, 8, 0, 8),
setState(() => color: Colors.transparent,
addressController.text = contact.address); child: copyImage),
widget.onPushAddressBookButton?.call(context); ),
} )))
}, ])))
child: Container( ])),
padding: EdgeInsets.all(8), ),
decoration: BoxDecoration( ],
color: widget.addressButtonsColor,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/open_book.png',
color: Theme.of(context)
.extension<SendPageTheme>()!
.textFieldButtonIconColor,
)),
),
)),
),
Padding(
padding: EdgeInsets.only(left: 2),
child: Container(
width: 34,
height: 34,
padding: EdgeInsets.only(top: 0),
child: Semantics(
label: S.of(context).copy_address,
child: InkWell(
onTap: () {
Clipboard.setData(
ClipboardData(text: addressController.text));
showBar<void>(
context, S.of(context).copied_to_clipboard);
},
child: Container(
padding: EdgeInsets.fromLTRB(8, 8, 0, 8),
color: Colors.transparent,
child: copyImage),
),
)))
])))
])),
),
]), ]),
); );
} }

View file

@ -24,22 +24,17 @@ import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart'; import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
void showInformation( void showInformation(ExchangeTradeViewModel exchangeTradeViewModel, BuildContext context) {
ExchangeTradeViewModel exchangeTradeViewModel, BuildContext context) {
final trade = exchangeTradeViewModel.trade; final trade = exchangeTradeViewModel.trade;
final walletName = exchangeTradeViewModel.wallet.name; final walletName = exchangeTradeViewModel.wallet.name;
final information = exchangeTradeViewModel.isSendable final information = exchangeTradeViewModel.isSendable
? S.current.exchange_result_confirm( ? S.current.exchange_result_confirm(trade.amount, trade.from.toString(), walletName) +
trade.amount, trade.from.toString(), walletName) + exchangeTradeViewModel.extraInfo
exchangeTradeViewModel.extraInfo : S.current.exchange_result_description(trade.amount, trade.from.toString()) +
: S.current.exchange_result_description( exchangeTradeViewModel.extraInfo;
trade.amount, trade.from.toString()) +
exchangeTradeViewModel.extraInfo;
showPopUp<void>( showPopUp<void>(context: context, builder: (_) => InformationPage(information: information));
context: context,
builder: (_) => InformationPage(information: information));
} }
class ExchangeTradePage extends BasePage { class ExchangeTradePage extends BasePage {
@ -72,8 +67,7 @@ class ExchangeTradePage extends BasePage {
} }
@override @override
Widget body(BuildContext context) => Widget body(BuildContext context) => ExchangeTradeForm(exchangeTradeViewModel);
ExchangeTradeForm(exchangeTradeViewModel);
} }
class ExchangeTradeForm extends StatefulWidget { class ExchangeTradeForm extends StatefulWidget {
@ -138,7 +132,9 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
style: TextStyle( style: TextStyle(
fontSize: 14.0, fontSize: 14.0,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor), color: Theme.of(context)
.extension<TransactionTradeTheme>()!
.detailsTitlesColor),
), ),
if (trade.expiredAt != null) if (trade.expiredAt != null)
TimerWidget(trade.expiredAt!, TimerWidget(trade.expiredAt!,
@ -159,9 +155,9 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all( border: Border.all(
width: 3, width: 3,
color: Theme.of(context).extension<ExchangePageTheme>()!.qrCodeColor color: Theme.of(context)
) .extension<ExchangePageTheme>()!
), .qrCodeColor)),
child: QrImage(data: trade.inputAddress ?? fetchingLabel), child: QrImage(data: trade.inputAddress ?? fetchingLabel),
)))), )))),
Spacer(flex: 3) Spacer(flex: 3)
@ -192,10 +188,8 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
? Builder( ? Builder(
builder: (context) => GestureDetector( builder: (context) => GestureDetector(
onTap: () { onTap: () {
Clipboard.setData( Clipboard.setData(ClipboardData(text: value));
ClipboardData(text: value)); showBar<void>(context, S.of(context).copied_to_clipboard);
showBar<void>(context,
S.of(context).copied_to_clipboard);
}, },
child: content, child: content,
)) ))
@ -209,17 +203,14 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
bottomSectionPadding: EdgeInsets.fromLTRB(24, 0, 24, 24), bottomSectionPadding: EdgeInsets.fromLTRB(24, 0, 24, 24),
bottomSection: Observer(builder: (_) { bottomSection: Observer(builder: (_) {
final trade = widget.exchangeTradeViewModel.trade; final trade = widget.exchangeTradeViewModel.trade;
final sendingState = final sendingState = widget.exchangeTradeViewModel.sendViewModel.state;
widget.exchangeTradeViewModel.sendViewModel.state;
return widget.exchangeTradeViewModel.isSendable && return widget.exchangeTradeViewModel.isSendable &&
!(sendingState is TransactionCommitted) !(sendingState is TransactionCommitted)
? LoadingPrimaryButton( ? LoadingPrimaryButton(
isDisabled: trade.inputAddress == null || isDisabled: trade.inputAddress == null || trade.inputAddress!.isEmpty,
trade.inputAddress!.isEmpty,
isLoading: sendingState is IsExecutingState, isLoading: sendingState is IsExecutingState,
onPressed: () => onPressed: () => widget.exchangeTradeViewModel.confirmSending(),
widget.exchangeTradeViewModel.confirmSending(),
text: S.of(context).confirm, text: S.of(context).confirm,
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
textColor: Colors.white) textColor: Colors.white)
@ -257,27 +248,26 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
return ConfirmSendingAlert( return ConfirmSendingAlert(
alertTitle: S.of(popupContext).confirm_sending, alertTitle: S.of(popupContext).confirm_sending,
amount: S.of(popupContext).send_amount, amount: S.of(popupContext).send_amount,
amountValue: widget.exchangeTradeViewModel.sendViewModel amountValue: widget
.pendingTransaction!.amountFormatted, .exchangeTradeViewModel.sendViewModel.pendingTransaction!.amountFormatted,
fee: S.of(popupContext).send_fee, fee: S.of(popupContext).send_fee,
feeValue: widget.exchangeTradeViewModel.sendViewModel feeValue: widget
.pendingTransaction!.feeFormatted, .exchangeTradeViewModel.sendViewModel.pendingTransaction!.feeFormatted,
feeRate: widget.exchangeTradeViewModel.sendViewModel.pendingTransaction!.feeRate, feeRate:
widget.exchangeTradeViewModel.sendViewModel.pendingTransaction!.feeRate,
rightButtonText: S.of(popupContext).send, rightButtonText: S.of(popupContext).send,
leftButtonText: S.of(popupContext).cancel, leftButtonText: S.of(popupContext).cancel,
actionRightButton: () async { actionRightButton: () async {
Navigator.of(popupContext).pop(); Navigator.of(popupContext).pop();
await widget.exchangeTradeViewModel.sendViewModel await widget.exchangeTradeViewModel.sendViewModel.commitTransaction();
.commitTransaction();
transactionStatePopup(); transactionStatePopup();
}, },
actionLeftButton: () => Navigator.of(popupContext).pop(), actionLeftButton: () => Navigator.of(popupContext).pop(),
feeFiatAmount: widget.exchangeTradeViewModel feeFiatAmount:
.pendingTransactionFeeFiatAmountFormatted, widget.exchangeTradeViewModel.pendingTransactionFeeFiatAmountFormatted,
fiatAmountValue: widget.exchangeTradeViewModel fiatAmountValue:
.pendingTransactionFiatAmountValueFormatted, widget.exchangeTradeViewModel.pendingTransactionFiatAmountValueFormatted,
outputs: widget.exchangeTradeViewModel.sendViewModel outputs: widget.exchangeTradeViewModel.sendViewModel.outputs);
.outputs);
}); });
}); });
} }
@ -305,85 +295,26 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
void transactionStatePopup() { void transactionStatePopup() {
if (this.mounted) { if (this.mounted) {
showPopUp<void>( showPopUp<void>(
context: context, context: context,
builder: (BuildContext popupContext) { builder: (BuildContext popupContext) {
return Observer(builder: (_) { return Observer(builder: (_) {
final state = widget final state = widget.exchangeTradeViewModel.sendViewModel.state;
.exchangeTradeViewModel.sendViewModel.state;
if (state is TransactionCommitted) { if (state is TransactionCommitted) {
return Stack( return Stack(
children: <Widget>[ children: <Widget>[
Container( Container(
color: Theme.of(popupContext).colorScheme.background, color: Theme.of(popupContext).colorScheme.background,
child: Center( child: Center(
child: Image.asset( child: Image.asset('assets/images/birthday_cake.png'),
'assets/images/birthday_cake.png'),
),
),
Center(
child: Padding(
padding: EdgeInsets.only(
top: 220, left: 24, right: 24),
child: Text(
S.of(popupContext).send_success(widget
.exchangeTradeViewModel
.wallet
.currency
.toString()),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Theme.of(popupContext).extension<CakeTextTheme>()!.titleColor,
decoration: TextDecoration.none,
),
), ),
), ),
), Center(
Positioned(
left: 24,
right: 24,
bottom: 24,
child: PrimaryButton(
onPressed: () {
Navigator.pushNamedAndRemoveUntil(
popupContext,
Routes.dashboard,
(route) => false,
);
RequestReviewHandler.requestReview();
},
text: S.of(popupContext).got_it,
color: Theme.of(popupContext).primaryColor,
textColor: Colors.white))
],
);
}
return Stack(
children: <Widget>[
Container(
color: Theme.of(popupContext).colorScheme.background,
child: Center(
child: Image.asset(
'assets/images/birthday_cake.png'),
),
),
BackdropFilter(
filter: ImageFilter.blur(
sigmaX: 3.0, sigmaY: 3.0),
child: Container(
decoration: BoxDecoration(
color: Theme.of(popupContext)
.colorScheme
.background
.withOpacity(0.25)),
child: Center(
child: Padding( child: Padding(
padding: EdgeInsets.only(top: 220), padding: EdgeInsets.only(top: 220, left: 24, right: 24),
child: Text( child: Text(
S.of(popupContext).send_sending, S.of(popupContext).send_success(
widget.exchangeTradeViewModel.wallet.currency.toString()),
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontSize: 22, fontSize: 22,
@ -394,12 +325,60 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
), ),
), ),
), ),
Positioned(
left: 24,
right: 24,
bottom: 24,
child: PrimaryButton(
onPressed: () {
Navigator.pushNamedAndRemoveUntil(
popupContext,
Routes.dashboard,
(route) => false,
);
RequestReviewHandler.requestReview();
},
text: S.of(popupContext).got_it,
color: Theme.of(popupContext).primaryColor,
textColor: Colors.white))
],
);
}
return Stack(
children: <Widget>[
Container(
color: Theme.of(popupContext).colorScheme.background,
child: Center(
child: Image.asset('assets/images/birthday_cake.png'),
),
), ),
) BackdropFilter(
], filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
); child: Container(
decoration: BoxDecoration(
color: Theme.of(popupContext).colorScheme.background.withOpacity(0.25)),
child: Center(
child: Padding(
padding: EdgeInsets.only(top: 220),
child: Text(
S.of(popupContext).send_sending,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Theme.of(popupContext).extension<CakeTextTheme>()!.titleColor,
decoration: TextDecoration.none,
),
),
),
),
),
)
],
);
});
}); });
});
} }
} }
} }

View file

@ -0,0 +1,340 @@
import 'package:cake_wallet/lightning/lightning.dart';
import 'package:cake_wallet/src/screens/receive/widgets/lightning_input_form.dart';
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/present_receive_option_picker.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
import 'package:cake_wallet/view_model/lightning_invoice_page_view_model.dart';
import 'package:cake_wallet/view_model/lightning_view_model.dart';
import 'package:cw_core/receive_page_option.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/trail_button.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:mobx/mobx.dart';
import 'package:qr_flutter/qr_flutter.dart' as qr;
class LightningInvoicePage extends BasePage {
LightningInvoicePage({
required this.lightningInvoicePageViewModel,
required this.receiveOptionViewModel,
}) : _amountFocusNode = FocusNode() {}
final _descriptionController = TextEditingController();
final _amountController = TextEditingController();
final FocusNode _amountFocusNode;
final LightningInvoicePageViewModel lightningInvoicePageViewModel;
final ReceiveOptionViewModel receiveOptionViewModel;
final _formKey = GlobalKey<FormState>();
bool effectsInstalled = false;
@override
bool get gradientAll => true;
@override
bool get resizeToAvoidBottomInset => false;
@override
bool get extendBodyBehindAppBar => true;
@override
AppBarStyle get appBarStyle => AppBarStyle.transparent;
@override
void onClose(BuildContext context) => Navigator.popUntil(context, (route) => route.isFirst);
@override
Widget middle(BuildContext context) => PresentReceiveOptionPicker(
receiveOptionViewModel: receiveOptionViewModel, color: titleColor(context));
@override
Widget trailing(BuildContext context) => TrailButton(
caption: S.of(context).clear,
onPressed: () {
_formKey.currentState?.reset();
});
Future<bool> _onNavigateBack(BuildContext context) async {
onClose(context);
return false;
}
@override
Widget body(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) => _setReactions(context));
return WillPopScope(
onWillPop: () => _onNavigateBack(context),
child: KeyboardActions(
disableScroll: true,
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context).extension<KeyboardTheme>()!.keyboardBarColor,
nextFocus: false,
actions: [
KeyboardActionsItem(
focusNode: _amountFocusNode,
toolbarButtons: [(_) => KeyboardDoneButton()],
),
]),
child: Container(
color: Theme.of(context).colorScheme.background,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24),
content: Container(
decoration: responsiveLayoutUtil.shouldRenderMobileUI
? BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
gradient: LinearGradient(
colors: [
Theme.of(context)
.extension<ExchangePageTheme>()!
.firstGradientTopPanelColor,
Theme.of(context)
.extension<ExchangePageTheme>()!
.secondGradientTopPanelColor,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
)
: null,
child: Padding(
padding: EdgeInsets.fromLTRB(24, 120, 24, 0),
child: LightningInvoiceForm(
descriptionController: _descriptionController,
amountController: _amountController,
depositAmountFocus: _amountFocusNode,
formKey: _formKey,
lightningInvoicePageViewModel: lightningInvoicePageViewModel,
),
),
),
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.only(top: 12, bottom: 12, right: 6),
margin: const EdgeInsets.only(left: 24, right: 24, bottom: 48),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(15)),
color: Color.fromARGB(255, 170, 147, 30),
border: Border.all(
color: Color.fromARGB(178, 223, 214, 0),
width: 2,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: 48,
height: 48,
margin: EdgeInsets.only(left: 12, bottom: 48, right: 20),
child: Image.asset(
"assets/images/warning.png",
color: Color.fromARGB(128, 255, 255, 255),
),
),
FutureBuilder(
future: lightningInvoicePageViewModel.lightningViewModel
.invoiceSoftLimitsSats(),
builder: (context, snapshot) {
if (snapshot.data == null) {
return Expanded(
child:
Container(child: Center(child: CircularProgressIndicator())));
}
late String finalText;
InvoiceSoftLimitsResult limits = snapshot.data as InvoiceSoftLimitsResult;
if (limits.inboundLiquidity == 0) {
finalText = S.of(context).lightning_invoice_min(
limits.feePercent.toString(),
lightning!.satsToLightningString(limits.minFee));
} else {
finalText = S.of(context).lightning_invoice_min_max(
limits.feePercent.toString(),
lightning!.satsToLightningString(limits.minFee),
lightning!.satsToLightningString(limits.inboundLiquidity),
);
}
return Expanded(
child: Text(
finalText,
maxLines: 5,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
),
),
);
},
),
],
),
),
Observer(builder: (_) {
return LoadingPrimaryButton(
text: S.of(context).create_invoice,
onPressed: () {
FocusScope.of(context).unfocus();
lightningInvoicePageViewModel.setRequestParams(
inputAmount: _amountController.text,
inputDescription: _descriptionController.text,
);
lightningInvoicePageViewModel.createInvoice();
},
color: Theme.of(context).primaryColor,
textColor: Colors.white,
isLoading: lightningInvoicePageViewModel.state is IsExecutingState,
);
}),
],
),
),
),
),
);
}
void _setReactions(BuildContext context) {
if (effectsInstalled) {
return;
}
reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) async {
if (option == lightning!.getOptionOnchain()) {
Navigator.popAndPushNamed(
context,
Routes.lightningReceiveOnchain,
arguments: [lightning!.getOptionOnchain()],
);
}
});
reaction((_) => lightningInvoicePageViewModel.state, (ExecutionState state) {
if (state is ExecutedSuccessfullyState) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle: S.of(context).invoice_created,
alertContent: '',
contentWidget: Column(
children: [
Center(
child: AspectRatio(
aspectRatio: 1.0,
child: Container(
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
border: Border.all(
width: 3,
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
),
),
child: Container(
decoration: BoxDecoration(
border: Border.all(
width: 3,
color: Colors.white,
),
),
child: QrImage(
data: state.payload as String,
version: 14,
errorCorrectionLevel: qr.QrErrorCorrectLevel.L,
)),
),
),
),
Container(
padding: const EdgeInsets.only(top: 12, bottom: 12, right: 6),
margin: const EdgeInsets.only(top: 32),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(15)),
color: Color.fromARGB(255, 170, 147, 30),
border: Border.all(
color: Color.fromARGB(178, 223, 214, 0),
width: 2,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: 32,
height: 32,
margin: EdgeInsets.only(left: 12, bottom: 48, right: 12),
child: Image.asset(
"assets/images/warning.png",
color: Color.fromARGB(128, 255, 255, 255),
),
),
Expanded(
child: Material(
color: Colors.transparent,
child: Text(
S.of(context).lightning_invoice_warning,
maxLines: 5,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color:
Theme.of(context).extension<DashboardPageTheme>()!.textColor,
),
),
),
),
],
),
),
],
),
rightButtonText: S.of(context).ok,
actionRightButton: () => Navigator.of(context).pop(),
actionLeftButton: () async {
Clipboard.setData(ClipboardData(text: state.payload as String));
showBar<void>(context, S.of(context).copied_to_clipboard);
},
leftButtonText: S.of(context).copy,
);
});
}
if (state is FailureState) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: state.error.toString(),
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
}
});
effectsInstalled = true;
}
}

View file

@ -0,0 +1,252 @@
import 'package:cake_wallet/entities/qr_view_data.dart';
import 'package:cake_wallet/lightning/lightning.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/present_receive_option_picker.dart';
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
import 'package:cake_wallet/src/widgets/gradient_background.dart';
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
import 'package:cake_wallet/themes/extensions/qr_code_theme.dart';
import 'package:cake_wallet/utils/brightness_util.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
import 'package:cake_wallet/view_model/lightning_view_model.dart';
import 'package:cw_core/receive_page_option.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
class LightningReceiveOnchainPage extends BasePage {
LightningReceiveOnchainPage(
{required this.addressListViewModel,
required this.receiveOptionViewModel,
required this.lightningViewModel})
: _amountController = TextEditingController(),
_formKey = GlobalKey<FormState>() {
_amountController.addListener(() {
if (_formKey.currentState!.validate()) {
addressListViewModel.changeAmount(_amountController.text);
}
});
}
final WalletAddressListViewModel addressListViewModel;
final ReceiveOptionViewModel receiveOptionViewModel;
final LightningViewModel lightningViewModel;
final TextEditingController _amountController;
final GlobalKey<FormState> _formKey;
bool effectsInstalled = false;
@override
String get title => S.current.receive;
@override
bool get gradientBackground => true;
@override
bool get resizeToAvoidBottomInset => true;
@override
Widget middle(BuildContext context) => PresentReceiveOptionPicker(
color: titleColor(context), receiveOptionViewModel: receiveOptionViewModel);
@override
Widget Function(BuildContext, Widget) get rootWrapper =>
(BuildContext context, Widget scaffold) => GradientBackground(scaffold: scaffold);
@override
Widget body(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) => _setReactions(context));
final copyImage = Image.asset('assets/images/copy_address.png',
color: Theme.of(context).extension<QRCodeTheme>()!.qrWidgetCopyButtonColor);
String heroTag = "lightning_receive";
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
FutureBuilder(
future: lightningViewModel.receiveOnchain(),
builder: ((context, snapshot) {
if (snapshot.data == null) {
return CircularProgressIndicator();
}
ReceiveOnchainResult results = snapshot.data as ReceiveOnchainResult;
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Text(
S.of(context).qr_fullscreen,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor),
),
),
Row(
children: <Widget>[
Spacer(flex: 3),
Observer(
builder: (_) => Flexible(
flex: 5,
child: GestureDetector(
onTap: () {
BrightnessUtil.changeBrightnessForFunction(
() async {
await Navigator.pushNamed(context, Routes.fullscreenQR,
arguments: QrViewData(
data: results.bitcoinAddress,
heroTag: heroTag,
));
},
);
},
child: Hero(
tag: Key(heroTag),
child: Center(
child: AspectRatio(
aspectRatio: 1.0,
child: Container(
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
border: Border.all(
width: 3,
color: Theme.of(context)
.extension<DashboardPageTheme>()!
.textColor,
),
),
child: Container(
decoration: BoxDecoration(
border: Border.all(
width: 3,
color: Colors.white,
),
),
child: QrImage(data: results.bitcoinAddress)),
),
),
),
),
),
),
),
Spacer(flex: 3)
],
),
Padding(
padding: EdgeInsets.only(top: 20, bottom: 8, left: 24, right: 24),
child: Builder(
builder: (context) => Observer(
builder: (context) => GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(text: results.bitcoinAddress));
showBar<void>(context, S.of(context).copied_to_clipboard);
},
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Text(
results.bitcoinAddress,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.extension<DashboardPageTheme>()!
.textColor),
),
),
Padding(
padding: EdgeInsets.only(left: 12),
child: copyImage,
)
],
),
),
),
),
)
],
);
}),
),
Container(
padding: const EdgeInsets.only(top: 24, bottom: 24, right: 6),
margin: const EdgeInsets.symmetric(horizontal: 24),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(15)),
color: Color.fromARGB(255, 170, 147, 30),
border: Border.all(
color: Color.fromARGB(178, 223, 214, 0),
width: 2,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: 48,
height: 48,
margin: EdgeInsets.only(left: 12, bottom: 48, right: 20),
child: Image.asset(
"assets/images/warning.png",
color: Color.fromARGB(128, 255, 255, 255),
),
),
FutureBuilder(
future: lightningViewModel.receiveOnchain(),
builder: (context, snapshot) {
if (snapshot.data == null) {
return Expanded(
child: Container(child: Center(child: CircularProgressIndicator())));
}
ReceiveOnchainResult results = snapshot.data as ReceiveOnchainResult;
return Expanded(
child: Text(
S.of(context).lightning_receive_limits(
lightning!.satsToLightningString(results.minAllowedDeposit),
lightning!.satsToLightningString(results.maxAllowedDeposit),
results.feePercent.toString(),
lightning!.satsToLightningString(results.fee),
),
maxLines: 10,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
),
),
);
}),
],
),
),
],
);
}
void _setReactions(BuildContext context) {
if (effectsInstalled) {
return;
}
reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) async {
if (option == lightning!.getOptionInvoice()) {
Navigator.popAndPushNamed(
context,
Routes.lightningInvoice,
arguments: [lightning!.getOptionInvoice()],
);
}
});
effectsInstalled = true;
}
}

View file

@ -7,15 +7,16 @@ import 'package:flutter/services.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart'; import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
class AnonpayCurrencyInputField extends StatelessWidget { class AnonpayCurrencyInputField extends StatelessWidget {
const AnonpayCurrencyInputField( const AnonpayCurrencyInputField({
{super.key, super.key,
required this.onTapPicker, this.onTapPicker,
required this.selectedCurrency, required this.selectedCurrency,
required this.focusNode, required this.focusNode,
required this.controller, required this.controller,
required this.minAmount, required this.minAmount,
required this.maxAmount}); required this.maxAmount,
final Function() onTapPicker; });
final Function()? onTapPicker;
final Currency selectedCurrency; final Currency selectedCurrency;
final FocusNode focusNode; final FocusNode focusNode;
final TextEditingController controller; final TextEditingController controller;
@ -23,6 +24,7 @@ class AnonpayCurrencyInputField extends StatelessWidget {
final String maxAmount; final String maxAmount;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool hasDecimals = selectedCurrency.name.toLowerCase() != "sats";
final arrowBottomPurple = Image.asset( final arrowBottomPurple = Image.asset(
'assets/images/arrow_bottom_purple_icon.png', 'assets/images/arrow_bottom_purple_icon.png',
color: Colors.white, color: Colors.white,
@ -34,40 +36,50 @@ class AnonpayCurrencyInputField extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border( border: Border(
bottom: BorderSide( bottom: BorderSide(
color: color: Theme.of(context)
Theme.of(context).extension<ExchangePageTheme>()!.textFieldBorderBottomPanelColor, .extension<ExchangePageTheme>()!
.textFieldBorderBottomPanelColor,
width: 1)), width: 1)),
), ),
child: Padding( child: Padding(
padding: EdgeInsets.only(top: 20), padding: EdgeInsets.only(top: 20),
child: Row( child: Row(
children: [ children: [
Container( if (onTapPicker != null)
padding: EdgeInsets.only(right: 8), Container(
height: 32, padding: EdgeInsets.only(right: 8),
child: InkWell( height: 32,
onTap: onTapPicker, child: InkWell(
child: Row( onTap: onTapPicker,
mainAxisAlignment: MainAxisAlignment.spaceBetween, child: Row(
mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ mainAxisSize: MainAxisSize.min,
Padding( children: <Widget>[
padding: EdgeInsets.only(right: 5), Padding(
child: arrowBottomPurple, padding: EdgeInsets.only(right: 5),
), child: arrowBottomPurple,
Text(selectedCurrency.name.toUpperCase(), ),
style: TextStyle( Text(selectedCurrency.name.toUpperCase(),
fontWeight: FontWeight.w600, fontSize: 16, color: Colors.white)) style: TextStyle(
]), fontWeight: FontWeight.w600,
), fontSize: 16,
), color: Colors.white))
]),
),
)
else
Text(selectedCurrency.name.toUpperCase(),
style: TextStyle(
fontWeight: FontWeight.w600, fontSize: 16, color: Colors.white)),
selectedCurrency.tag != null selectedCurrency.tag != null
? Padding( ? Padding(
padding: const EdgeInsets.only(right: 3.0), padding: const EdgeInsets.only(right: 3.0),
child: Container( child: Container(
height: 32, height: 32,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor, color: Theme.of(context)
.extension<SendPageTheme>()!
.textFieldButtonColor,
borderRadius: BorderRadius.all(Radius.circular(6))), borderRadius: BorderRadius.all(Radius.circular(6))),
child: Center( child: Center(
child: Padding( child: Padding(
@ -77,7 +89,9 @@ class AnonpayCurrencyInputField extends StatelessWidget {
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonIconColor, color: Theme.of(context)
.extension<SendPageTheme>()!
.textFieldButtonIconColor,
), ),
), ),
), ),
@ -102,20 +116,21 @@ class AnonpayCurrencyInputField extends StatelessWidget {
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
enabled: true, enabled: true,
textAlign: TextAlign.left, textAlign: TextAlign.left,
keyboardType: keyboardType: TextInputType.numberWithOptions(
TextInputType.numberWithOptions(signed: false, decimal: true), signed: false, decimal: hasDecimals),
inputFormatters: [ inputFormatters: [
FilteringTextInputFormatter.deny(RegExp('[\\-|\\ ]')) FilteringTextInputFormatter.deny(RegExp('[\\-|\\ ]')),
if (!hasDecimals) FilteringTextInputFormatter.deny(RegExp('[\.,]')),
], ],
hintText: '0.0000', hintText: hasDecimals ? '0.0000' : '0',
borderColor: Colors.transparent, borderColor: Colors.transparent,
//widget.borderColor,
textStyle: TextStyle( textStyle: TextStyle(
fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white), fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white),
placeholderTextStyle: TextStyle( placeholderTextStyle: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor, color:
Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor,
), ),
validator: null, validator: null,
), ),
@ -131,19 +146,23 @@ class AnonpayCurrencyInputField extends StatelessWidget {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( if (minAmount.isNotEmpty) ...[
S.of(context).min_value(minAmount, selectedCurrency.toString()), Text(
style: TextStyle( S.of(context).min_value(minAmount, selectedCurrency.toString()),
fontSize: 10,
height: 1.2,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor),
),
SizedBox(width: 10),
Text(S.of(context).max_value(maxAmount, selectedCurrency.toString()),
style: TextStyle( style: TextStyle(
fontSize: 10, fontSize: 10,
height: 1.2, height: 1.2,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor)), color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor),
),
],
SizedBox(width: 10),
if (maxAmount.isNotEmpty) ...[
Text(S.of(context).max_value(maxAmount, selectedCurrency.toString()),
style: TextStyle(
fontSize: 10,
height: 1.2,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor))
],
], ],
), ),
) )

View file

@ -0,0 +1,81 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/lightning/lightning.dart';
import 'package:cake_wallet/src/screens/receive/widgets/anonpay_currency_input_field.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
import 'package:cake_wallet/typography.dart';
import 'package:cake_wallet/view_model/lightning_invoice_page_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/lightning/lightning.dart';
class LightningInvoiceForm extends StatelessWidget {
LightningInvoiceForm({
super.key,
required this.formKey,
required this.lightningInvoicePageViewModel,
required this.amountController,
required this.descriptionController,
required this.depositAmountFocus,
}) : _descriptionFocusNode = FocusNode() {
amountController.text = lightningInvoicePageViewModel.amount;
descriptionController.text = lightningInvoicePageViewModel.description;
}
final TextEditingController amountController;
final TextEditingController descriptionController;
final LightningInvoicePageViewModel lightningInvoicePageViewModel;
final FocusNode depositAmountFocus;
final FocusNode _descriptionFocusNode;
final GlobalKey<FormState> formKey;
@override
Widget build(BuildContext context) {
return Form(
key: formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).invoice_details,
style: textMediumSemiBold(),
),
Observer(builder: (_) {
return AnonpayCurrencyInputField(
controller: amountController,
focusNode: depositAmountFocus,
maxAmount: '',
minAmount: (lightningInvoicePageViewModel.minimum != null)
? lightning!
.satsToLightningString(lightningInvoicePageViewModel.minimum!.round())
: '...',
selectedCurrency: CryptoCurrency.btcln,
);
}),
SizedBox(
height: 24,
),
BaseTextFormField(
controller: descriptionController,
focusNode: _descriptionFocusNode,
textInputAction: TextInputAction.next,
borderColor:
Theme.of(context).extension<ExchangePageTheme>()!.textFieldBorderTopPanelColor,
suffixIcon: SizedBox(width: 36),
hintText: S.of(context).optional_description,
placeholderTextStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor,
),
textStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white),
validator: null,
),
SizedBox(
height: 52,
),
],
));
}
}

View file

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:qr_flutter/qr_flutter.dart' as qr; import 'package:qr_flutter/qr_flutter.dart' as qr;
import 'package:qr_flutter/qr_flutter.dart';
class QrImage extends StatelessWidget { class QrImage extends StatelessWidget {
QrImage({ QrImage({
@ -23,7 +24,9 @@ class QrImage extends StatelessWidget {
return qr.QrImageView( return qr.QrImageView(
data: data, data: data,
errorCorrectionLevel: errorCorrectionLevel, errorCorrectionLevel: errorCorrectionLevel,
version: version ?? 9, // Previous value: 7 something happened after flutter upgrade monero wallets addresses are longer than ver. 7 ??? // Previous value: 7 something happened after flutter upgrade monero wallets addresses are longer than ver. 7 ???
// changed from 9 to auto
version: version ?? QrVersions.auto,
size: size, size: size,
foregroundColor: foregroundColor, foregroundColor: foregroundColor,
backgroundColor: backgroundColor, backgroundColor: backgroundColor,

View file

@ -270,7 +270,8 @@ class WalletRestorePage extends BasePage {
// bip39: // bip39:
const validSeedLengths = [12, 18, 24]; const validSeedLengths = [12, 18, 24];
if (walletRestoreViewModel.type == WalletType.bitcoin && final type = walletRestoreViewModel.type;
if ((type == WalletType.bitcoin || type == WalletType.lightning) &&
!(validSeedLengths.contains(seedWords.length))) { !(validSeedLengths.contains(seedWords.length))) {
return false; return false;
} }

View file

@ -0,0 +1,311 @@
import 'package:breez_sdk/breez_sdk.dart';
import 'package:breez_sdk/bridge_generated.dart' as BZG;
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/lightning/lightning.dart';
import 'package:cake_wallet/src/screens/receive/widgets/anonpay_currency_input_field.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/lightning_send_view_model.dart';
import 'package:cake_wallet/view_model/lightning_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/generated/i18n.dart';
class LightningSendConfirmPage extends BasePage {
LightningSendConfirmPage({required this.invoice, required this.lightningSendViewModel})
: _formKey = GlobalKey<FormState>() {
initialSatAmount = ((invoice.amountMsat ?? 0) ~/ 1000);
_amountController = TextEditingController();
_fiatAmountController = TextEditingController();
_amountController.text = initialSatAmount.toString();
_fiatAmountController.text = lightningSendViewModel.formattedFiatAmount(initialSatAmount);
}
final GlobalKey<FormState> _formKey;
final controller = PageController(initialPage: 0);
BZG.LNInvoice invoice;
late int initialSatAmount;
late TextEditingController _amountController;
late TextEditingController _fiatAmountController;
final FocusNode _depositAmountFocus = FocusNode();
final LightningSendViewModel lightningSendViewModel;
bool _effectsInstalled = false;
@override
String get title => S.current.send;
@override
bool get gradientAll => true;
@override
bool get resizeToAvoidBottomInset => false;
@override
bool get extendBodyBehindAppBar => true;
@override
Widget? leading(BuildContext context) {
final _backButton = Icon(
Icons.arrow_back_ios,
color: titleColor(context),
size: 16,
);
final _closeButton =
currentTheme.type == ThemeType.dark ? closeButtonImageDarkTheme : closeButtonImage;
bool isMobileView = responsiveLayoutUtil.shouldRenderMobileUI;
return MergeSemantics(
child: SizedBox(
height: isMobileView ? 37 : 45,
width: isMobileView ? 37 : 45,
child: ButtonTheme(
minWidth: double.minPositive,
child: Semantics(
label: !isMobileView ? S.of(context).close : S.of(context).seed_alert_back,
child: TextButton(
style: ButtonStyle(
overlayColor: MaterialStateColor.resolveWith((states) => Colors.transparent),
),
onPressed: () => onClose(context),
child: !isMobileView ? _closeButton : _backButton,
),
),
),
),
);
}
@override
AppBarStyle get appBarStyle => AppBarStyle.transparent;
@override
void onClose(BuildContext context) {
Navigator.of(context).pop();
}
@override
Widget body(BuildContext context) {
_setEffects(context);
return WillPopScope(
onWillPop: () => _onNavigateBack(context),
child: KeyboardActions(
disableScroll: true,
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context).extension<KeyboardTheme>()!.keyboardBarColor,
nextFocus: false,
actions: [
KeyboardActionsItem(
focusNode: FocusNode(),
toolbarButtons: [(_) => KeyboardDoneButton()],
),
]),
child: Container(
color: Theme.of(context).colorScheme.background,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24),
content: Container(
decoration: responsiveLayoutUtil.shouldRenderMobileUI
? BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
gradient: LinearGradient(
colors: [
Theme.of(context)
.extension<ExchangePageTheme>()!
.firstGradientTopPanelColor,
Theme.of(context)
.extension<ExchangePageTheme>()!
.secondGradientTopPanelColor,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
)
: null,
child: Observer(builder: (_) {
return Padding(
padding: EdgeInsets.fromLTRB(24, 120, 24, 0),
child: Column(
children: [
BaseTextFormField(
enabled: false,
borderColor: Theme.of(context)
.extension<ExchangePageTheme>()!
.textFieldBorderTopPanelColor,
suffixIcon: SizedBox(width: 36),
initialValue: "${S.of(context).invoice}: ${invoice.bolt11}",
placeholderTextStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor,
),
textStyle: TextStyle(
fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white),
validator: null,
),
SizedBox(height: 24),
if (invoice.amountMsat == null)
Observer(builder: (_) {
return AnonpayCurrencyInputField(
controller: _amountController,
focusNode: _depositAmountFocus,
maxAmount: '',
minAmount: '',
selectedCurrency: CryptoCurrency.btcln,
);
})
else
BaseTextFormField(
enabled: false,
borderColor: Theme.of(context)
.extension<ExchangePageTheme>()!
.textFieldBorderTopPanelColor,
suffixIcon: SizedBox(width: 36),
initialValue:
"sats: ${lightning!.bitcoinAmountToLightningString(amount: initialSatAmount)}",
placeholderTextStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor,
),
textStyle: TextStyle(
fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white),
validator: null,
),
SizedBox(height: 24),
BaseTextFormField(
enabled: false,
controller: _fiatAmountController,
prefixIcon: Padding(
padding: EdgeInsets.only(top: 9),
child: Text(
lightningSendViewModel.fiat.title + ':',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
),
borderColor: Theme.of(context)
.extension<ExchangePageTheme>()!
.textFieldBorderTopPanelColor,
suffixIcon: SizedBox(width: 36),
placeholderTextStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor,
),
textStyle: TextStyle(
fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white),
validator: null,
),
SizedBox(height: 24),
if (invoice.description?.isNotEmpty ?? false) ...[
BaseTextFormField(
enabled: false,
initialValue: "${S.of(context).description}: ${invoice.description}",
textInputAction: TextInputAction.next,
borderColor: Theme.of(context)
.extension<ExchangePageTheme>()!
.textFieldBorderTopPanelColor,
suffixIcon: SizedBox(width: 36),
placeholderTextStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor,
),
textStyle: TextStyle(
fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white),
validator: null,
),
SizedBox(height: 24),
],
],
),
);
}),
),
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: Observer(builder: (_) {
return Column(
children: <Widget>[
LoadingPrimaryButton(
text: S.of(context).send,
onPressed: () async {
try {
await lightningSendViewModel.send(
invoice,
int.parse(_amountController.text)
);
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: '',
alertContent:
S.of(context).send_success(CryptoCurrency.btc.toString()),
buttonText: S.of(context).ok,
buttonAction: () {
Navigator.of(context).pop();
});
});
} catch (e) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: e.toString(),
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
}
},
color: Theme.of(context).primaryColor,
textColor: Colors.white,
isLoading: lightningSendViewModel.loading,
),
],
);
}),
),
),
),
);
}
Future<bool> _onNavigateBack(BuildContext context) async {
onClose(context);
return false;
}
void _setEffects(BuildContext context) {
if (_effectsInstalled) {
return;
}
_amountController.addListener(() {
final amount = _amountController.text;
_fiatAmountController.text = lightningSendViewModel.formattedFiatAmount(int.parse(amount));
});
_effectsInstalled = true;
}
}

View file

@ -0,0 +1,233 @@
import 'package:breez_sdk/breez_sdk.dart';
import 'package:breez_sdk/bridge_generated.dart';
import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/src/widgets/address_text_field.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/lightning_send_view_model.dart';
import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/material.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/src/widgets/trail_button.dart';
import 'package:cake_wallet/generated/i18n.dart';
class LightningSendPage extends BasePage {
LightningSendPage({
required this.output,
required this.authService,
required this.lightningSendViewModel,
}) : _formKey = GlobalKey<FormState>();
final Output output;
final AuthService authService;
final LightningSendViewModel lightningSendViewModel;
final GlobalKey<FormState> _formKey;
final bolt11Controller = TextEditingController();
final bolt11FocusNode = FocusNode();
bool _effectsInstalled = false;
@override
String get title => S.current.send;
@override
bool get gradientAll => true;
@override
bool get resizeToAvoidBottomInset => false;
@override
bool get extendBodyBehindAppBar => true;
@override
Widget? leading(BuildContext context) {
final _backButton = Icon(
Icons.arrow_back_ios,
color: titleColor(context),
size: 16,
);
final _closeButton =
currentTheme.type == ThemeType.dark ? closeButtonImageDarkTheme : closeButtonImage;
bool isMobileView = responsiveLayoutUtil.shouldRenderMobileUI;
return MergeSemantics(
child: SizedBox(
height: isMobileView ? 37 : 45,
width: isMobileView ? 37 : 45,
child: ButtonTheme(
minWidth: double.minPositive,
child: Semantics(
label: !isMobileView ? S.of(context).close : S.of(context).seed_alert_back,
child: TextButton(
style: ButtonStyle(
overlayColor: MaterialStateColor.resolveWith((states) => Colors.transparent),
),
onPressed: () => onClose(context),
child: !isMobileView ? _closeButton : _backButton,
),
),
),
),
);
}
@override
AppBarStyle get appBarStyle => AppBarStyle.transparent;
@override
void onClose(BuildContext context) {
Navigator.of(context).pop();
}
@override
Widget trailing(context) => TrailButton(
caption: S.of(context).clear,
onPressed: () {
_formKey.currentState?.reset();
});
@override
Widget body(BuildContext context) {
_setEffects(context);
return WillPopScope(
onWillPop: () => _onNavigateBack(context),
child: KeyboardActions(
disableScroll: true,
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context).extension<KeyboardTheme>()!.keyboardBarColor,
nextFocus: false,
actions: [
KeyboardActionsItem(
focusNode: FocusNode(),
toolbarButtons: [(_) => KeyboardDoneButton()],
),
]),
child: Container(
color: Theme.of(context).colorScheme.background,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24),
content: Container(
decoration: responsiveLayoutUtil.shouldRenderMobileUI
? BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
gradient: LinearGradient(
colors: [
Theme.of(context)
.extension<ExchangePageTheme>()!
.firstGradientTopPanelColor,
Theme.of(context)
.extension<ExchangePageTheme>()!
.secondGradientTopPanelColor,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
)
: null,
child: Padding(
padding: EdgeInsets.fromLTRB(24, 120, 24, 0),
child: Column(
children: [
AddressTextField(
focusNode: bolt11FocusNode,
controller: bolt11Controller,
onURIScanned: (uri) {
final paymentRequest = PaymentRequest.fromUri(uri);
bolt11Controller.text = paymentRequest.address;
},
options: [
AddressTextFieldOption.paste,
AddressTextFieldOption.qrCode,
AddressTextFieldOption.addressBook
],
buttonColor:
Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
borderColor:
Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
textStyle:
TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white),
hintStyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
onPushPasteButton: (context) async {
output.resetParsedAddress();
await output.fetchParsedAddress(context);
await send(context);
},
onPushAddressBookButton: (context) async {
output.resetParsedAddress();
},
onSelectedContact: (contact) {
output.loadContact(contact);
},
selectedCurrency: CryptoCurrency.btc,
),
SizedBox(height: 24),
],
),
),
),
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: Column(
children: <Widget>[
PrimaryButton(
text: S.of(context).send,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
onPressed: () => send(context),
),
],
),
),
),
),
);
}
Future<void> send(BuildContext context) async {
try {
await lightningSendViewModel.processInput(context, bolt11Controller.text);
} catch (e) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: e.toString(),
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
}
}
Future<bool> _onNavigateBack(BuildContext context) async {
onClose(context);
return false;
}
void _setEffects(BuildContext context) {
if (_effectsInstalled) {
return;
}
_effectsInstalled = true;
}
}

View file

@ -90,14 +90,22 @@ class ConnectionSyncPage extends BasePage {
}), }),
], ],
], ],
SettingsCellWithArrow( Observer(
title: S.current.manage_nodes, builder: (context) {
handler: (context) => Navigator.of(context).pushNamed(Routes.manageNodes), if (!dashboardViewModel.hasNodes) return const SizedBox();
return Column(
children: [
SettingsCellWithArrow(
title: S.current.manage_nodes,
handler: (context) => Navigator.of(context).pushNamed(Routes.manageNodes),
),
],
);
},
), ),
Observer( Observer(
builder: (context) { builder: (context) {
if (!dashboardViewModel.hasPowNodes) return const SizedBox(); if (!dashboardViewModel.hasPowNodes) return const SizedBox();
return Column( return Column(
children: [ children: [
SettingsCellWithArrow( SettingsCellWithArrow(

View file

@ -96,6 +96,7 @@ class WalletListBody extends StatefulWidget {
class WalletListBodyState extends State<WalletListBody> { class WalletListBodyState extends State<WalletListBody> {
final moneroIcon = Image.asset('assets/images/monero_logo.png', height: 24, width: 24); final moneroIcon = Image.asset('assets/images/monero_logo.png', height: 24, width: 24);
final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24); final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
final lightningIcon = Image.asset('assets/images/lightning_logo.png', height: 24, width: 24);
final tBitcoinIcon = Image.asset('assets/images/tbtc.png', height: 24, width: 24); final tBitcoinIcon = Image.asset('assets/images/tbtc.png', height: 24, width: 24);
final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24); final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24);
final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24); final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24);
@ -309,6 +310,8 @@ class WalletListBodyState extends State<WalletListBody> {
return tBitcoinIcon; return tBitcoinIcon;
} }
return bitcoinIcon; return bitcoinIcon;
case WalletType.lightning:
return lightningIcon;
case WalletType.monero: case WalletType.monero:
return moneroIcon; return moneroIcon;
case WalletType.litecoin: case WalletType.litecoin:

View file

@ -10,6 +10,7 @@ class AlertWithTwoActions extends BaseAlertDialog {
required this.rightButtonText, required this.rightButtonText,
required this.actionLeftButton, required this.actionLeftButton,
required this.actionRightButton, required this.actionRightButton,
this.contentWidget,
this.alertBarrierDismissible = true, this.alertBarrierDismissible = true,
this.isDividerExist = false, this.isDividerExist = false,
// this.leftActionColor, // this.leftActionColor,
@ -20,6 +21,7 @@ class AlertWithTwoActions extends BaseAlertDialog {
final String alertContent; final String alertContent;
final String leftButtonText; final String leftButtonText;
final String rightButtonText; final String rightButtonText;
final Widget? contentWidget;
final VoidCallback actionLeftButton; final VoidCallback actionLeftButton;
final VoidCallback actionRightButton; final VoidCallback actionRightButton;
final bool alertBarrierDismissible; final bool alertBarrierDismissible;
@ -47,4 +49,9 @@ class AlertWithTwoActions extends BaseAlertDialog {
// Color get rightButtonColor => rightActionColor; // Color get rightButtonColor => rightActionColor;
@override @override
bool get isDividerExists => isDividerExist; bool get isDividerExists => isDividerExist;
@override
Widget content(BuildContext context) {
return contentWidget ?? super.content(context);
}
} }

View file

@ -3,6 +3,7 @@ import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart'; import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/utils/exception_handler.dart'; import 'package:cake_wallet/utils/exception_handler.dart';
import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cw_core/balance.dart'; import 'package:cw_core/balance.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
@ -37,7 +38,8 @@ abstract class AppStoreBase with Store {
@action @action
Future<void> changeCurrentWallet( Future<void> changeCurrentWallet(
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet) async { WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet) async {
this.wallet?.close(); bool switchingToSameWalletType = this.wallet?.type == wallet.type;
this.wallet?.close(switchingToSameWalletType: switchingToSameWalletType);
this.wallet = wallet; this.wallet = wallet;
this.wallet!.setExceptionHandler(ExceptionHandler.onError); this.wallet!.setExceptionHandler(ExceptionHandler.onError);

View file

@ -956,6 +956,7 @@ abstract class SettingsStoreBase with Store {
if (bitcoinElectrumServer != null) { if (bitcoinElectrumServer != null) {
nodes[WalletType.bitcoin] = bitcoinElectrumServer; nodes[WalletType.bitcoin] = bitcoinElectrumServer;
nodes[WalletType.lightning] = bitcoinElectrumServer;
} }
if (litecoinElectrumServer != null) { if (litecoinElectrumServer != null) {
@ -1320,6 +1321,7 @@ abstract class SettingsStoreBase with Store {
if (bitcoinElectrumServer != null) { if (bitcoinElectrumServer != null) {
nodes[WalletType.bitcoin] = bitcoinElectrumServer; nodes[WalletType.bitcoin] = bitcoinElectrumServer;
nodes[WalletType.lightning] = bitcoinElectrumServer;
} }
if (litecoinElectrumServer != null) { if (litecoinElectrumServer != null) {

View file

@ -48,6 +48,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
case WalletType.haven: case WalletType.haven:
case WalletType.nano: case WalletType.nano:
case WalletType.banano: case WalletType.banano:
case WalletType.lightning:
return false; return false;
} }
} }

View file

@ -65,7 +65,9 @@ abstract class BalanceViewModelBase with Store {
@computed @computed
double get price { double get price {
final price = fiatConvertationStore.prices[appStore.wallet!.currency]; CryptoCurrency currency = appStore.wallet!.currency;
final price = fiatConvertationStore.prices[currency];
if (price == null) { if (price == null) {
// price should update on next fetch: // price should update on next fetch:
@ -153,6 +155,8 @@ abstract class BalanceViewModelBase with Store {
case WalletType.nano: case WalletType.nano:
case WalletType.banano: case WalletType.banano:
return S.current.receivable_balance; return S.current.receivable_balance;
case WalletType.lightning:
return S.current.max_receivable;
default: default:
return S.current.unconfirmed; return S.current.unconfirmed;
} }
@ -249,6 +253,7 @@ abstract class BalanceViewModelBase with Store {
asset: key, asset: key,
formattedAssetTitle: _formatterAsset(key))); formattedAssetTitle: _formatterAsset(key)));
} }
final fiatCurrency = settingsStore.fiatCurrency; final fiatCurrency = settingsStore.fiatCurrency;
final price = fiatConvertationStore.prices[key] ?? 0; final price = fiatConvertationStore.prices[key] ?? 0;
@ -262,7 +267,7 @@ abstract class BalanceViewModelBase with Store {
' ' + ' ' +
_getFiatBalance(price: price, cryptoAmount: value.formattedAdditionalBalance)); _getFiatBalance(price: price, cryptoAmount: value.formattedAdditionalBalance));
final availableFiatBalance = isFiatDisabled var availableFiatBalance = isFiatDisabled
? '' ? ''
: (fiatCurrency.toString() + : (fiatCurrency.toString() +
' ' + ' ' +
@ -397,6 +402,7 @@ abstract class BalanceViewModelBase with Store {
} }
String _getFiatBalance({required double price, String? cryptoAmount}) { String _getFiatBalance({required double price, String? cryptoAmount}) {
cryptoAmount = cryptoAmount?.replaceAll(',', '');// fix for amounts > 1000
if (cryptoAmount == null || cryptoAmount.isEmpty || double.tryParse(cryptoAmount) == null) { if (cryptoAmount == null || cryptoAmount.isEmpty || double.tryParse(cryptoAmount) == null) {
return '0.00'; return '0.00';
} }

View file

@ -1,5 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'package:breez_sdk/bridge_generated.dart';
import 'package:cake_wallet/buy/buy_provider.dart'; import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/core/key_service.dart'; import 'package:cake_wallet/core/key_service.dart';
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
@ -29,6 +30,7 @@ import 'package:cake_wallet/view_model/dashboard/formatted_item_list.dart';
import 'package:cake_wallet/view_model/dashboard/order_list_item.dart'; import 'package:cake_wallet/view_model/dashboard/order_list_item.dart';
import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart'; import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart';
import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart'; import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart';
import 'package:cake_wallet/view_model/lightning_view_model.dart';
import 'package:cake_wallet/view_model/settings/sync_mode.dart'; import 'package:cake_wallet/view_model/settings/sync_mode.dart';
import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cryptography/cryptography.dart'; import 'package:cryptography/cryptography.dart';
@ -54,19 +56,20 @@ part 'dashboard_view_model.g.dart';
class DashboardViewModel = DashboardViewModelBase with _$DashboardViewModel; class DashboardViewModel = DashboardViewModelBase with _$DashboardViewModel;
abstract class DashboardViewModelBase with Store { abstract class DashboardViewModelBase with Store {
DashboardViewModelBase( DashboardViewModelBase({
{required this.balanceViewModel, required this.balanceViewModel,
required this.appStore, required this.appStore,
required this.tradesStore, required this.tradesStore,
required this.tradeFilterStore, required this.tradeFilterStore,
required this.transactionFilterStore, required this.transactionFilterStore,
required this.settingsStore, required this.settingsStore,
required this.yatStore, required this.yatStore,
required this.ordersStore, required this.ordersStore,
required this.anonpayTransactionsStore, required this.anonpayTransactionsStore,
required this.sharedPreferences, required this.sharedPreferences,
required this.keyService}) required this.keyService,
: hasSellAction = false, required this.lightningViewModel,
}) : hasSellAction = false,
hasBuyAction = false, hasBuyAction = false,
hasExchangeAction = false, hasExchangeAction = false,
isShowFirstYatIntroduction = false, isShowFirstYatIntroduction = false,
@ -345,6 +348,8 @@ abstract class DashboardViewModelBase with Store {
TransactionFilterStore transactionFilterStore; TransactionFilterStore transactionFilterStore;
LightningViewModel lightningViewModel;
Map<String, List<FilterItem>> filterItems; Map<String, List<FilterItem>> filterItems;
BuyProvider? get defaultBuyProvider => ProvidersHelper.getProviderByType( BuyProvider? get defaultBuyProvider => ProvidersHelper.getProviderByType(
@ -383,7 +388,12 @@ abstract class DashboardViewModelBase with Store {
void furtherShowYatPopup(bool shouldShow) => settingsStore.shouldShowYatPopup = shouldShow; void furtherShowYatPopup(bool shouldShow) => settingsStore.shouldShowYatPopup = shouldShow;
@computed @computed
bool get isEnabledExchangeAction => settingsStore.exchangeStatus != ExchangeApiMode.disabled; bool get isEnabledExchangeAction {
if (wallet.type == WalletType.lightning) {
return false;
}
return settingsStore.exchangeStatus != ExchangeApiMode.disabled;
}
@observable @observable
bool hasExchangeAction; bool hasExchangeAction;
@ -407,9 +417,26 @@ abstract class DashboardViewModelBase with Store {
ReactionDisposer? _onMoneroBalanceChangeReaction; ReactionDisposer? _onMoneroBalanceChangeReaction;
@computed
bool get hasNodes => wallet.type != WalletType.lightning;
@computed @computed
bool get hasPowNodes => wallet.type == WalletType.nano || wallet.type == WalletType.banano; bool get hasPowNodes => wallet.type == WalletType.nano || wallet.type == WalletType.banano;
String get serviceMessage {
if (wallet.type == WalletType.lightning) {
final serviceStatus = lightningViewModel.serviceHealthCheck();
if (serviceStatus == HealthCheckStatus.ServiceDisruption) {
return S.current.breez_warning_disruption;
} else if (serviceStatus == HealthCheckStatus.Maintenance) {
return S.current.breez_warning_maintenance;
}
return "";
}
return "";
}
bool get showRepWarning { bool get showRepWarning {
if (wallet.type != WalletType.nano) { if (wallet.type != WalletType.nano) {
return false; return false;

View file

@ -1,4 +1,5 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/lightning/lightning.dart';
import 'package:cw_core/receive_page_option.dart'; import 'package:cw_core/receive_page_option.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
@ -16,14 +17,28 @@ abstract class ReceiveOptionViewModelBase with Store {
: ReceivePageOption.mainnet), : ReceivePageOption.mainnet),
_options = [] { _options = [] {
final walletType = _wallet.type; final walletType = _wallet.type;
_options = walletType == WalletType.haven
? [ReceivePageOption.mainnet] switch (walletType) {
: walletType == WalletType.bitcoin case WalletType.haven:
? [ _options = [ReceivePageOption.mainnet];
...bitcoin!.getBitcoinReceivePageOptions(), break;
...ReceivePageOptions.where((element) => element != ReceivePageOption.mainnet) case WalletType.lightning:
] _options = [...lightning!.getLightningReceivePageOptions()];
: ReceivePageOptions; break;
case WalletType.bitcoin:
_options = [
...bitcoin!.getBitcoinReceivePageOptions(),
...ReceivePageOptions.where((element) => element != ReceivePageOption.mainnet)
];
break;
default:
_options = [
ReceivePageOption.mainnet,
ReceivePageOption.anonPayDonationLink,
ReceivePageOption.anonPayInvoice
];
break;
}
} }
final WalletBase _wallet; final WalletBase _wallet;

View file

@ -2,6 +2,7 @@ import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/lightning/lightning.dart';
import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart'; import 'package:cake_wallet/reactions/wallet_connect.dart';
@ -122,6 +123,11 @@ class TransactionListItem extends ActionListItem with Keyable {
cryptoAmount: bitcoin!.formatterBitcoinAmountToDouble(amount: transaction.amount), cryptoAmount: bitcoin!.formatterBitcoinAmountToDouble(amount: transaction.amount),
price: price); price: price);
break; break;
case WalletType.lightning:
amount = calculateFiatAmountRaw(
cryptoAmount: lightning!.formatterLightningAmountToDouble(amount: transaction.amount),
price: price);
break;
case WalletType.haven: case WalletType.haven:
final asset = haven!.assetOfTransaction(transaction); final asset = haven!.assetOfTransaction(transaction);
final price = balanceViewModel.fiatConvertationStore.prices[asset]; final price = balanceViewModel.fiatConvertationStore.prices[asset];

View file

@ -174,6 +174,11 @@ abstract class ExchangeTradeViewModelBase with Store {
} }
static bool _checkIfCanSend(TradesStore tradesStore, WalletBase wallet) { static bool _checkIfCanSend(TradesStore tradesStore, WalletBase wallet) {
if (wallet.currency == CryptoCurrency.btcln) {
return false;
}
bool _isEthToken() => bool _isEthToken() =>
wallet.currency == CryptoCurrency.eth && wallet.currency == CryptoCurrency.eth &&
tradesStore.trade!.from.tag == CryptoCurrency.eth.title; tradesStore.trade!.from.tag == CryptoCurrency.eth.title;

View file

@ -278,12 +278,16 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
bool get hasAllAmount => bool get hasAllAmount =>
(wallet.type == WalletType.bitcoin || (wallet.type == WalletType.bitcoin ||
wallet.type == WalletType.lightning ||
wallet.type == WalletType.litecoin || wallet.type == WalletType.litecoin ||
wallet.type == WalletType.bitcoinCash) && wallet.type == WalletType.bitcoinCash) &&
depositCurrency == wallet.currency; depositCurrency == wallet.currency;
bool get isMoneroWallet => wallet.type == WalletType.monero; bool get isMoneroWallet => wallet.type == WalletType.monero;
// lightning doesn't have the same concept of "addresses" (since it uses invoices)
bool get hasAddress => wallet.type != WalletType.lightning;
bool get isLowFee { bool get isLowFee {
switch (wallet.type) { switch (wallet.type) {
case WalletType.monero: case WalletType.monero:
@ -539,6 +543,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
/// return after the first successful trade /// return after the first successful trade
return; return;
} catch (e) { } catch (e) {
print(e);
continue; continue;
} }
} }
@ -651,6 +656,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
depositCurrency = CryptoCurrency.btc; depositCurrency = CryptoCurrency.btc;
receiveCurrency = CryptoCurrency.xmr; receiveCurrency = CryptoCurrency.xmr;
break; break;
case WalletType.lightning:
depositCurrency = CryptoCurrency.btcln;
receiveCurrency = CryptoCurrency.xmr;
break;
case WalletType.litecoin: case WalletType.litecoin:
depositCurrency = CryptoCurrency.ltc; depositCurrency = CryptoCurrency.ltc;
receiveCurrency = CryptoCurrency.xmr; receiveCurrency = CryptoCurrency.xmr;

View file

@ -0,0 +1,130 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/view_model/lightning_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/currency.dart';
import 'package:cw_core/receive_page_option.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:mobx/mobx.dart';
import 'package:shared_preferences/shared_preferences.dart';
part 'lightning_invoice_page_view_model.g.dart';
class LightningInvoicePageViewModel = LightningInvoicePageViewModelBase
with _$LightningInvoicePageViewModel;
abstract class LightningInvoicePageViewModelBase with Store {
LightningInvoicePageViewModelBase(
this.settingsStore,
this._wallet,
this.sharedPreferences,
this.lightningViewModel,
) : description = '',
amount = '',
state = InitialExecutionState(),
selectedCurrency = walletTypeToCryptoCurrency(_wallet.type),
cryptoCurrency = walletTypeToCryptoCurrency(_wallet.type) {
_fetchLimits();
}
List<Currency> get currencies => [walletTypeToCryptoCurrency(_wallet.type), ...FiatCurrency.all];
final SettingsStore settingsStore;
final WalletBase _wallet;
final SharedPreferences sharedPreferences;
final LightningViewModel lightningViewModel;
@observable
Currency selectedCurrency;
CryptoCurrency cryptoCurrency;
@observable
String description;
@observable
String amount;
@observable
ExecutionState state;
@computed
int get selectedCurrencyIndex => currencies.indexOf(selectedCurrency);
@observable
double? minimum;
@observable
double? maximum;
@action
void selectCurrency(Currency currency) {
selectedCurrency = currency;
maximum = minimum = null;
if (currency is CryptoCurrency) {
cryptoCurrency = currency;
} else {
cryptoCurrency = walletTypeToCryptoCurrency(_wallet.type);
}
_fetchLimits();
}
@action
Future<void> createInvoice() async {
state = IsExecutingState();
if (amount.isNotEmpty) {
final amountInCrypto = double.tryParse(amount);
if (amountInCrypto == null) {
state = FailureState('Amount is invalid');
return;
}
if (minimum != null && amountInCrypto < minimum!) {
state = FailureState('Amount is too small');
return;
}
if (amountInCrypto > 4000000) {
state = FailureState('Amount is too big');
return;
}
}
try {
String bolt11 = await lightningViewModel.createInvoice(
amountSats: amount,
description: description,
);
state = ExecutedSuccessfullyState(payload: bolt11);
} catch (e) {
state = FailureState(e.toString());
}
}
@action
void setRequestParams({
required String inputAmount,
required String inputDescription,
}) {
description = inputDescription;
amount = inputAmount;
}
@action
Future<void> _fetchLimits() async {
final limits = await lightningViewModel.invoiceSoftLimitsSats();
minimum = limits.minFee.toDouble();
maximum = limits.inboundLiquidity.toDouble();
}
@action
void reset() {
selectedCurrency = walletTypeToCryptoCurrency(_wallet.type);
cryptoCurrency = walletTypeToCryptoCurrency(_wallet.type);
description = '';
amount = '';
try {
_fetchLimits();
} catch (_) {}
}
}

View file

@ -0,0 +1,93 @@
import 'dart:async';
import 'package:breez_sdk/breez_sdk.dart';
import 'package:breez_sdk/bridge_generated.dart' as BZG;
import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/lightning/lightning.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/widgets.dart';
import 'package:mobx/mobx.dart';
part 'lightning_send_view_model.g.dart';
class LightningSendViewModel = LightningSendViewModelBase with _$LightningSendViewModel;
abstract class LightningSendViewModelBase with Store {
LightningSendViewModelBase({
required this.settingsStore,
required this.fiatConversionStore,
}) {}
final SettingsStore settingsStore;
final FiatConversionStore fiatConversionStore;
@observable
bool loading = false;
@action
void setLoading(bool value) {
loading = value;
}
FiatCurrency get fiat => settingsStore.fiatCurrency;
String formattedFiatAmount(int sats) {
String amount = calculateFiatAmountRaw(
cryptoAmount: lightning!.formatterLightningAmountToDouble(amount: sats),
price: fiatConversionStore.prices[CryptoCurrency.btcln],
);
return amount;
}
@action
Future<void> send(BZG.LNInvoice invoice, int satAmount) async {
try {
setLoading(true);
final sdk = await BreezSDK();
late BZG.SendPaymentRequest req;
if (invoice.amountMsat == null) {
req = BZG.SendPaymentRequest(
bolt11: invoice.bolt11,
amountMsat: satAmount * 1000,
);
} else {
req = BZG.SendPaymentRequest(bolt11: invoice.bolt11);
}
await sdk.sendPayment(req: req);
setLoading(false);
} catch (e) {
setLoading(false);
}
}
@action
Future<void> processInput(BuildContext context, String input) async {
FocusScope.of(context).unfocus();
final sdk = await BreezSDK();
late BZG.InputType inputType;
try {
inputType = await sdk.parseInput(input: input);
} catch (_) {
throw Exception("Unknown input type");
}
if (inputType is BZG.InputType_Bolt11) {
final bolt11 = await sdk.parseInvoice(input);
Navigator.of(context).pushNamed(Routes.lightningSendConfirm, arguments: bolt11);
} else if (inputType is BZG.InputType_LnUrlPay) {
throw Exception("Unsupported input type");
} else {
throw Exception("Unknown input type");
}
}
}

View file

@ -0,0 +1,130 @@
import 'dart:async';
import 'package:breez_sdk/breez_sdk.dart';
import 'package:breez_sdk/bridge_generated.dart' as BZG;
import 'package:mobx/mobx.dart';
import 'package:cw_lightning/.secrets.g.dart' as secrets;
part 'lightning_view_model.g.dart';
class LightningViewModel = LightningViewModelBase with _$LightningViewModel;
abstract class LightningViewModelBase with Store {
LightningViewModelBase() {}
Future<ReceiveOnchainResult> receiveOnchain() async {
final sdk = await BreezSDK();
BZG.ReceiveOnchainRequest req = const BZG.ReceiveOnchainRequest();
BZG.SwapInfo swapInfo = await sdk.receiveOnchain(req: req);
print("Minimum amount allowed to deposit in sats: ${swapInfo.minAllowedDeposit}");
print("Maximum amount allowed to deposit in sats: ${swapInfo.maxAllowedDeposit}");
int fee = 0;
double feePercent = 0;
try {
final nodeState = (await sdk.nodeInfo())!;
int inboundLiquidity = nodeState.inboundLiquidityMsats ~/ 1000;
final openingFees = await sdk.openChannelFee(
req: BZG.OpenChannelFeeRequest(amountMsat: inboundLiquidity + 1));
feePercent = (openingFees.feeParams.proportional * 100) / 1000000;
fee = openingFees.feeParams.minMsat ~/ 1000;
} catch (_) {}
return ReceiveOnchainResult(
bitcoinAddress: swapInfo.bitcoinAddress,
minAllowedDeposit: swapInfo.minAllowedDeposit,
maxAllowedDeposit: swapInfo.maxAllowedDeposit,
feePercent: feePercent,
fee: fee,
);
}
Future<String> createInvoice({required String amountSats, String? description}) async {
final sdk = await BreezSDK();
final req = BZG.ReceivePaymentRequest(
amountMsat: (double.parse(amountSats) * 1000).round(),
description: description ?? '',
);
final res = await sdk.receivePayment(req: req);
return res.lnInvoice.bolt11;
}
Future<InvoiceSoftLimitsResult> invoiceSoftLimitsSats() async {
double feePercent = 0.4;
int minFee = (2500 * 1000) ~/ 1000; // 2500 sats
int inboundLiquidity = 1000000000 * 1000 * 10; // 10 BTC
int balance = 0;
final sdk = await BreezSDK();
try {
final nodeState = (await sdk.nodeInfo())!;
inboundLiquidity = nodeState.inboundLiquidityMsats ~/ 1000;
final openingFees = await sdk.openChannelFee(
req: BZG.OpenChannelFeeRequest(amountMsat: inboundLiquidity + 1));
feePercent = (openingFees.feeParams.proportional * 100) / 1000000;
minFee = openingFees.feeParams.minMsat ~/ 1000;
balance = nodeState.channelsBalanceMsat ~/ 1000;
} catch (_) {}
return InvoiceSoftLimitsResult(
minFee: minFee,
inboundLiquidity: inboundLiquidity,
feePercent: feePercent,
balance: balance,
);
}
Future<int> getBalanceSats() async {
try {
final sdk = await BreezSDK();
final nodeState = (await sdk.nodeInfo())!;
return nodeState.channelsBalanceMsat ~/ 1000;
} catch (_) {
return 0;
}
}
Future<BZG.HealthCheckStatus> serviceHealthCheck() async {
try {
final sdk = await BreezSDK();
BZG.ServiceHealthCheckResponse response =
await sdk.serviceHealthCheck(apiKey: secrets.breezApiKey);
return response.status;
} catch (_) {
return BZG.HealthCheckStatus.ServiceDisruption;
}
}
}
class ReceiveOnchainResult {
final String bitcoinAddress;
final int minAllowedDeposit;
final int maxAllowedDeposit;
final int fee;
final double feePercent;
ReceiveOnchainResult({
required this.bitcoinAddress,
required this.minAllowedDeposit,
required this.maxAllowedDeposit,
required this.fee,
required this.feePercent,
});
}
class InvoiceSoftLimitsResult {
final double feePercent;
final int minFee;
final int inboundLiquidity;
final int balance;
InvoiceSoftLimitsResult({
required this.inboundLiquidity,
required this.feePercent,
required this.minFee,
required this.balance,
});
}

View file

@ -85,6 +85,7 @@ abstract class NodeCreateOrEditViewModelBase with Store {
case WalletType.litecoin: case WalletType.litecoin:
case WalletType.bitcoinCash: case WalletType.bitcoinCash:
case WalletType.bitcoin: case WalletType.bitcoin:
case WalletType.lightning:
return false; return false;
} }
} }

View file

@ -58,6 +58,9 @@ abstract class NodeListViewModelBase with Store {
node = getBitcoinDefaultElectrumServer(nodes: _nodeSource)!; node = getBitcoinDefaultElectrumServer(nodes: _nodeSource)!;
} }
break; break;
case WalletType.lightning:
node = getBitcoinDefaultElectrumServer(nodes: _nodeSource)!;
break;
case WalletType.monero: case WalletType.monero:
node = getMoneroDefaultNode(nodes: _nodeSource); node = getMoneroDefaultNode(nodes: _nodeSource);
break; break;

View file

@ -85,6 +85,7 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
spendKey: restoreWallet.spendKey ?? '', spendKey: restoreWallet.spendKey ?? '',
height: restoreWallet.height ?? 0); height: restoreWallet.height ?? 0);
case WalletType.bitcoin: case WalletType.bitcoin:
case WalletType.lightning:
case WalletType.litecoin: case WalletType.litecoin:
return bitcoin!.createBitcoinRestoreWalletFromWIFCredentials( return bitcoin!.createBitcoinRestoreWalletFromWIFCredentials(
name: name, password: password, wif: wif); name: name, password: password, wif: wif);

View file

@ -22,6 +22,9 @@ class WalletRestoreFromQRCode {
'bitcoin': WalletType.bitcoin, 'bitcoin': WalletType.bitcoin,
'bitcoin-wallet': WalletType.bitcoin, 'bitcoin-wallet': WalletType.bitcoin,
'bitcoin_wallet': WalletType.bitcoin, 'bitcoin_wallet': WalletType.bitcoin,
'lightning': WalletType.lightning,
'lightning-wallet': WalletType.lightning,
'lightning_wallet': WalletType.lightning,
'litecoin': WalletType.litecoin, 'litecoin': WalletType.litecoin,
'litecoin-wallet': WalletType.litecoin, 'litecoin-wallet': WalletType.litecoin,
'litecoin_wallet': WalletType.litecoin, 'litecoin_wallet': WalletType.litecoin,

View file

@ -58,6 +58,9 @@ abstract class TransactionDetailsViewModelBase with Store {
case WalletType.bitcoinCash: case WalletType.bitcoinCash:
_addElectrumListItems(tx, dateFormat); _addElectrumListItems(tx, dateFormat);
break; break;
case WalletType.lightning:
_addLightningListItems(tx, dateFormat);
break;
case WalletType.haven: case WalletType.haven:
_addHavenListItems(tx, dateFormat); _addHavenListItems(tx, dateFormat);
break; break;
@ -100,14 +103,16 @@ abstract class TransactionDetailsViewModelBase with Store {
final type = wallet.type; final type = wallet.type;
items.add(BlockExplorerListItem( if (_explorerDescription(type) != '') {
title: S.current.view_in_block_explorer, items.add(BlockExplorerListItem(
value: _explorerDescription(type), title: S.current.view_in_block_explorer,
onTap: () { value: _explorerDescription(type),
try { onTap: () {
launch(_explorerUrl(type, tx.id)); try {
} catch (e) {} launch(_explorerUrl(type, tx.id));
})); } catch (e) {}
}));
}
final description = transactionDescriptionBox.values.firstWhere( final description = transactionDescriptionBox.values.firstWhere(
(val) => val.id == transactionInfo.id, (val) => val.id == transactionInfo.id,
@ -262,6 +267,19 @@ abstract class TransactionDetailsViewModelBase with Store {
items.addAll(_items); items.addAll(_items);
} }
void _addLightningListItems(TransactionInfo tx, DateFormat dateFormat) {
final _items = [
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(
title: S.current.transaction_details_date, value: dateFormat.format(tx.date)),
StandartListItem(title: S.current.transaction_details_amount, value: tx.amountFormatted()),
if (tx.feeFormatted()?.isNotEmpty ?? false)
StandartListItem(title: S.current.transaction_details_fee, value: tx.feeFormatted()!),
];
items.addAll(_items);
}
void _addHavenListItems(TransactionInfo tx, DateFormat dateFormat) { void _addHavenListItems(TransactionInfo tx, DateFormat dateFormat) {
items.addAll([ items.addAll([
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id), StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id),

View file

@ -83,6 +83,16 @@ class BitcoinURI extends PaymentURI {
} }
} }
class LightningURI extends PaymentURI {
LightningURI({required String amount, required String address})
: super(amount: amount, address: address);
@override
String toString() {
throw Exception('N/A for lightning wallets (need to make a bolt11 invoice).');
}
}
class LitecoinURI extends PaymentURI { class LitecoinURI extends PaymentURI {
LitecoinURI({required String amount, required String address}) LitecoinURI({required String amount, required String address})
: super(amount: amount, address: address); : super(amount: amount, address: address);
@ -282,6 +292,10 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
return BitcoinURI(amount: amount, address: address.address); return BitcoinURI(amount: amount, address: address.address);
} }
if (wallet.type == WalletType.lightning) {
return LightningURI(amount: amount, address: address.address);
}
if (wallet.type == WalletType.litecoin) { if (wallet.type == WalletType.litecoin) {
return LitecoinURI(amount: amount, address: address.address); return LitecoinURI(amount: amount, address: address.address);
} }

View file

@ -104,6 +104,7 @@ abstract class WalletCreationVMBase with Store {
derivationType: DerivationType.nano, derivationType: DerivationType.nano,
); );
case WalletType.bitcoin: case WalletType.bitcoin:
case WalletType.lightning:
case WalletType.litecoin: case WalletType.litecoin:
return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first; return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first;
default: default:
@ -118,6 +119,7 @@ abstract class WalletCreationVMBase with Store {
derivationType: DerivationType.nano, derivationType: DerivationType.nano,
); );
case WalletType.bitcoin: case WalletType.bitcoin:
case WalletType.lightning:
return DerivationInfo( return DerivationInfo(
derivationType: DerivationType.bip39, derivationType: DerivationType.bip39,
derivationPath: "m/84'/0'/0'/0", derivationPath: "m/84'/0'/0'/0",

View file

@ -21,6 +21,7 @@ class WalletKeysViewModel = WalletKeysViewModelBase with _$WalletKeysViewModel;
abstract class WalletKeysViewModelBase with Store { abstract class WalletKeysViewModelBase with Store {
WalletKeysViewModelBase(this._appStore) WalletKeysViewModelBase(this._appStore)
: title = _appStore.wallet!.type == WalletType.bitcoin || : title = _appStore.wallet!.type == WalletType.bitcoin ||
_appStore.wallet!.type == WalletType.lightning ||
_appStore.wallet!.type == WalletType.litecoin || _appStore.wallet!.type == WalletType.litecoin ||
_appStore.wallet!.type == WalletType.bitcoinCash _appStore.wallet!.type == WalletType.bitcoinCash
? S.current.wallet_seed ? S.current.wallet_seed
@ -163,6 +164,7 @@ abstract class WalletKeysViewModelBase with Store {
} }
if (_appStore.wallet!.type == WalletType.bitcoin || if (_appStore.wallet!.type == WalletType.bitcoin ||
_appStore.wallet!.type == WalletType.lightning ||
_appStore.wallet!.type == WalletType.litecoin || _appStore.wallet!.type == WalletType.litecoin ||
_appStore.wallet!.type == WalletType.bitcoinCash) { _appStore.wallet!.type == WalletType.bitcoinCash) {
// final keys = bitcoin!.getWalletKeys(_appStore.wallet!); // final keys = bitcoin!.getWalletKeys(_appStore.wallet!);
@ -247,6 +249,8 @@ abstract class WalletKeysViewModelBase with Store {
return 'banano-wallet'; return 'banano-wallet';
case WalletType.polygon: case WalletType.polygon:
return 'polygon-wallet'; return 'polygon-wallet';
case WalletType.lightning:
return 'lightning-wallet';
case WalletType.solana: case WalletType.solana:
return 'solana-wallet'; return 'solana-wallet';
case WalletType.tron: case WalletType.tron:

View file

@ -1,5 +1,6 @@
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart'; import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/lightning/lightning.dart';
import 'package:cake_wallet/solana/solana.dart'; import 'package:cake_wallet/solana/solana.dart';
import 'package:cake_wallet/tron/tron.dart'; import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cake_wallet/wownero/wownero.dart';
@ -69,6 +70,8 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
name: name, language: options!.first as String, isPolyseed: options.last as bool); name: name, language: options!.first as String, isPolyseed: options.last as bool);
case WalletType.bitcoin: case WalletType.bitcoin:
return bitcoin!.createBitcoinNewWalletCredentials(name: name); return bitcoin!.createBitcoinNewWalletCredentials(name: name);
case WalletType.lightning:
return bitcoin!.createBitcoinNewWalletCredentials(name: name);
case WalletType.litecoin: case WalletType.litecoin:
return bitcoin!.createBitcoinNewWalletCredentials(name: name); return bitcoin!.createBitcoinNewWalletCredentials(name: name);
case WalletType.haven: case WalletType.haven:

View file

@ -108,6 +108,14 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
derivationType: derivationInfo!.derivationType!, derivationType: derivationInfo!.derivationType!,
derivationPath: derivationInfo.derivationPath!, derivationPath: derivationInfo.derivationPath!,
); );
case WalletType.lightning:
return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials(
name: name,
mnemonic: seed,
password: password,
derivationType: derivationInfo!.derivationType!,
derivationPath: derivationInfo.derivationPath!,
);
case WalletType.haven: case WalletType.haven:
return haven!.createHavenRestoreWalletFromSeedCredentials( return haven!.createHavenRestoreWalletFromSeedCredentials(
name: name, height: height, mnemonic: seed, password: password); name: name, height: height, mnemonic: seed, password: password);

View file

@ -2,6 +2,7 @@ cd cw_core; flutter pub get; flutter packages pub run build_runner build --delet
cd cw_evm; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. cd cw_evm; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd ..
cd cw_monero; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. cd cw_monero; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd ..
cd cw_bitcoin; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. cd cw_bitcoin; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd ..
cd cw_lightning && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
cd cw_haven; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. cd cw_haven; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd ..
cd cw_nano; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. cd cw_nano; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd ..
cd cw_bitcoin_cash; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. cd cw_bitcoin_cash; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd ..

View file

@ -12,7 +12,7 @@ dependencies:
version: 4.0.2 version: 4.0.2
shared_preferences: ^2.0.15 shared_preferences: ^2.0.15
# provider: ^6.0.3 # provider: ^6.0.3
rxdart: ^0.27.4 rxdart: ^0.28.0
yaml: ^3.1.1 yaml: ^3.1.1
#barcode_scan: any #barcode_scan: any
barcode_scan2: ^4.2.1 barcode_scan2: ^4.2.1
@ -107,14 +107,20 @@ dependencies:
ref: cake-update-v3 ref: cake-update-v3
ledger_flutter: ^1.0.1 ledger_flutter: ^1.0.1
breez_sdk:
git:
url: https://github.com/breez/breez-sdk-flutter.git
ref: v0.4.3-rc1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
build_runner: ^2.3.3 build_runner: ^2.4.7
logging: ^1.2.0 logging: ^1.2.0
mobx_codegen: ^2.1.1 mobx_codegen: ^2.1.1
build_resolvers: ^2.0.9 build_resolvers: ^2.0.9
hive_generator: ^1.1.3 hive_generator: ^2.0.1
# flutter_launcher_icons: ^0.11.0 # flutter_launcher_icons: ^0.11.0
# check flutter_launcher_icons for usage # check flutter_launcher_icons for usage
pedantic: ^1.8.0 pedantic: ^1.8.0
@ -128,6 +134,8 @@ dependency_overrides:
bech32: bech32:
git: git:
url: https://github.com/cake-tech/bech32.git url: https://github.com/cake-tech/bech32.git
flutter_rust_bridge: ^1.82.6
uuid: ^4.1.0
ledger_flutter: ledger_flutter:
git: git:
url: https://github.com/cake-tech/ledger-flutter.git url: https://github.com/cake-tech/ledger-flutter.git

View file

@ -81,6 +81,8 @@
"bitcoin_payments_require_1_confirmation": "تتطلب مدفوعات Bitcoin تأكيدًا واحدًا ، والذي قد يستغرق 20 دقيقة أو أكثر. شكرا لصبرك! سيتم إرسال بريد إلكتروني إليك عند تأكيد الدفع.", "bitcoin_payments_require_1_confirmation": "تتطلب مدفوعات Bitcoin تأكيدًا واحدًا ، والذي قد يستغرق 20 دقيقة أو أكثر. شكرا لصبرك! سيتم إرسال بريد إلكتروني إليك عند تأكيد الدفع.",
"block_remaining": "1 كتلة متبقية", "block_remaining": "1 كتلة متبقية",
"Blocks_remaining": "بلوك متبقي ${status}", "Blocks_remaining": "بلوك متبقي ${status}",
"breez_warning_disruption": "خدمة Breez تواجه حاليًا قضايا. الرجاء معاودة المحاولة في وقت لاحق.",
"breez_warning_maintenance": "تمر خدمة Breez حاليًا بالصيانة. الرجاء معاودة المحاولة في وقت لاحق.",
"bluetooth": "بلوتوث", "bluetooth": "بلوتوث",
"bright_theme": "مشرق", "bright_theme": "مشرق",
"bump_fee": "رسوم عثرة", "bump_fee": "رسوم عثرة",
@ -337,6 +339,8 @@
"inputs": "المدخلات", "inputs": "المدخلات",
"introducing_cake_pay": "نقدم لكم Cake Pay!", "introducing_cake_pay": "نقدم لكم Cake Pay!",
"invalid_input": "مدخل غير صالح", "invalid_input": "مدخل غير صالح",
"invoice": "فاتورة",
"invoice_created": "الفاتورة التي تم إنشاؤها",
"invoice_details": "تفاصيل الفاتورة", "invoice_details": "تفاصيل الفاتورة",
"is_percentage": "يكون", "is_percentage": "يكون",
"last_30_days": "آخر 30 يومًا", "last_30_days": "آخر 30 يومًا",
@ -347,6 +351,10 @@
"ledger_error_wrong_app": "يرجى التأكد", "ledger_error_wrong_app": "يرجى التأكد",
"ledger_please_enable_bluetooth": "يرجى تمكين البلوتوث للكشف عن دفتر الأستاذ الخاص بك", "ledger_please_enable_bluetooth": "يرجى تمكين البلوتوث للكشف عن دفتر الأستاذ الخاص بك",
"light_theme": "فاتح", "light_theme": "فاتح",
"lightning_invoice_min": "<سيتم تطبيق رسوم الإعداد ${feePercent} ٪ مع ما لا يقل عن ${min} SATs عند تلقي هذه الفاتورة",
"lightning_invoice_min_max": "سيتم تطبيق رسوم إعداد ${feePercent} ٪ مع ما لا يقل عن ${min} SATs لتلقي أكثر من ${max} SATs",
"lightning_invoice_warning": "يجب أن تبقي التطبيق مفتوحًا حتى يتم الانتهاء من الدفع أو تفشل المعاملة",
"lightning_receive_limits": "أرسل أكثر من ${min} SATs وحتى ${max} SATs إلى هذا العنوان. سيتم تطبيق رسوم إعداد ${feePercent} ٪ مع ما لا يقل عن ${fee} SATs عند تلقي هذه الفاتورة. سيؤدي ذلك إلى تحويل أي Bitcoin المستلم إلى Lightning. سيتم تطبيق رسوم على السلسلة. لا يمكن استخدام هذا العنوان إلا مرة واحدة.",
"load_more": "تحميل المزيد", "load_more": "تحميل المزيد",
"loading_your_wallet": "يتم تحميل محفظتك", "loading_your_wallet": "يتم تحميل محفظتك",
"login": "تسجيل الدخول", "login": "تسجيل الدخول",
@ -360,6 +368,7 @@
"market_place": "منصة التجارة", "market_place": "منصة التجارة",
"matrix_green_dark_theme": "موضوع ماتريكس الأخضر الداكن", "matrix_green_dark_theme": "موضوع ماتريكس الأخضر الداكن",
"max_amount": "الحد الأقصى: ${value}", "max_amount": "الحد الأقصى: ${value}",
"max_receivable": "ماكس القبض",
"max_value": "الحد الأقصى: ${value} ${currency}", "max_value": "الحد الأقصى: ${value} ${currency}",
"memo": "مذكرة:", "memo": "مذكرة:",
"message": "ﺔﻟﺎﺳﺭ", "message": "ﺔﻟﺎﺳﺭ",

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