Cw 453 (#1306)

* feat: rebase btc-addr-types, migrate to bitcoin_base * feat: allow scanning elect-rs using get_tweaks * feat: scanning and adding addresses working with getTweaks, add btc SP address type * chore: pubspec.lock * chore: pubspec.lock * fix: scan when switching, fix multiple unspents in same tx * fix: initial scan * fix: initial scan * fix: scanning issues * fix: sync, storing silent unspents * chore: deps * fix: label issues, clear spent utxo * chore: deps * fix: build * fix: missing types * feat: new electrs API & changes, fixes for last block scanning * feat: Scan Silent Payments homepage toggle * chore: build configure * feat: generic fixes, testnet UI improvements, useSSL on bitcoin nodes * fix: invalid Object in sendData * feat: improve addresses page & address book displays * feat: silent payments labeled addresses disclaimer * fix: missing i18n * chore: print * feat: single block scan, rescan by date working for btc mainnet * feat: new cake features page replace market page, move sp scan toggle, auto switch node pop up alert * feat: delete silent addresses * fix: red dot in non ssl nodes * fix: inconsistent connection states, fix tx history * fix: tx & balance displays, cpfp sending * feat: new rust lib * chore: node path * fix: check node based on network * fix: missing txcount from addresses * style: padding in feature page cards * fix: restore not getting all wallet addresses by type * fix: auto switch node broken * fix: silent payment txs not being restored * feat: change scanning to subscription model, sync improvements * fix: scan re-subscription * fix: default nodes * fix: improve scanning by date, fix single block scan * refactor: common function for input tx selection * fix: nodes & build * fix: send all with multiple outs * refactor: unchanged file * Update pr_test_build.yml * chore: upgrade * chore: merge changes * refactor: unchanged files [skip ci] * fix: scan fixes, add date, allow sending while scanning * feat: sync fixes, sp settings * feat: fix resyncing * fix: date from height logic, status disconnected & chain tip get * fix: params * feat: electrum migration if using cake electrum * fix nodes update versions * re-enable tron * update sp_scanner to work on iOS [skip ci] * fix: wrong socket for old electrum nodes * Fix unchecked wallet type call * fix: double balance * feat: node domain * fix: menu name * fix: update tip on set scanning * fix: connection switching back and forth * feat: check if node is electrs, and supports sp * chore: fix build * minor enhancements * fixes and enhancements * solve conflicts with main * fix: status toggle * minor enhancement * Monero.com fixes * update sp_scanner to include windows and linux --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>

Rafael committed May 29, 2024 at 11:43 UTC 96b9b60f50bf115cc7ec1e88472ae7f007d80df8
122 files changed +3879 -1242
.github/workflows/pr_test_build.yml
+1 -1
@@ -42,7 +42,7 @@ jobs:
42 - name: Flutter action
43 uses: subosito/flutter-action@v1
44 with:
45 - flutter-version: "3.19.5"
45 + flutter-version: "3.19.6"
46 channel: stable
47
48 - name: Install package dependencies
assets/images/cards.svg new
+65
@@ -0,0 +1,65 @@
1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2 +<!-- Created with Inkscape (http://www.inkscape.org/) -->
3 +
4 +<svg
5 + width="500mm"
6 + height="500mm"
7 + viewBox="0 0 500 500"
8 + version="1.1"
9 + id="svg1"
10 + xml:space="preserve"
11 + inkscape:export-filename="cards.png"
12 + inkscape:export-xdpi="25.4"
13 + inkscape:export-ydpi="25.4"
14 + inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
15 + sodipodi:docname="cards.svg"
16 + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
17 + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
18 + xmlns:xlink="http://www.w3.org/1999/xlink"
19 + xmlns="http://www.w3.org/2000/svg"
20 + xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
21 + id="namedview1"
22 + pagecolor="#ffffff"
23 + bordercolor="#000000"
24 + borderopacity="0.25"
25 + inkscape:showpageshadow="2"
26 + inkscape:pageopacity="0.0"
27 + inkscape:pagecheckerboard="0"
28 + inkscape:deskcolor="#d1d1d1"
29 + inkscape:document-units="mm"
30 + inkscape:zoom="0.37701202"
31 + inkscape:cx="924.37372"
32 + inkscape:cy="962.83403"
33 + inkscape:window-width="1682"
34 + inkscape:window-height="1064"
35 + inkscape:window-x="26"
36 + inkscape:window-y="23"
37 + inkscape:window-maximized="0"
38 + inkscape:current-layer="layer1" /><defs
39 + id="defs1" /><g
40 + inkscape:label="Layer 1"
41 + inkscape:groupmode="layer"
42 + id="layer1"><image
43 + width="323.74396"
44 + height="202.87956"
45 + preserveAspectRatio="none"
46 + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAC8CAYAAADCbJOCAAAABHNCSVQICAgIfAhkiAAAIABJREFU&#10;eJzt3Xmc3VV9//HXOef7vfvsM5nJQgIJSVgDQRFRZFEWMSJqjQtUrdhSC/qrVlwqtqK2VWlLVVRK&#10;lVasWpUqiiCCgKAIsiSBkAaSQAhJyJ5Z7/79fs/5/fG9985MSCAJCcllPs/HY5LJzF0nd973fD/n&#10;c85XsSunn+5x990hwNz5Z00JXXWBgjOdYz7OTUWR2eV1hRDieTgoKNSzKL3IafsbV/V+9fSyOzcD&#10;sHCh4YYbop1dTz3P19ysE04/XDn+GucWKqV7AZyzOOf2y5MQQkwcSmmUUjjAOfusUvrHVpuvrV50&#10;x1pAQ/ytcdfZ4TY0YAFmH3/6ZQ7+Xhnd4qII56yt381OrieEEHvKgbOAUkprbTysjQYV6nOrlvz2&#10;67XLKMaE1tjg0YCdN+/sbFFXv6+NeWsUBjjnQqWUQUJKCLH/OOdcpLX2tDa4yP4oGOIDa9bcXWbM&#10;QKoeQgrgqKNOz5YT7hbPmFPDIAyUwkOCSgjx0nHOucjzE561wW1hm37rmrtPr8IVDnAaUCxcqAGq&#10;iegHnvFODYMgUAofCSshxEtLKaW8MKgGWvvnmAH3X3CFrWeUqVfkZx1/2ieN8T8ch5XyD/SjFkJM&#10;XEopYyMbeJ5/XEfv9K39d/3qQRYuNApQs4895TCrzTJwCeLjRRlZCSEONKeUcjhGNObolY/etUED&#10;zuJ90ngm7eJ+BQkrIcTBQFlrrfa8tkiFHwWcmjv/rCmRrTzmlOog7q+SwBJCHCycUgpn7eZKNjxa&#10;h7ZyntKmE2dldCWEONgo56xTxvSlSslzNaiz2UlHqRBCHCScQjlnOUuDO945q0DpA/2ohBDiuZRy&#10;zim0na/BTXFSuxLiJaGUanzsyXV2/vUXuh57fF8v/Fhe+H53vPy+uFvnHMoxzQOV2ic3KYTYJaM1&#10;DgjCEBtZjNF4ngeArS/TBbSuH+jEJWXnHFHt8koprLUYY4iiCGsdnmew1o3bkEArhdKaMAwJwxCl&#10;FL7vobUmikY3QXihMNvZ46pf3xizk8uMLjN2zqG1IgwjQOF5Ztx97yEFDudIe3t7C0KI3aO1Zngk&#10;j9aars4OMpk0hUKRbf0DGK3JZNJYG++CMpIvoADjGZxzKBS5XJZ8oQAOUqkkg4NDtLTm8IzH4NAw&#10;mXS6FlxxmJUrFcrlCp3tbXR3dRKGIdu2D1CuVGhtycW3qxSVSoUwqgfO2BK2AudIpZJordFaUygW&#10;sZGltTUHKIZHRlBK0ZLNElmLUopiqYyNLChIJhKUymXaWluw1jE4NExLLvuif5YSWELsR0op8vkC&#10;bzzzNN5/wTuYffihtORyjOTzPPZ/K7j62u/y6GOPk0olSaWSfPnzn2ZSdye/+8OD3HPfg1zx6b9m&#10;2tTJbNy0hS9e+XUWPbKMT33sr3jzG19Pwk9w7x8f4stXXUOhWCSRSFAoFJkxfRqX/sV7OemV8+nu&#10;6iAIAtau38D/3HATP/rpTWQyGYZH8rznHefxljedTRAGGK0xOh41Ka2olCt89h/+ha3b+ikWS7zy&#10;hHl86KILmTt7JgCrnlrDt6//Eff98WHa21sZHs5z8Qcu4LRTTmJgYJAvXnk1l//Fh3nDaa/FWssv&#10;b72Tr13zn42R2V7/PGcdd6rMDgqxHxhjGBwa4sJ3vo2r/umzO73MSL7A+e/+c55Y+RTTpk7m3tv/&#10;l4Tvs/LJp2nJZZncN6lx2VWr13DPvQ/w5+9717jbuOnWO/irj12O7/lMnTyJG753DVMm9+70/v7+&#10;H6/iuv/+MdVqwLeu+iIL37pgl4//ze+8iD/88WHOO/dMvnP1V0gkxq/Ys9byoY9dzi2/vgvrHP99&#10;7VWcecYp5PMFfnffg7zp7DPGXf6LV36dq6+9no72tr07PHSUTGffjCv2/JpCiOejlCIIArq7uvjx&#10;9d/AGEM+X+Bvr7iSa//zh8ydM5Purg7SqRTDIwVuv+t3dHd1svBtC0gmEvR0d1KpVnlk6XKmTpmM&#10;c47uzg5OOO4Yli1fwdDwCF2dHURRxLQpk7nxl7excfNWrv7nzzN/3tEAXP/Dn/K3n/sK1WrAvGOO&#10;AGDqlF5+9NNfolBEkWV4ZIQlS5fzx4cW0zupm1w23kj4ydVruPra62ltyfH9//gq7W2tFApFPv+V&#10;r/Hzm2/j1SfOJ5NJc8rJJ3LjzbfRPzDIgnNez+EzD0UpOHLu4dz3wCKU1mQzaRzQ1tLCDTfegucZ&#10;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&#10;"
47 + id="image1-6"
48 + x="132.5432"
49 + y="52.500706"
50 + inkscape:label="Amazon" /><image
51 + width="327.47772"
52 + height="206.18967"
53 + preserveAspectRatio="none"
54 + xlink:href="data:image/png;base64,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&#10;9azrZmRkaMnSpVqydKmb0p2/rKwsLV68WIv/nte0SpUquqRxY0VFRSk7O1sJCQn6c+PGQt+aNCYm&#10;RhN/+klVY2PPua7FYtGr//2vjqWmatyPPxa43uGUFI2fMEHjJ0yQxWJR48aNVbNGDYWEhCg1NVXb&#10;ExK0adMmOQtx33ubzaZ3RoxQn969C/X1+IpjR49q586dpmMAPocyCrjRjh07dPhwsmIqFTxO0Bt8&#10;/tlnun/IEP30888lup+mTZro0KFDSiqm8Y116tRRUlLSOa/U37t3r/bu3Xte+6hZs6a+//Zb1a5V&#10;q9DviYiI0EcffKCcnBxNnDTpnOs7nU6tX79e69evL3K+oKAgvTt6tG6/7bYCx7L6quTkZO324qE0&#10;gLdizCjgRna7XQkleC93d4mOitInH32k2/r3L5GjvDabTXfdeac+//RThYSEFNt2W7dqpSmTJ6tB&#10;/frFXsQsFos6duigKb/8ohbNmxf5/aGhofrmq6/0+GOPKTo6uliz/aNevXr66osvdNcdd1BE87F9&#10;23aupAcMoIwCbrZ18xbTEYpFeHi4Pv74Y33+6adnnRC/qMqXL69PP/lEH3/4oSIiIoptu/+4vF07&#10;zZg2TXfecYf8/f2LZZsRERF64fnn9esvv6h27drnvZ2goCC9+sormjxpki5u1KhYskmSv82mG66/&#10;XrOmT9f1111XbNstbbZsKR2fTcDbUEYBN/vrr79MRyg2Af7+uvGGG7R8yRK9+PzzhRojWZCKFSvq&#10;+Wef1YJ583Rb//6yWkvuv6eYmBh98N57mjZ1qq7u1eu8S2lkZKQeffhhzZ09W88984yCgoIuOJvV&#10;alXrVq204Pff9dWXX6pNmzby8/M7r22FhITommuu0W9Tp+rbr79WzFmmkYK06a+NpiMAPokxo4Cb&#10;bS2FR18qV66sZ595Rtddd53mz5+v6TNmaNny5ee8RWhwcLDatG6tTp06qVfPnqpbp45sNttpyzu0&#10;b5/v0Uarn5/Cw8PPeL1KlSoFTqd0SePGrsf+/v7q0L69mjVrpiVLlmj6jBmaPn26du/ZI4fDUWDm&#10;gIAANW/eXN26dFHnzp3VtEmTYjvCeqqwsDDdesst6tK5s1bFx2vy5MlavnKlNm/efNaLlAL8/XVJ&#10;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&#10;V6vj47Vo4SKlph5z/fuqUKGCnnvhedfR0Kzjx/XFZ59rzJjvTxve0LBRIz362FBd0b69645kg+4f&#10;rNde+a+OHz9e4L4BeCbKKOChYir/v4ymHE4559jMt0eOLNL2Tx1nWCU2VrfdfrtrnOTBgwd15+13&#10;aEdCwhnv2/jnn3r2qac1d84cvTNqlIKCghQeHq6XXnlFt/fvX+SLStpdfrmaNmsm6WTpnjrl1zPW&#10;Wb5smeLjV+nKzidPZXfv1VM//PDDaUfmCrLxz40act9gZWdnu17Lzs7WjOnTtW/fPv00aaKsVqus&#10;Vqvad+ioeXPnyeFw6KMPPtRlbdvKYrEoOjpaV7RvrzHff3/G9i0Wi67u3dv1fPu2bZo5c+Zp2bv9&#10;fZvO2NhYNW3WTAsXLHAt97PZXGNi//neWa1WtWzVUhGRkUo9pWxWr1HDdVTR4XBo2dJl+c6o4HQ6&#10;9evkyRr1zjunzZyQkpKiTz/+RFarnx4Z+qgsFov8/f3VvmMH/TBmrKSTN0kY8uADriEae/fu1dCH&#10;H9bq+NVn7GfhggX6Y8MGffH112p8SWNJ0qOPDdX03347rdyeym636/tvv9WwN948LZvVatWNN/VT&#10;zVq1JJ0s048PfUwzpk8/Yxsb//xTg+65V8Pfflu9el8tSbr2uuv0+9y5mjN7Tr77BeC5OE0PeCib&#10;3/9/V8xz5J1z/T7X9C3Sn1MNuOduXVS2rKSTJWfwwEH5FtF/2O12zZ09RxPGj3eVodZtWqtlq1ZF&#10;/jp79+kji8UiSVq2dKniV8Xnu78pv/6/pNauXVs9e/U6Y738vDd69GlF9FQb1q/XHxs2uJ7Xql1L&#10;kZGRkqSVK1Zo/bp1rmXXXn+dypU/c2xig4YN1aZNG9fzab9NO61Azp8/3/U4LDz8jDG2V3a+UuXK&#10;l5ckxa9a5Sp9/v7+6nDKKXlJan3Kfg4eOKDFp8zDeqrk5GSN+e67fKfwcjgcmvDjj6ed6m5x6f/H&#10;sl5+xRVq0+Yy1/PPPvk03yL6j6NHj+q/L7/s2l5gYKC69+hR4PqrVq7U6JGjzsgWV726bujXz/X8&#10;l0mT8i2i/8jNzdWIt97SH3/fCMHf3189e11d4PoAPBdlFPBQaWlprscRf49lLAnh/ypI69au1Yb1&#10;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&#10;/wd2gKJuj5U9TgAAAABJRU5ErkJggg==&#10;"
55 + id="image1"
56 + x="8.2439814"
57 + y="113.07963"
58 + inkscape:label="Uber" /><image
59 + width="346.95718"
60 + height="218.58302"
61 + preserveAspectRatio="none"
62 + xlink:href="data:image/png;base64,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&#10;hmRjR5IYrTOtXD2uDnlwMo64efYWi7vZaq1OqVT2wrpSrfHkA/fwtm99m+xf/C7Z6poVVHuRyU9y&#10;uPnPVteo3Pw+0+nTkL2Ih9r/8V9gUdeoT7p5lvsPak4+/GHOnltleWkJnw4Ru5Fis3yPDyv4HB3C&#10;ip8jqCS7uMuVKgsLi6ytrbK7s2PZHCPpNx6NuXHjuqXqnZ0jLRaouaJl7a6RZXlUeW8/Z5ot5ubm&#10;KVcqtKemvKk1NzfLr/3qZ3ju+b/l2WefpVwuc8/dpzl37jz9/oBE22TF973vcTo7Ozz/t99HJ5or&#10;V69x6dJlDLC1tcUjDz/EmbMvREMY8mF0ojHjvRo1GmvpjxSNi2BFFZ0r8UdBqFq7Is7dLgNnmsQa&#10;u1qv0Wq3EbVhN7TyjK7D0YhXXnmZ69evhcky+eSGVkoUmUUH2OtobNeXjY0NVxsmAlijdI7K7fha&#10;QBUSEGO/kxdqSnwtt6Is1KS5OGEii7b0/hm5zUiw+kG06wKFSzpNABuMYAgjK+nkVI/Q7cdtIKHf&#10;63Lj+nVL4yumvoyRmyF5LqUUU9MzliVTKTKEDJJw30p8mPFewKNajOSV5S7PiCCI7CC6MbIYWBtR&#10;YsahTruKjME3hRCTNk1T+16eU66U+dAD93D3U19i8NSX7bUjgf5mHYWjisIdO4wuGJJekfTIGH5C&#10;8PaGh1uf2bW/5t2zG/yv/82n+O//rz/l/PlzaJ3YrtLAhIS65eWk9NJhavd8lz/ZEnTNze2jPTXt&#10;+pFFmElZXuylpRtcv36VXrfnw6JSi1YslSiXK1RrVWr1OvV6g3q9Tq1ed6yKlq96ZmaaX/6lf8TL&#10;L7/CU1/8C89VvX9xgRdfehmtbDutT3z8o+RZxh/84b+n0+mgUHbC3aItlUoMhuJ4jcy5SNBYig/J&#10;qI+Ekx+PyPW3R+vYyA9hk7rXWZ6zs7PDcDTyCEMr7X0d+/ZZlkp/OYdOkkTT7w948cUXuH79ehjX&#10;PEdaRMWTFEcxBQ0JNc7y0pLNeVJxe7KIbFHvEUJK2bIbn1MVUJRs4DjvKlAi7vXlRYvMvxahpwXQ&#10;+bEyxtIU227M1tGcpgmF1FIEC8WyoFhx8INFuTudDteuXqXf70ZCU0fjEsSbUoqpqWmmp2ccCpQx&#10;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&#10;K91hZLThAAAAAElFTkSuQmCC&#10;"
63 + id="image1-2"
64 + x="136.44643"
65 + y="238.98746" /></g></svg>
assets/images/tbtc.png
Binary files /dev/null and b/assets/images/tbtc.png differ
assets/text/Release_Notes.txt
+2 -3
@@ -1,3 +1,2 @@
1 -Add Tron wallet
2 -Hardware wallets enhancements
3 -Bug fixes
\ No newline at end of file
1 +Bitcoin Silent Payments
2 +Bug fixes and generic enhancements
cw_bitcoin/lib/bitcoin_address_record.dart
+102 -30
@@ -3,8 +3,8 @@ import 'dart:convert';
3 import 'package:bitcoin_base/bitcoin_base.dart';
4 import 'package:cw_bitcoin/script_hash.dart' as sh;
5
6 -class BitcoinAddressRecord {
7 - BitcoinAddressRecord(
6 +abstract class BaseBitcoinAddressRecord {
7 + BaseBitcoinAddressRecord(
8 this.address, {
9 required this.index,
10 this.isHidden = false,
@@ -13,36 +13,14 @@ class BitcoinAddressRecord {
13 String name = '',
14 bool isUsed = false,
15 required this.type,
16 - String? scriptHash,
16 required this.network,
17 }) : _txCount = txCount,
18 _balance = balance,
19 _name = name,
21 - _isUsed = isUsed,
22 - scriptHash = scriptHash ?? sh.scriptHash(address, network: network);
23 -
24 - factory BitcoinAddressRecord.fromJSON(String jsonSource, BasedUtxoNetwork network) {
25 - final decoded = json.decode(jsonSource) as Map;
26 -
27 - return BitcoinAddressRecord(
28 - decoded['address'] as String,
29 - index: decoded['index'] as int,
30 - isHidden: decoded['isHidden'] as bool? ?? false,
31 - isUsed: decoded['isUsed'] as bool? ?? false,
32 - txCount: decoded['txCount'] as int? ?? 0,
33 - name: decoded['name'] as String? ?? '',
34 - balance: decoded['balance'] as int? ?? 0,
35 - type: decoded['type'] != null && decoded['type'] != ''
36 - ? BitcoinAddressType.values
37 - .firstWhere((type) => type.toString() == decoded['type'] as String)
38 - : SegwitAddresType.p2wpkh,
39 - scriptHash: decoded['scriptHash'] as String?,
40 - network: network,
41 - );
42 - }
20 + _isUsed = isUsed;
21
22 @override
45 - bool operator ==(Object o) => o is BitcoinAddressRecord && address == o.address;
23 + bool operator ==(Object o) => o is BaseBitcoinAddressRecord && address == o.address;
24
25 final String address;
26 bool isHidden;
@@ -51,8 +29,7 @@ class BitcoinAddressRecord {
29 int _balance;
30 String _name;
31 bool _isUsed;
54 - String? scriptHash;
55 - BasedUtxoNetwork network;
32 + BasedUtxoNetwork? network;
33
34 int get txCount => _txCount;
35
@@ -69,16 +46,57 @@ class BitcoinAddressRecord {
46 void setAsUsed() => _isUsed = true;
47 void setNewName(String label) => _name = label;
48
72 - @override
49 int get hashCode => address.hashCode;
50
51 BitcoinAddressType type;
52
77 - String updateScriptHash(BasedUtxoNetwork network) {
53 + String toJSON();
54 +}
55 +
56 +class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
57 + BitcoinAddressRecord(
58 + super.address, {
59 + required super.index,
60 + super.isHidden = false,
61 + super.txCount = 0,
62 + super.balance = 0,
63 + super.name = '',
64 + super.isUsed = false,
65 + required super.type,
66 + String? scriptHash,
67 + required super.network,
68 + }) : scriptHash =
69 + scriptHash ?? (network != null ? sh.scriptHash(address, network: network) : null);
70 +
71 + factory BitcoinAddressRecord.fromJSON(String jsonSource, {BasedUtxoNetwork? network}) {
72 + final decoded = json.decode(jsonSource) as Map;
73 +
74 + return BitcoinAddressRecord(
75 + decoded['address'] as String,
76 + index: decoded['index'] as int,
77 + isHidden: decoded['isHidden'] as bool? ?? false,
78 + isUsed: decoded['isUsed'] as bool? ?? false,
79 + txCount: decoded['txCount'] as int? ?? 0,
80 + name: decoded['name'] as String? ?? '',
81 + balance: decoded['balance'] as int? ?? 0,
82 + type: decoded['type'] != null && decoded['type'] != ''
83 + ? BitcoinAddressType.values
84 + .firstWhere((type) => type.toString() == decoded['type'] as String)
85 + : SegwitAddresType.p2wpkh,
86 + scriptHash: decoded['scriptHash'] as String?,
87 + network: network,
88 + );
89 + }
90 +
91 + String? scriptHash;
92 +
93 + String getScriptHash(BasedUtxoNetwork network) {
94 + if (scriptHash != null) return scriptHash!;
95 scriptHash = sh.scriptHash(address, network: network);
96 return scriptHash!;
97 }
98
99 + @override
100 String toJSON() => json.encode({
101 'address': address,
102 'index': index,
@@ -91,3 +109,57 @@ class BitcoinAddressRecord {
109 'scriptHash': scriptHash,
110 });
111 }
112 +
113 +class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord {
114 + BitcoinSilentPaymentAddressRecord(
115 + super.address, {
116 + required super.index,
117 + super.isHidden = false,
118 + super.txCount = 0,
119 + super.balance = 0,
120 + super.name = '',
121 + super.isUsed = false,
122 + required this.silentPaymentTweak,
123 + required super.network,
124 + required super.type,
125 + }) : super();
126 +
127 + factory BitcoinSilentPaymentAddressRecord.fromJSON(String jsonSource,
128 + {BasedUtxoNetwork? network}) {
129 + final decoded = json.decode(jsonSource) as Map;
130 +
131 + return BitcoinSilentPaymentAddressRecord(
132 + decoded['address'] as String,
133 + index: decoded['index'] as int,
134 + isHidden: decoded['isHidden'] as bool? ?? false,
135 + isUsed: decoded['isUsed'] as bool? ?? false,
136 + txCount: decoded['txCount'] as int? ?? 0,
137 + name: decoded['name'] as String? ?? '',
138 + balance: decoded['balance'] as int? ?? 0,
139 + network: (decoded['network'] as String?) == null
140 + ? network
141 + : BasedUtxoNetwork.fromName(decoded['network'] as String),
142 + silentPaymentTweak: decoded['silent_payment_tweak'] as String?,
143 + type: decoded['type'] != null && decoded['type'] != ''
144 + ? BitcoinAddressType.values
145 + .firstWhere((type) => type.toString() == decoded['type'] as String)
146 + : SilentPaymentsAddresType.p2sp,
147 + );
148 + }
149 +
150 + final String? silentPaymentTweak;
151 +
152 + @override
153 + String toJSON() => json.encode({
154 + 'address': address,
155 + 'index': index,
156 + 'isHidden': isHidden,
157 + 'isUsed': isUsed,
158 + 'txCount': txCount,
159 + 'name': name,
160 + 'balance': balance,
161 + 'type': type.toString(),
162 + 'network': network?.value,
163 + 'silent_payment_tweak': silentPaymentTweak,
164 + });
165 +}
cw_bitcoin/lib/bitcoin_receive_page_option.dart
+23
@@ -8,6 +8,8 @@ class BitcoinReceivePageOption implements ReceivePageOption {
8 static const p2wsh = BitcoinReceivePageOption._('Segwit (P2WSH)');
9 static const p2pkh = BitcoinReceivePageOption._('Legacy (P2PKH)');
10
11 + static const silent_payments = BitcoinReceivePageOption._('Silent Payments');
12 +
13 const BitcoinReceivePageOption._(this.value);
14
15 final String value;
@@ -17,6 +19,7 @@ class BitcoinReceivePageOption implements ReceivePageOption {
19 }
20
21 static const all = [
22 + BitcoinReceivePageOption.silent_payments,
23 BitcoinReceivePageOption.p2wpkh,
24 BitcoinReceivePageOption.p2tr,
25 BitcoinReceivePageOption.p2wsh,
@@ -24,6 +27,24 @@ class BitcoinReceivePageOption implements ReceivePageOption {
27 BitcoinReceivePageOption.p2pkh
28 ];
29
30 + BitcoinAddressType toType() {
31 + switch (this) {
32 + case BitcoinReceivePageOption.p2tr:
33 + return SegwitAddresType.p2tr;
34 + case BitcoinReceivePageOption.p2wsh:
35 + return SegwitAddresType.p2wsh;
36 + case BitcoinReceivePageOption.p2pkh:
37 + return P2pkhAddressType.p2pkh;
38 + case BitcoinReceivePageOption.p2sh:
39 + return P2shAddressType.p2wpkhInP2sh;
40 + case BitcoinReceivePageOption.silent_payments:
41 + return SilentPaymentsAddresType.p2sp;
42 + case BitcoinReceivePageOption.p2wpkh:
43 + default:
44 + return SegwitAddresType.p2wpkh;
45 + }
46 + }
47 +
48 factory BitcoinReceivePageOption.fromType(BitcoinAddressType type) {
49 switch (type) {
50 case SegwitAddresType.p2tr:
@@ -34,6 +55,8 @@ class BitcoinReceivePageOption implements ReceivePageOption {
55 return BitcoinReceivePageOption.p2pkh;
56 case P2shAddressType.p2wpkhInP2sh:
57 return BitcoinReceivePageOption.p2sh;
58 + case SilentPaymentsAddresType.p2sp:
59 + return BitcoinReceivePageOption.silent_payments;
60 case SegwitAddresType.p2wpkh:
61 default:
62 return BitcoinReceivePageOption.p2wpkh;
cw_bitcoin/lib/bitcoin_unspent.dart
+57 -4
@@ -2,13 +2,66 @@ import 'package:cw_bitcoin/bitcoin_address_record.dart';
2 import 'package:cw_core/unspent_transaction_output.dart';
3
4 class BitcoinUnspent extends Unspent {
5 - BitcoinUnspent(BitcoinAddressRecord addressRecord, String hash, int value, int vout)
5 + BitcoinUnspent(BaseBitcoinAddressRecord addressRecord, String hash, int value, int vout)
6 : bitcoinAddressRecord = addressRecord,
7 super(addressRecord.address, hash, value, vout, null);
8
9 - factory BitcoinUnspent.fromJSON(BitcoinAddressRecord address, Map<String, dynamic> json) =>
9 + factory BitcoinUnspent.fromJSON(BaseBitcoinAddressRecord? address, Map<String, dynamic> json) =>
10 BitcoinUnspent(
11 - address, json['tx_hash'] as String, json['value'] as int, json['tx_pos'] as int);
11 + address ?? BitcoinAddressRecord.fromJSON(json['address_record'].toString()),
12 + json['tx_hash'] as String,
13 + json['value'] as int,
14 + json['tx_pos'] as int,
15 + );
16
13 - final BitcoinAddressRecord bitcoinAddressRecord;
17 + Map<String, dynamic> toJson() {
18 + final json = <String, dynamic>{
19 + 'address_record': bitcoinAddressRecord.toJSON(),
20 + 'tx_hash': hash,
21 + 'value': value,
22 + 'tx_pos': vout,
23 + };
24 + return json;
25 + }
26 +
27 + final BaseBitcoinAddressRecord bitcoinAddressRecord;
28 +}
29 +
30 +class BitcoinSilentPaymentsUnspent extends BitcoinUnspent {
31 + BitcoinSilentPaymentsUnspent(
32 + BitcoinSilentPaymentAddressRecord addressRecord,
33 + String hash,
34 + int value,
35 + int vout, {
36 + required this.silentPaymentTweak,
37 + required this.silentPaymentLabel,
38 + }) : super(addressRecord, hash, value, vout);
39 +
40 + @override
41 + factory BitcoinSilentPaymentsUnspent.fromJSON(
42 + BitcoinSilentPaymentAddressRecord? address, Map<String, dynamic> json) =>
43 + BitcoinSilentPaymentsUnspent(
44 + address ?? BitcoinSilentPaymentAddressRecord.fromJSON(json['address_record'].toString()),
45 + json['tx_hash'] as String,
46 + json['value'] as int,
47 + json['tx_pos'] as int,
48 + silentPaymentTweak: json['silent_payment_tweak'] as String?,
49 + silentPaymentLabel: json['silent_payment_label'] as String?,
50 + );
51 +
52 + @override
53 + Map<String, dynamic> toJson() {
54 + final json = <String, dynamic>{
55 + 'address_record': bitcoinAddressRecord.toJSON(),
56 + 'tx_hash': hash,
57 + 'value': value,
58 + 'tx_pos': vout,
59 + 'silent_payment_tweak': silentPaymentTweak,
60 + 'silent_payment_label': silentPaymentLabel,
61 + };
62 + return json;
63 + }
64 +
65 + String? silentPaymentTweak;
66 + String? silentPaymentLabel;
67 }
cw_bitcoin/lib/bitcoin_wallet.dart
+43 -23
@@ -39,22 +39,28 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
39 Map<String, int>? initialRegularAddressIndex,
40 Map<String, int>? initialChangeAddressIndex,
41 String? passphrase,
42 + List<BitcoinSilentPaymentAddressRecord>? initialSilentAddresses,
43 + int initialSilentAddressIndex = 0,
44 + bool? alwaysScan,
45 }) : super(
43 - mnemonic: mnemonic,
44 - passphrase: passphrase,
45 - xpub: xpub,
46 - password: password,
47 - walletInfo: walletInfo,
48 - unspentCoinsInfo: unspentCoinsInfo,
49 - networkType: networkParam == null
50 - ? bitcoin.bitcoin
51 - : networkParam == BitcoinNetwork.mainnet
46 + mnemonic: mnemonic,
47 + passphrase: passphrase,
48 + xpub: xpub,
49 + password: password,
50 + walletInfo: walletInfo,
51 + unspentCoinsInfo: unspentCoinsInfo,
52 + networkType: networkParam == null
53 ? bitcoin.bitcoin
53 - : bitcoin.testnet,
54 - initialAddresses: initialAddresses,
55 - initialBalance: initialBalance,
56 - seedBytes: seedBytes,
57 - currency: CryptoCurrency.btc) {
54 + : networkParam == BitcoinNetwork.mainnet
55 + ? bitcoin.bitcoin
56 + : bitcoin.testnet,
57 + initialAddresses: initialAddresses,
58 + initialBalance: initialBalance,
59 + seedBytes: seedBytes,
60 + currency:
61 + networkParam == BitcoinNetwork.testnet ? CryptoCurrency.tbtc : CryptoCurrency.btc,
62 + alwaysScan: alwaysScan,
63 + ) {
64 // in a standard BIP44 wallet, mainHd derivation path = m/84'/0'/0'/0 (account 0, index unspecified here)
65 // the sideHd derivation path = m/84'/0'/0'/1 (account 1, index unspecified here)
66 // String derivationPath = walletInfo.derivationInfo!.derivationPath!;
@@ -62,14 +68,18 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
68 // final hd = bitcoin.HDWallet.fromSeed(seedBytes, network: networkType);
69 walletAddresses = BitcoinWalletAddresses(
70 walletInfo,
65 - electrumClient: electrumClient,
71 initialAddresses: initialAddresses,
72 initialRegularAddressIndex: initialRegularAddressIndex,
73 initialChangeAddressIndex: initialChangeAddressIndex,
74 + initialSilentAddresses: initialSilentAddresses,
75 + initialSilentAddressIndex: initialSilentAddressIndex,
76 mainHd: hd,
77 sideHd: accountHD.derive(1),
78 network: networkParam ?? network,
79 + masterHd:
80 + seedBytes != null ? bitcoin.HDWallet.fromSeed(seedBytes, network: networkType) : null,
81 );
82 +
83 autorun((_) {
84 this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
85 });
@@ -84,9 +94,11 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
94 String? addressPageType,
95 BasedUtxoNetwork? network,
96 List<BitcoinAddressRecord>? initialAddresses,
97 + List<BitcoinSilentPaymentAddressRecord>? initialSilentAddresses,
98 ElectrumBalance? initialBalance,
99 Map<String, int>? initialRegularAddressIndex,
100 Map<String, int>? initialChangeAddressIndex,
101 + int initialSilentAddressIndex = 0,
102 }) async {
103 late Uint8List seedBytes;
104
@@ -109,6 +121,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
121 walletInfo: walletInfo,
122 unspentCoinsInfo: unspentCoinsInfo,
123 initialAddresses: initialAddresses,
124 + initialSilentAddresses: initialSilentAddresses,
125 + initialSilentAddressIndex: initialSilentAddressIndex,
126 initialBalance: initialBalance,
127 seedBytes: seedBytes,
128 initialRegularAddressIndex: initialRegularAddressIndex,
@@ -123,6 +137,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
137 required WalletInfo walletInfo,
138 required Box<UnspentCoinsInfo> unspentCoinsInfo,
139 required String password,
140 + required bool alwaysScan,
141 }) async {
142 final network = walletInfo.network != null
143 ? BasedUtxoNetwork.fromName(walletInfo.network!)
@@ -163,12 +178,15 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
178 walletInfo: walletInfo,
179 unspentCoinsInfo: unspentCoinsInfo,
180 initialAddresses: snp.addresses,
181 + initialSilentAddresses: snp.silentAddresses,
182 + initialSilentAddressIndex: snp.silentAddressIndex,
183 initialBalance: snp.balance,
184 seedBytes: seedBytes,
185 initialRegularAddressIndex: snp.regularAddressIndex,
186 initialChangeAddressIndex: snp.changeAddressIndex,
187 addressPageType: snp.addressPageType,
188 networkParam: network,
189 + alwaysScan: alwaysScan,
190 );
191 }
192
@@ -179,7 +197,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
197 void setLedger(Ledger setLedger, LedgerDevice setLedgerDevice) {
198 _ledger = setLedger;
199 _ledgerDevice = setLedgerDevice;
182 - _bitcoinLedgerApp = BitcoinLedgerApp(_ledger!, derivationPath: walletInfo.derivationInfo!.derivationPath!);
200 + _bitcoinLedgerApp =
201 + BitcoinLedgerApp(_ledger!, derivationPath: walletInfo.derivationInfo!.derivationPath!);
202 }
203
204 @override
@@ -202,16 +221,17 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
221 final publicKeyAndDerivationPath = publicKeys[utxo.ownerDetails.address.pubKeyHash()]!;
222
223 psbtReadyInputs.add(PSBTReadyUtxoWithAddress(
205 - utxo: utxo.utxo,
206 - rawTx: rawTx,
207 - ownerDetails: utxo.ownerDetails,
208 - ownerDerivationPath: publicKeyAndDerivationPath.derivationPath,
209 - ownerMasterFingerprint: masterFingerprint,
210 - ownerPublicKey: publicKeyAndDerivationPath.publicKey,
224 + utxo: utxo.utxo,
225 + rawTx: rawTx,
226 + ownerDetails: utxo.ownerDetails,
227 + ownerDerivationPath: publicKeyAndDerivationPath.derivationPath,
228 + ownerMasterFingerprint: masterFingerprint,
229 + ownerPublicKey: publicKeyAndDerivationPath.publicKey,
230 ));
231 }
232
214 - final psbt = PSBTTransactionBuild(inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF);
233 + final psbt =
234 + PSBTTransactionBuild(inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF);
235
236 final rawHex = await _bitcoinLedgerApp!.signPsbt(_ledgerDevice!, psbt: psbt.psbt);
237 return BtcTransaction.fromRaw(hex.encode(rawHex));
cw_bitcoin/lib/bitcoin_wallet_addresses.dart
+3 -1
@@ -15,10 +15,12 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
15 required super.mainHd,
16 required super.sideHd,
17 required super.network,
18 - required super.electrumClient,
18 super.initialAddresses,
19 super.initialRegularAddressIndex,
20 super.initialChangeAddressIndex,
21 + super.initialSilentAddresses,
22 + super.initialSilentAddressIndex = 0,
23 + super.masterHd,
24 }) : super(walletInfo);
25
26 @override
cw_bitcoin/lib/bitcoin_wallet_service.dart
+25 -17
@@ -19,10 +19,11 @@ class BitcoinWalletService extends WalletService<
19 BitcoinRestoreWalletFromSeedCredentials,
20 BitcoinRestoreWalletFromWIFCredentials,
21 BitcoinRestoreWalletFromHardware> {
22 - BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
22 + BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.alwaysScan);
23
24 final Box<WalletInfo> walletInfoSource;
25 final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
26 + final bool alwaysScan;
27
28 @override
29 WalletType getType() => WalletType.bitcoin;
@@ -55,20 +56,24 @@ class BitcoinWalletService extends WalletService<
56 .firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
57 try {
58 final wallet = await BitcoinWalletBase.open(
58 - password: password,
59 - name: name,
60 - walletInfo: walletInfo,
61 - unspentCoinsInfo: unspentCoinsInfoSource);
59 + password: password,
60 + name: name,
61 + walletInfo: walletInfo,
62 + unspentCoinsInfo: unspentCoinsInfoSource,
63 + alwaysScan: alwaysScan,
64 + );
65 await wallet.init();
66 saveBackup(name);
67 return wallet;
68 } catch (_) {
69 await restoreWalletFilesFromBackup(name);
70 final wallet = await BitcoinWalletBase.open(
68 - password: password,
69 - name: name,
70 - walletInfo: walletInfo,
71 - unspentCoinsInfo: unspentCoinsInfoSource);
71 + password: password,
72 + name: name,
73 + walletInfo: walletInfo,
74 + unspentCoinsInfo: unspentCoinsInfoSource,
75 + alwaysScan: alwaysScan,
76 + );
77 await wallet.init();
78 return wallet;
79 }
@@ -87,10 +92,12 @@ class BitcoinWalletService extends WalletService<
92 final currentWalletInfo = walletInfoSource.values
93 .firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
94 final currentWallet = await BitcoinWalletBase.open(
90 - password: password,
91 - name: currentName,
92 - walletInfo: currentWalletInfo,
93 - unspentCoinsInfo: unspentCoinsInfoSource);
95 + password: password,
96 + name: currentName,
97 + walletInfo: currentWalletInfo,
98 + unspentCoinsInfo: unspentCoinsInfoSource,
99 + alwaysScan: alwaysScan,
100 + );
101
102 await currentWallet.renameWalletFiles(newName);
103 await saveBackup(newName);
@@ -105,12 +112,13 @@ class BitcoinWalletService extends WalletService<
112 @override
113 Future<BitcoinWallet> restoreFromHardwareWallet(BitcoinRestoreWalletFromHardware credentials,
114 {bool? isTestnet}) async {
108 -
115 final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet;
116 credentials.walletInfo?.network = network.value;
111 - credentials.walletInfo?.derivationInfo?.derivationPath = credentials.hwAccountData.derivationPath;
117 + credentials.walletInfo?.derivationInfo?.derivationPath =
118 + credentials.hwAccountData.derivationPath;
119
113 - final wallet = await BitcoinWallet(password: credentials.password!,
120 + final wallet = await BitcoinWallet(
121 + password: credentials.password!,
122 xpub: credentials.hwAccountData.xpub,
123 walletInfo: credentials.walletInfo!,
124 unspentCoinsInfo: unspentCoinsInfoSource,
@@ -123,7 +131,7 @@ class BitcoinWalletService extends WalletService<
131
132 @override
133 Future<BitcoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials,
126 - {bool? isTestnet}) async =>
134 + {bool? isTestnet}) async =>
135 throw UnimplementedError();
136
137 @override
cw_bitcoin/lib/electrum.dart
+53 -15
@@ -41,23 +41,35 @@ class ElectrumClient {
41
42 bool get isConnected => _isConnected;
43 Socket? socket;
44 - void Function(bool)? onConnectionStatusChange;
44 + void Function(bool?)? onConnectionStatusChange;
45 int _id;
46 final Map<String, SocketTask> _tasks;
47 + Map<String, SocketTask> get tasks => _tasks;
48 final Map<String, String> _errors;
49 bool _isConnected;
50 Timer? _aliveTimer;
51 String unterminatedString;
52
52 - Future<void> connectToUri(Uri uri) async => await connect(host: uri.host, port: uri.port);
53 + Uri? uri;
54 + bool? useSSL;
55
54 - Future<void> connect({required String host, required int port}) async {
56 + Future<void> connectToUri(Uri uri, {bool? useSSL}) async {
57 + this.uri = uri;
58 + this.useSSL = useSSL;
59 + await connect(host: uri.host, port: uri.port, useSSL: useSSL);
60 + }
61 +
62 + Future<void> connect({required String host, required int port, bool? useSSL}) async {
63 try {
64 await socket?.close();
65 } catch (_) {}
66
59 - socket = await SecureSocket.connect(host, port,
60 - timeout: connectionTimeout, onBadCertificate: (_) => true);
67 + if (useSSL == false) {
68 + socket = await Socket.connect(host, port, timeout: connectionTimeout);
69 + } else {
70 + socket = await SecureSocket.connect(host, port,
71 + timeout: connectionTimeout, onBadCertificate: (_) => true);
72 + }
73 _setIsConnected(true);
74
75 socket!.listen((Uint8List event) {
@@ -79,7 +91,7 @@ class ElectrumClient {
91 _setIsConnected(false);
92 }, onDone: () {
93 unterminatedString = '';
82 - _setIsConnected(false);
94 + _setIsConnected(null);
95 });
96 keepAlive();
97 }
@@ -134,11 +146,12 @@ class ElectrumClient {
146 await callWithTimeout(method: 'server.ping');
147 _setIsConnected(true);
148 } on RequestFailedTimeoutException catch (_) {
137 - _setIsConnected(false);
149 + _setIsConnected(null);
150 }
151 }
152
141 - Future<List<String>> version() => call(method: 'server.version').then((dynamic result) {
153 + Future<List<String>> version() =>
154 + call(method: 'server.version', params: ["", "1.4"]).then((dynamic result) {
155 if (result is List) {
156 return result.map((dynamic val) => val.toString()).toList();
157 }
@@ -266,6 +279,18 @@ class ElectrumClient {
279 Future<Map<String, dynamic>> getHeader({required int height}) async =>
280 await call(method: 'blockchain.block.get_header', params: [height]) as Map<String, dynamic>;
281
282 + BehaviorSubject<Object>? tweaksSubscribe({required int height, required int count}) {
283 + _id += 1;
284 + return subscribe<Object>(
285 + id: 'blockchain.tweaks.subscribe:${height + count}',
286 + method: 'blockchain.tweaks.subscribe',
287 + params: [height, count, false],
288 + );
289 + }
290 +
291 + Future<dynamic> getTweaks({required int height}) async =>
292 + await callWithTimeout(method: 'blockchain.tweaks.subscribe', params: [height, 1, false]);
293 +
294 Future<double> estimatefee({required int p}) =>
295 call(method: 'blockchain.estimatefee', params: [p]).then((dynamic result) {
296 if (result is double) {
@@ -308,9 +333,6 @@ class ElectrumClient {
333 });
334
335 Future<List<int>> feeRates({BasedUtxoNetwork? network}) async {
311 - if (network == BitcoinNetwork.testnet) {
312 - return [1, 1, 1];
313 - }
336 try {
337 final topDoubleString = await estimatefee(p: 1);
338 final middleDoubleString = await estimatefee(p: 5);
@@ -332,7 +354,7 @@ class ElectrumClient {
354 // "hex": "00000020890208a0ae3a3892aa047c5468725846577cfcd9b512b50000000000000000005dc2b02f2d297a9064ee103036c14d678f9afc7e3d9409cf53fd58b82e938e8ecbeca05a2d2103188ce804c4"
355 // }
356 Future<int?> getCurrentBlockChainTip() =>
335 - call(method: 'blockchain.headers.subscribe').then((result) {
357 + callWithTimeout(method: 'blockchain.headers.subscribe').then((result) {
358 if (result is Map<String, dynamic>) {
359 return result["height"] as int;
360 }
@@ -340,6 +362,12 @@ class ElectrumClient {
362 return null;
363 });
364
365 + BehaviorSubject<Object>? chainTipSubscribe() {
366 + _id += 1;
367 + return subscribe<Object>(
368 + id: 'blockchain.headers.subscribe', method: 'blockchain.headers.subscribe');
369 + }
370 +
371 BehaviorSubject<Object>? scripthashUpdate(String scripthash) {
372 _id += 1;
373 return subscribe<Object>(
@@ -396,7 +424,9 @@ class ElectrumClient {
424
425 Future<void> close() async {
426 _aliveTimer?.cancel();
399 - await socket?.close();
427 + try {
428 + await socket?.close();
429 + } catch (_) {}
430 onConnectionStatusChange = null;
431 }
432
@@ -431,17 +461,25 @@ class ElectrumClient {
461
462 _tasks[id]?.subject?.add(params.last);
463 break;
464 + case 'blockchain.headers.subscribe':
465 + final params = request['params'] as List<dynamic>;
466 + _tasks[method]?.subject?.add(params.last);
467 + break;
468 + case 'blockchain.tweaks.subscribe':
469 + final params = request['params'] as List<dynamic>;
470 + _tasks[_tasks.keys.first]?.subject?.add(params.last);
471 + break;
472 default:
473 break;
474 }
475 }
476
439 - void _setIsConnected(bool isConnected) {
477 + void _setIsConnected(bool? isConnected) {
478 if (_isConnected != isConnected) {
479 onConnectionStatusChange?.call(isConnected);
480 }
481
444 - _isConnected = isConnected;
482 + _isConnected = isConnected ?? false;
483 }
484
485 void _handleResponse(Map<String, dynamic> response) {
cw_bitcoin/lib/electrum_balance.dart
+7 -4
@@ -3,8 +3,11 @@ import 'package:cw_bitcoin/bitcoin_amount_format.dart';
3 import 'package:cw_core/balance.dart';
4
5 class ElectrumBalance extends Balance {
6 - const ElectrumBalance({required this.confirmed, required this.unconfirmed, required this.frozen})
7 - : super(confirmed, unconfirmed);
6 + ElectrumBalance({
7 + required this.confirmed,
8 + required this.unconfirmed,
9 + required this.frozen,
10 + }) : super(confirmed, unconfirmed);
11
12 static ElectrumBalance? fromJSON(String? jsonSource) {
13 if (jsonSource == null) {
@@ -19,8 +22,8 @@ class ElectrumBalance extends Balance {
22 frozen: decoded['frozen'] as int? ?? 0);
23 }
24
22 - final int confirmed;
23 - final int unconfirmed;
25 + int confirmed;
26 + int unconfirmed;
27 final int frozen;
28
29 @override
cw_bitcoin/lib/electrum_transaction_history.dart
+11 -15
@@ -11,13 +11,11 @@ part 'electrum_transaction_history.g.dart';
11
12 const transactionsHistoryFileName = 'transactions.json';
13
14 -class ElectrumTransactionHistory = ElectrumTransactionHistoryBase
15 - with _$ElectrumTransactionHistory;
14 +class ElectrumTransactionHistory = ElectrumTransactionHistoryBase with _$ElectrumTransactionHistory;
15
16 abstract class ElectrumTransactionHistoryBase
17 extends TransactionHistoryBase<ElectrumTransactionInfo> with Store {
19 - ElectrumTransactionHistoryBase(
20 - {required this.walletInfo, required String password})
18 + ElectrumTransactionHistoryBase({required this.walletInfo, required String password})
19 : _password = password,
20 _height = 0 {
21 transactions = ObservableMap<String, ElectrumTransactionInfo>();
@@ -30,8 +28,7 @@ abstract class ElectrumTransactionHistoryBase
28 Future<void> init() async => await _load();
29
30 @override
33 - void addOne(ElectrumTransactionInfo transaction) =>
34 - transactions[transaction.id] = transaction;
31 + void addOne(ElectrumTransactionInfo transaction) => transactions[transaction.id] = transaction;
32
33 @override
34 void addMany(Map<String, ElectrumTransactionInfo> transactions) =>
@@ -40,11 +37,13 @@ abstract class ElectrumTransactionHistoryBase
37 @override
38 Future<void> save() async {
39 try {
43 - final dirPath =
44 - await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
40 + final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
41 final path = '$dirPath/$transactionsHistoryFileName';
46 - final data =
47 - json.encode({'height': _height, 'transactions': transactions});
42 + final txjson = {};
43 + for (final tx in transactions.entries) {
44 + txjson[tx.key] = tx.value.toJson();
45 + }
46 + final data = json.encode({'height': _height, 'transactions': txjson});
47 await writeData(path: path, password: _password, data: data);
48 } catch (e) {
49 print('Error while save bitcoin transaction history: ${e.toString()}');
@@ -57,8 +56,7 @@ abstract class ElectrumTransactionHistoryBase
56 }
57
58 Future<Map<String, dynamic>> _read() async {
60 - final dirPath =
61 - await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
59 + final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
60 final path = '$dirPath/$transactionsHistoryFileName';
61 final content = await read(path: path, password: _password);
62 return json.decode(content) as Map<String, dynamic>;
@@ -84,7 +82,5 @@ abstract class ElectrumTransactionHistoryBase
82 }
83 }
84
87 - void _update(ElectrumTransactionInfo transaction) =>
88 - transactions[transaction.id] = transaction;
89 -
85 + void _update(ElectrumTransactionInfo transaction) => transactions[transaction.id] = transaction;
86 }
cw_bitcoin/lib/electrum_transaction_info.dart
+37 -48
@@ -1,9 +1,8 @@
1 import 'package:bitcoin_base/bitcoin_base.dart';
2 -import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
3 -import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
2 import 'package:cw_bitcoin/address_from_output.dart';
3 import 'package:cw_bitcoin/bitcoin_address_record.dart';
4 import 'package:cw_bitcoin/bitcoin_amount_format.dart';
5 +import 'package:cw_bitcoin/bitcoin_unspent.dart';
6 import 'package:cw_core/transaction_direction.dart';
7 import 'package:cw_core/transaction_info.dart';
8 import 'package:cw_core/format_amount.dart';
@@ -19,6 +18,8 @@ class ElectrumTransactionBundle {
18 }
19
20 class ElectrumTransactionInfo extends TransactionInfo {
21 + List<BitcoinSilentPaymentsUnspent>? unspents;
22 +
23 ElectrumTransactionInfo(this.type,
24 {required String id,
25 required int height,
@@ -29,7 +30,9 @@ class ElectrumTransactionInfo extends TransactionInfo {
30 required TransactionDirection direction,
31 required bool isPending,
32 required DateTime date,
32 - required int confirmations}) {
33 + required int confirmations,
34 + String? to,
35 + this.unspents}) {
36 this.id = id;
37 this.height = height;
38 this.amount = amount;
@@ -40,6 +43,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
43 this.date = date;
44 this.isPending = isPending;
45 this.confirmations = confirmations;
46 + this.to = to;
47 }
48
49 factory ElectrumTransactionInfo.fromElectrumVerbose(Map<String, Object> obj, WalletType type,
@@ -153,52 +157,31 @@ class ElectrumTransactionInfo extends TransactionInfo {
157 confirmations: bundle.confirmations);
158 }
159
156 - factory ElectrumTransactionInfo.fromHexAndHeader(WalletType type, String hex,
157 - {List<String>? addresses, required int height, int? timestamp, required int confirmations}) {
158 - final tx = bitcoin.Transaction.fromHex(hex);
159 - var exist = false;
160 - var amount = 0;
161 -
162 - if (addresses != null) {
163 - tx.outs.forEach((out) {
164 - try {
165 - final p2pkh =
166 - bitcoin.P2PKH(data: PaymentData(output: out.script), network: bitcoin.bitcoin);
167 - exist = addresses.contains(p2pkh.data.address);
168 -
169 - if (exist) {
170 - amount += out.value!;
171 - }
172 - } catch (_) {}
173 - });
174 - }
175 -
176 - final date =
177 - timestamp != null ? DateTime.fromMillisecondsSinceEpoch(timestamp * 1000) : DateTime.now();
178 -
179 - return ElectrumTransactionInfo(type,
180 - id: tx.getId(),
181 - height: height,
182 - isPending: false,
183 - fee: null,
184 - direction: TransactionDirection.incoming,
185 - amount: amount,
186 - date: date,
187 - confirmations: confirmations);
188 - }
189 -
160 factory ElectrumTransactionInfo.fromJson(Map<String, dynamic> data, WalletType type) {
191 - return ElectrumTransactionInfo(type,
192 - id: data['id'] as String,
193 - height: data['height'] as int,
194 - amount: data['amount'] as int,
195 - fee: data['fee'] as int,
196 - direction: parseTransactionDirectionFromInt(data['direction'] as int),
197 - date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
198 - isPending: data['isPending'] as bool,
199 - inputAddresses: data['inputAddresses'] as List<String>,
200 - outputAddresses: data['outputAddresses'] as List<String>,
201 - confirmations: data['confirmations'] as int);
161 + final inputAddresses = data['inputAddresses'] as List<dynamic>? ?? [];
162 + final outputAddresses = data['outputAddresses'] as List<dynamic>? ?? [];
163 + final unspents = data['unspents'] as List<dynamic>? ?? [];
164 +
165 + return ElectrumTransactionInfo(
166 + type,
167 + id: data['id'] as String,
168 + height: data['height'] as int,
169 + amount: data['amount'] as int,
170 + fee: data['fee'] as int,
171 + direction: parseTransactionDirectionFromInt(data['direction'] as int),
172 + date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
173 + isPending: data['isPending'] as bool,
174 + confirmations: data['confirmations'] as int,
175 + inputAddresses:
176 + inputAddresses.isEmpty ? [] : inputAddresses.map((e) => e.toString()).toList(),
177 + outputAddresses:
178 + outputAddresses.isEmpty ? [] : outputAddresses.map((e) => e.toString()).toList(),
179 + to: data['to'] as String?,
180 + unspents: unspents
181 + .map((unspent) =>
182 + BitcoinSilentPaymentsUnspent.fromJSON(null, unspent as Map<String, dynamic>))
183 + .toList(),
184 + );
185 }
186
187 final WalletType type;
@@ -244,8 +227,14 @@ class ElectrumTransactionInfo extends TransactionInfo {
227 m['isPending'] = isPending;
228 m['confirmations'] = confirmations;
229 m['fee'] = fee;
230 + m['to'] = to;
231 + m['unspents'] = unspents?.map((e) => e.toJson()).toList() ?? [];
232 m['inputAddresses'] = inputAddresses;
233 m['outputAddresses'] = outputAddresses;
234 return m;
235 }
236 +
237 + String toString() {
238 + return 'ElectrumTransactionInfo(id: $id, height: $height, amount: $amount, fee: $fee, direction: $direction, date: $date, isPending: $isPending, confirmations: $confirmations, to: $to, unspent: $unspents)';
239 + }
240 }
cw_bitcoin/lib/electrum_wallet.dart
+912 -266
@@ -1,11 +1,12 @@
1 import 'dart:async';
2 import 'dart:convert';
3 import 'dart:io';
4 +import 'dart:isolate';
5 import 'dart:math';
6
7 import 'package:bitcoin_base/bitcoin_base.dart';
7 -import 'package:bitcoin_base/bitcoin_base.dart' as bitcoin_base;
8 import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
9 +import 'package:blockchain_utils/blockchain_utils.dart';
10 import 'package:collection/collection.dart';
11 import 'package:cw_bitcoin/address_from_output.dart';
12 import 'package:cw_bitcoin/bitcoin_address_record.dart';
@@ -35,33 +36,39 @@ import 'package:cw_core/unspent_coins_info.dart';
36 import 'package:cw_core/utils/file.dart';
37 import 'package:cw_core/wallet_base.dart';
38 import 'package:cw_core/wallet_info.dart';
39 +import 'package:cw_core/wallet_type.dart';
40 +import 'package:cw_core/get_height_by_date.dart';
41 import 'package:flutter/foundation.dart';
42 import 'package:hive/hive.dart';
43 import 'package:http/http.dart' as http;
44 import 'package:mobx/mobx.dart';
45 import 'package:rxdart/subjects.dart';
46 +import 'package:sp_scanner/sp_scanner.dart';
47
48 part 'electrum_wallet.g.dart';
49
50 class ElectrumWallet = ElectrumWalletBase with _$ElectrumWallet;
51
52 +const int TWEAKS_COUNT = 25;
53 +
54 abstract class ElectrumWalletBase
55 extends WalletBase<ElectrumBalance, ElectrumTransactionHistory, ElectrumTransactionInfo>
56 with Store {
51 - ElectrumWalletBase(
52 - {required String password,
53 - required WalletInfo walletInfo,
54 - required Box<UnspentCoinsInfo> unspentCoinsInfo,
55 - required this.networkType,
56 - String? xpub,
57 - String? mnemonic,
58 - Uint8List? seedBytes,
59 - this.passphrase,
60 - List<BitcoinAddressRecord>? initialAddresses,
61 - ElectrumClient? electrumClient,
62 - ElectrumBalance? initialBalance,
63 - CryptoCurrency? currency})
64 - : accountHD =
57 + ElectrumWalletBase({
58 + required String password,
59 + required WalletInfo walletInfo,
60 + required Box<UnspentCoinsInfo> unspentCoinsInfo,
61 + required this.networkType,
62 + String? xpub,
63 + String? mnemonic,
64 + Uint8List? seedBytes,
65 + this.passphrase,
66 + List<BitcoinAddressRecord>? initialAddresses,
67 + ElectrumClient? electrumClient,
68 + ElectrumBalance? initialBalance,
69 + CryptoCurrency? currency,
70 + this.alwaysScan,
71 + }) : accountHD =
72 getAccountHDWallet(currency, networkType, seedBytes, xpub, walletInfo.derivationInfo),
73 syncStatus = NotConnectedSyncStatus(),
74 _password = password,
@@ -72,8 +79,12 @@ abstract class ElectrumWalletBase
79 _scripthashesUpdateSubject = {},
80 balance = ObservableMap<CryptoCurrency, ElectrumBalance>.of(currency != null
81 ? {
75 - currency:
76 - initialBalance ?? const ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0)
82 + currency: initialBalance ??
83 + ElectrumBalance(
84 + confirmed: 0,
85 + unconfirmed: 0,
86 + frozen: 0,
87 + )
88 }
89 : {}),
90 this.unspentCoinsInfo = unspentCoinsInfo,
@@ -84,6 +95,23 @@ abstract class ElectrumWalletBase
95 this.electrumClient = electrumClient ?? ElectrumClient();
96 this.walletInfo = walletInfo;
97 transactionHistory = ElectrumTransactionHistory(walletInfo: walletInfo, password: password);
98 +
99 + reaction((_) => syncStatus, (SyncStatus syncStatus) {
100 + if (syncStatus is! AttemptingSyncStatus && syncStatus is! SyncedTipSyncStatus)
101 + silentPaymentsScanningActive = syncStatus is SyncingSyncStatus;
102 +
103 + if (syncStatus is NotConnectedSyncStatus) {
104 + // Needs to re-subscribe to all scripthashes when reconnected
105 + _scripthashesUpdateSubject = {};
106 + }
107 +
108 + // Message is shown on the UI for 3 seconds, revert to synced
109 + if (syncStatus is SyncedTipSyncStatus) {
110 + Timer(Duration(seconds: 3), () {
111 + if (this.syncStatus is SyncedTipSyncStatus) this.syncStatus = SyncedSyncStatus();
112 + });
113 + }
114 + });
115 }
116
117 static bitcoin.HDWallet getAccountHDWallet(
@@ -113,6 +141,8 @@ abstract class ElectrumWalletBase
141 static int estimatedTransactionSize(int inputsCount, int outputsCounts) =>
142 inputsCount * 68 + outputsCounts * 34 + 10;
143
144 + bool? alwaysScan;
145 +
146 final bitcoin.HDWallet accountHD;
147 final String? _mnemonic;
148
@@ -137,6 +167,8 @@ abstract class ElectrumWalletBase
167 @observable
168 SyncStatus syncStatus;
169
170 + Set<String> get addressesSet => walletAddresses.allAddresses.map((addr) => addr.address).toSet();
171 +
172 List<String> get scriptHashes => walletAddresses.addressesByReceiveType
173 .map((addr) => scriptHash(addr.address, network: network))
174 .toList();
@@ -157,6 +189,64 @@ abstract class ElectrumWalletBase
189 @override
190 bool? isTestnet;
191
192 + bool get hasSilentPaymentsScanning => type == WalletType.bitcoin;
193 +
194 + @observable
195 + bool nodeSupportsSilentPayments = true;
196 + @observable
197 + bool silentPaymentsScanningActive = false;
198 +
199 + @action
200 + Future<void> setSilentPaymentsScanning(bool active) async {
201 + silentPaymentsScanningActive = active;
202 +
203 + if (active) {
204 + syncStatus = AttemptingSyncStatus();
205 +
206 + final tip = await getUpdatedChainTip();
207 +
208 + if (tip == walletInfo.restoreHeight) {
209 + syncStatus = SyncedTipSyncStatus(tip);
210 + }
211 +
212 + if (tip > walletInfo.restoreHeight) {
213 + _setListeners(walletInfo.restoreHeight, chainTipParam: _currentChainTip);
214 + }
215 + } else {
216 + alwaysScan = false;
217 +
218 + (await _isolate)?.kill(priority: Isolate.immediate);
219 +
220 + if (electrumClient.isConnected) {
221 + syncStatus = SyncedSyncStatus();
222 + } else {
223 + if (electrumClient.uri != null) {
224 + await electrumClient.connectToUri(electrumClient.uri!);
225 + startSync();
226 + }
227 + }
228 + }
229 + }
230 +
231 + int? _currentChainTip;
232 +
233 + Future<int> getCurrentChainTip() async {
234 + if (_currentChainTip != null) {
235 + return _currentChainTip!;
236 + }
237 + _currentChainTip = await electrumClient.getCurrentBlockChainTip() ?? 0;
238 +
239 + return _currentChainTip!;
240 + }
241 +
242 + Future<int> getUpdatedChainTip() async {
243 + final newTip = await electrumClient.getCurrentBlockChainTip();
244 + if (newTip != null && newTip > (_currentChainTip ?? 0)) {
245 + _currentChainTip = newTip;
246 + }
247 + return _currentChainTip ?? 0;
248 + }
249 +
250 @override
251 BitcoinWalletKeys get keys =>
252 BitcoinWalletKeys(wif: hd.wif!, privateKey: hd.privKey!, publicKey: hd.pubKey!);
@@ -164,32 +254,179 @@ abstract class ElectrumWalletBase
254 String _password;
255 List<BitcoinUnspent> unspentCoins;
256 List<int> _feeRates;
257 +
258 + // ignore: prefer_final_fields
259 Map<String, BehaviorSubject<Object>?> _scripthashesUpdateSubject;
260 +
261 + // ignore: prefer_final_fields
262 + BehaviorSubject<Object>? _chainTipUpdateSubject;
263 bool _isTransactionUpdating;
264 + Future<Isolate>? _isolate;
265
266 void Function(FlutterErrorDetails)? _onError;
267 + Timer? _autoSaveTimer;
268 + static const int _autoSaveInterval = 30;
269
270 Future<void> init() async {
271 await walletAddresses.init();
272 await transactionHistory.init();
273 await save();
274 +
275 + _autoSaveTimer =
276 + Timer.periodic(Duration(seconds: _autoSaveInterval), (_) async => await save());
277 + }
278 +
279 + @action
280 + Future<void> _setListeners(int height, {int? chainTipParam, bool? doSingleScan}) async {
281 + final chainTip = chainTipParam ?? await getUpdatedChainTip();
282 +
283 + if (chainTip == height) {
284 + syncStatus = SyncedSyncStatus();
285 + return;
286 + }
287 +
288 + syncStatus = AttemptingSyncStatus();
289 +
290 + if (_isolate != null) {
291 + final runningIsolate = await _isolate!;
292 + runningIsolate.kill(priority: Isolate.immediate);
293 + }
294 +
295 + final receivePort = ReceivePort();
296 + _isolate = Isolate.spawn(
297 + startRefresh,
298 + ScanData(
299 + sendPort: receivePort.sendPort,
300 + silentAddress: walletAddresses.silentAddress!,
301 + network: network,
302 + height: height,
303 + chainTip: chainTip,
304 + electrumClient: ElectrumClient(),
305 + transactionHistoryIds: transactionHistory.transactions.keys.toList(),
306 + node: ScanNode(node!.uri, node!.useSSL),
307 + labels: walletAddresses.labels,
308 + labelIndexes: walletAddresses.silentAddresses
309 + .where((addr) => addr.type == SilentPaymentsAddresType.p2sp && addr.index >= 1)
310 + .map((addr) => addr.index)
311 + .toList(),
312 + isSingleScan: doSingleScan ?? false,
313 + ));
314 +
315 + await for (var message in receivePort) {
316 + if (message is Map<String, ElectrumTransactionInfo>) {
317 + for (final map in message.entries) {
318 + final txid = map.key;
319 + final tx = map.value;
320 +
321 + if (tx.unspents != null) {
322 + final existingTxInfo = transactionHistory.transactions[txid];
323 + final txAlreadyExisted = existingTxInfo != null;
324 +
325 + // Updating tx after re-scanned
326 + if (txAlreadyExisted) {
327 + existingTxInfo.amount = tx.amount;
328 + existingTxInfo.confirmations = tx.confirmations;
329 + existingTxInfo.height = tx.height;
330 +
331 + final newUnspents = tx.unspents!
332 + .where((unspent) => !(existingTxInfo.unspents?.any((element) =>
333 + element.hash.contains(unspent.hash) &&
334 + element.vout == unspent.vout &&
335 + element.value == unspent.value) ??
336 + false))
337 + .toList();
338 +
339 + if (newUnspents.isNotEmpty) {
340 + newUnspents.forEach(_updateSilentAddressRecord);
341 +
342 + existingTxInfo.unspents ??= [];
343 + existingTxInfo.unspents!.addAll(newUnspents);
344 +
345 + final newAmount = newUnspents.length > 1
346 + ? newUnspents.map((e) => e.value).reduce((value, unspent) => value + unspent)
347 + : newUnspents[0].value;
348 +
349 + if (existingTxInfo.direction == TransactionDirection.incoming) {
350 + existingTxInfo.amount += newAmount;
351 + }
352 +
353 + // Updates existing TX
354 + transactionHistory.addOne(existingTxInfo);
355 + // Update balance record
356 + balance[currency]!.confirmed += newAmount;
357 + }
358 + } else {
359 + // else: First time seeing this TX after scanning
360 + tx.unspents!.forEach(_updateSilentAddressRecord);
361 +
362 + // Add new TX record
363 + transactionHistory.addMany(message);
364 + // Update balance record
365 + balance[currency]!.confirmed += tx.amount;
366 + }
367 +
368 + await updateAllUnspents();
369 + }
370 + }
371 + }
372 +
373 + if (message is SyncResponse) {
374 + if (message.syncStatus is UnsupportedSyncStatus) {
375 + nodeSupportsSilentPayments = false;
376 + }
377 +
378 + syncStatus = message.syncStatus;
379 + await walletInfo.updateRestoreHeight(message.height);
380 + }
381 + }
382 + }
383 +
384 + void _updateSilentAddressRecord(BitcoinSilentPaymentsUnspent unspent) {
385 + final silentAddress = walletAddresses.silentAddress!;
386 + final silentPaymentAddress = SilentPaymentAddress(
387 + version: silentAddress.version,
388 + B_scan: silentAddress.B_scan,
389 + B_spend: unspent.silentPaymentLabel != null
390 + ? silentAddress.B_spend.tweakAdd(
391 + BigintUtils.fromBytes(BytesUtils.fromHexString(unspent.silentPaymentLabel!)),
392 + )
393 + : silentAddress.B_spend,
394 + hrp: silentAddress.hrp,
395 + );
396 +
397 + final addressRecord = walletAddresses.silentAddresses
398 + .firstWhereOrNull((address) => address.address == silentPaymentAddress.toString());
399 + addressRecord?.txCount += 1;
400 + addressRecord?.balance += unspent.value;
401 +
402 + walletAddresses.addSilentAddresses(
403 + [unspent.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord],
404 + );
405 }
406
407 @action
408 @override
409 Future<void> startSync() async {
410 try {
182 - syncStatus = AttemptingSyncStatus();
411 + syncStatus = SyncronizingSyncStatus();
412 +
413 + if (hasSilentPaymentsScanning) {
414 + await _setInitialHeight();
415 + }
416 +
417 + await _subscribeForUpdates();
418 +
419 await updateTransactions();
184 - _subscribeForUpdates();
185 - await updateUnspent();
420 + await updateAllUnspents();
421 await updateBalance();
187 - _feeRates = await electrumClient.feeRates(network: network);
422
189 - Timer.periodic(
190 - const Duration(minutes: 1), (timer) async => _feeRates = await electrumClient.feeRates());
423 + Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates());
424
192 - syncStatus = SyncedSyncStatus();
425 + if (alwaysScan == true) {
426 + _setListeners(walletInfo.restoreHeight);
427 + } else {
428 + syncStatus = SyncedSyncStatus();
429 + }
430 } catch (e, stacktrace) {
431 print(stacktrace);
432 print(e.toString());
@@ -197,18 +434,39 @@ abstract class ElectrumWalletBase
434 }
435 }
436
437 + @action
438 + Future<void> updateFeeRates() async {
439 + final feeRates = await electrumClient.feeRates(network: network);
440 + if (feeRates != [0, 0, 0]) {
441 + _feeRates = feeRates;
442 + }
443 + }
444 +
445 + Node? node;
446 +
447 @action
448 @override
449 Future<void> connectToNode({required Node node}) async {
450 + this.node = node;
451 +
452 try {
453 syncStatus = ConnectingSyncStatus();
205 - await electrumClient.connectToUri(node.uri);
206 - electrumClient.onConnectionStatusChange = (bool isConnected) {
207 - if (!isConnected) {
454 +
455 + await electrumClient.close();
456 +
457 + electrumClient.onConnectionStatusChange = (bool? isConnected) async {
458 + if (syncStatus is SyncingSyncStatus) return;
459 +
460 + if (isConnected == true && syncStatus is! SyncedSyncStatus) {
461 + syncStatus = ConnectedSyncStatus();
462 + } else if (isConnected == false) {
463 syncStatus = LostConnectionSyncStatus();
464 + } else if (!(isConnected ?? false) && syncStatus is! ConnectingSyncStatus) {
465 + syncStatus = NotConnectedSyncStatus();
466 }
467 };
211 - syncStatus = ConnectedSyncStatus();
468 +
469 + await electrumClient.connectToUri(node.uri, useSSL: node.useSSL);
470 } catch (e) {
471 print(e.toString());
472 syncStatus = FailedSyncStatus();
@@ -219,60 +477,95 @@ abstract class ElectrumWalletBase
477
478 bool _isBelowDust(int amount) => amount <= _dustAmount && network != BitcoinNetwork.testnet;
479
222 - Future<EstimatedTxResult> estimateSendAllTx(
223 - List<BitcoinOutput> outputs,
224 - int feeRate, {
225 - String? memo,
226 - int credentialsAmount = 0,
227 - }) async {
228 - final utxos = <UtxoWithAddress>[];
229 - final privateKeys = <ECPrivate>[];
480 + UtxoDetails _createUTXOS({
481 + required bool sendAll,
482 + required int credentialsAmount,
483 + required bool paysToSilentPayment,
484 + int? inputsCount,
485 + }) {
486 + List<UtxoWithAddress> utxos = [];
487 + List<Outpoint> vinOutpoints = [];
488 + List<ECPrivateInfo> inputPrivKeyInfos = [];
489 final publicKeys = <String, PublicKeyWithDerivationPath>{};
231 -
490 int allInputsAmount = 0;
233 -
491 + bool spendsSilentPayment = false;
492 bool spendsUnconfirmedTX = false;
493
236 - for (int i = 0; i < unspentCoins.length; i++) {
237 - final utx = unspentCoins[i];
494 + int leftAmount = credentialsAmount;
495 + final availableInputs = unspentCoins.where((utx) => utx.isSending && !utx.isFrozen).toList();
496 + final unconfirmedCoins = availableInputs.where((utx) => utx.confirmations == 0).toList();
497
239 - if (utx.isSending && !utx.isFrozen) {
240 - if (!spendsUnconfirmedTX) spendsUnconfirmedTX = utx.confirmations == 0;
498 + for (int i = 0; i < availableInputs.length; i++) {
499 + final utx = availableInputs[i];
500 + if (!spendsUnconfirmedTX) spendsUnconfirmedTX = utx.confirmations == 0;
501
242 - allInputsAmount += utx.value;
502 + if (paysToSilentPayment) {
503 + // Check inputs for shared secret derivation
504 + if (utx.bitcoinAddressRecord.type == SegwitAddresType.p2wsh) {
505 + throw BitcoinTransactionSilentPaymentsNotSupported();
506 + }
507 + }
508
244 - final address = addressTypeFromStr(utx.address, network);
245 - final hd =
246 - utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd;
247 - final derivationPath =
248 - "${_hardenedDerivationPath(walletInfo.derivationInfo?.derivationPath ?? "m/0'")}"
249 - "/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}"
250 - "/${utx.bitcoinAddressRecord.index}";
251 - final pubKeyHex = hd.derive(utx.bitcoinAddressRecord.index).pubKey!;
509 + allInputsAmount += utx.value;
510 + leftAmount = leftAmount - utx.value;
511
253 - publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
512 + final address = addressTypeFromStr(utx.address, network);
513 + ECPrivate? privkey;
514 + bool? isSilentPayment = false;
515
255 - if (!walletInfo.isHardwareWallet) {
256 - final privkey =
257 - generateECPrivate(hd: hd, index: utx.bitcoinAddressRecord.index, network: network);
516 + final hd =
517 + utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd;
518 + final derivationPath =
519 + "${_hardenedDerivationPath(walletInfo.derivationInfo?.derivationPath ?? "m/0'")}"
520 + "/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}"
521 + "/${utx.bitcoinAddressRecord.index}";
522 + final pubKeyHex = hd.derive(utx.bitcoinAddressRecord.index).pubKey!;
523
259 - privateKeys.add(privkey);
260 - }
524 + publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
525
262 - utxos.add(
263 - UtxoWithAddress(
264 - utxo: BitcoinUtxo(
265 - txHash: utx.hash,
266 - value: BigInt.from(utx.value),
267 - vout: utx.vout,
268 - scriptType: _getScriptType(address),
269 - ),
270 - ownerDetails: UtxoAddressDetails(
271 - publicKey: pubKeyHex,
272 - address: address,
273 - ),
526 + if (utx.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) {
527 + final unspentAddress = utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord;
528 + privkey = walletAddresses.silentAddress!.b_spend.tweakAdd(
529 + BigintUtils.fromBytes(
530 + BytesUtils.fromHexString(unspentAddress.silentPaymentTweak!),
531 ),
532 );
533 + spendsSilentPayment = true;
534 + isSilentPayment = true;
535 + } else {
536 + privkey =
537 + generateECPrivate(hd: hd, index: utx.bitcoinAddressRecord.index, network: network);
538 + }
539 +
540 + vinOutpoints.add(Outpoint(txid: utx.hash, index: utx.vout));
541 + inputPrivKeyInfos.add(ECPrivateInfo(
542 + privkey,
543 + address.type == SegwitAddresType.p2tr,
544 + tweak: !isSilentPayment,
545 + ));
546 +
547 + utxos.add(
548 + UtxoWithAddress(
549 + utxo: BitcoinUtxo(
550 + txHash: utx.hash,
551 + value: BigInt.from(utx.value),
552 + vout: utx.vout,
553 + scriptType: _getScriptType(address),
554 + isSilentPayment: isSilentPayment,
555 + ),
556 + ownerDetails: UtxoAddressDetails(
557 + publicKey: privkey.getPublic().toHex(),
558 + address: address,
559 + ),
560 + ),
561 + );
562 +
563 + // sendAll continues for all inputs
564 + if (!sendAll) {
565 + bool amountIsAcquired = leftAmount <= 0;
566 + if ((inputsCount == null && amountIsAcquired) || inputsCount == i + 1) {
567 + break;
568 + }
569 }
570 }
571
@@ -280,20 +573,48 @@ abstract class ElectrumWalletBase
573 throw BitcoinTransactionNoInputsException();
574 }
575
576 + return UtxoDetails(
577 + availableInputs: availableInputs,
578 + unconfirmedCoins: unconfirmedCoins,
579 + utxos: utxos,
580 + vinOutpoints: vinOutpoints,
581 + inputPrivKeyInfos: inputPrivKeyInfos,
582 + publicKeys: publicKeys,
583 + allInputsAmount: allInputsAmount,
584 + spendsSilentPayment: spendsSilentPayment,
585 + spendsUnconfirmedTX: spendsUnconfirmedTX,
586 + );
587 + }
588 +
589 + Future<EstimatedTxResult> estimateSendAllTx(
590 + List<BitcoinOutput> outputs,
591 + int feeRate, {
592 + String? memo,
593 + int credentialsAmount = 0,
594 + bool hasSilentPayment = false,
595 + }) async {
596 + final utxoDetails = _createUTXOS(
597 + sendAll: true,
598 + credentialsAmount: credentialsAmount,
599 + paysToSilentPayment: hasSilentPayment,
600 + );
601 +
602 int estimatedSize;
603 if (network is BitcoinCashNetwork) {
604 estimatedSize = ForkedTransactionBuilder.estimateTransactionSize(
286 - utxos: utxos,
605 + utxos: utxoDetails.utxos,
606 outputs: outputs,
607 network: network as BitcoinCashNetwork,
608 memo: memo,
609 );
610 } else {
611 estimatedSize = BitcoinTransactionBuilder.estimateTransactionSize(
293 - utxos: utxos,
612 + utxos: utxoDetails.utxos,
613 outputs: outputs,
614 network: network,
615 memo: memo,
616 + inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos,
617 + vinOutpoints: utxoDetails.vinOutpoints,
618 );
619 }
620
@@ -304,7 +625,11 @@ abstract class ElectrumWalletBase
625 }
626
627 // Here, when sending all, the output amount equals to the input value - fee to fully spend every input on the transaction and have no amount left for change
307 - int amount = allInputsAmount - fee;
628 + int amount = utxoDetails.allInputsAmount - fee;
629 +
630 + if (amount <= 0) {
631 + throw BitcoinTransactionWrongBalanceException(amount: utxoDetails.allInputsAmount + fee);
632 + }
633
634 if (amount <= 0) {
635 throw BitcoinTransactionWrongBalanceException();
@@ -323,19 +648,21 @@ abstract class ElectrumWalletBase
648 }
649 }
650
326 - outputs[outputs.length - 1] =
327 - BitcoinOutput(address: outputs.last.address, value: BigInt.from(amount));
651 + if (outputs.length == 1) {
652 + outputs[0] = BitcoinOutput(address: outputs.last.address, value: BigInt.from(amount));
653 + }
654
655 return EstimatedTxResult(
330 - utxos: utxos,
331 - privateKeys: privateKeys,
332 - publicKeys: publicKeys,
656 + utxos: utxoDetails.utxos,
657 + inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos,
658 + publicKeys: utxoDetails.publicKeys,
659 fee: fee,
660 amount: amount,
661 isSendAll: true,
662 hasChange: false,
663 memo: memo,
338 - spendsUnconfirmedTX: spendsUnconfirmedTX,
664 + spendsUnconfirmedTX: utxoDetails.spendsUnconfirmedTX,
665 + spendsSilentPayment: utxoDetails.spendsSilentPayment,
666 );
667 }
668
@@ -346,79 +673,22 @@ abstract class ElectrumWalletBase
673 int? inputsCount,
674 String? memo,
675 bool? useUnconfirmed,
676 + bool hasSilentPayment = false,
677 }) async {
350 - final utxos = <UtxoWithAddress>[];
351 - final privateKeys = <ECPrivate>[];
352 - final publicKeys = <String, PublicKeyWithDerivationPath>{};
353 -
354 - int allInputsAmount = 0;
355 - bool spendsUnconfirmedTX = false;
356 -
357 - int leftAmount = credentialsAmount;
358 - final sendingCoins = unspentCoins.where((utx) => utx.isSending && !utx.isFrozen).toList();
359 - final unconfirmedCoins = sendingCoins.where((utx) => utx.confirmations == 0).toList();
360 -
361 - for (int i = 0; i < sendingCoins.length; i++) {
362 - final utx = sendingCoins[i];
363 -
364 - final isUncormirmed = utx.confirmations == 0;
365 - if (useUnconfirmed != true && isUncormirmed) continue;
366 -
367 - if (!spendsUnconfirmedTX) spendsUnconfirmedTX = isUncormirmed;
368 -
369 - allInputsAmount += utx.value;
370 - leftAmount = leftAmount - utx.value;
371 -
372 - final address = addressTypeFromStr(utx.address, network);
373 -
374 - final hd =
375 - utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd;
376 - final derivationPath =
377 - "${_hardenedDerivationPath(walletInfo.derivationInfo?.derivationPath ?? "m/0'")}"
378 - "/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}"
379 - "/${utx.bitcoinAddressRecord.index}";
380 - final pubKeyHex = hd.derive(utx.bitcoinAddressRecord.index).pubKey!;
381 -
382 - publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
383 -
384 - if (!walletInfo.isHardwareWallet) {
385 - final privkey =
386 - generateECPrivate(hd: hd, index: utx.bitcoinAddressRecord.index, network: network);
387 -
388 - privateKeys.add(privkey);
389 - }
390 -
391 - utxos.add(
392 - UtxoWithAddress(
393 - utxo: BitcoinUtxo(
394 - txHash: utx.hash,
395 - value: BigInt.from(utx.value),
396 - vout: utx.vout,
397 - scriptType: _getScriptType(address),
398 - ),
399 - ownerDetails: UtxoAddressDetails(
400 - publicKey: pubKeyHex,
401 - address: address,
402 - ),
403 - ),
404 - );
405 -
406 - bool amountIsAcquired = leftAmount <= 0;
407 - if ((inputsCount == null && amountIsAcquired) || inputsCount == i + 1) {
408 - break;
409 - }
410 - }
411 -
412 - if (utxos.isEmpty) {
413 - throw BitcoinTransactionNoInputsException();
414 - }
678 + final utxoDetails = _createUTXOS(
679 + sendAll: false,
680 + credentialsAmount: credentialsAmount,
681 + inputsCount: inputsCount,
682 + paysToSilentPayment: hasSilentPayment,
683 + );
684
416 - final spendingAllCoins = sendingCoins.length == utxos.length;
417 - final spendingAllConfirmedCoins =
418 - !spendsUnconfirmedTX && utxos.length == sendingCoins.length - unconfirmedCoins.length;
685 + final spendingAllCoins = utxoDetails.availableInputs.length == utxoDetails.utxos.length;
686 + final spendingAllConfirmedCoins = !utxoDetails.spendsUnconfirmedTX &&
687 + utxoDetails.utxos.length ==
688 + utxoDetails.availableInputs.length - utxoDetails.unconfirmedCoins.length;
689
690 // How much is being spent - how much is being sent
421 - int amountLeftForChangeAndFee = allInputsAmount - credentialsAmount;
691 + int amountLeftForChangeAndFee = utxoDetails.allInputsAmount - credentialsAmount;
692
693 if (amountLeftForChangeAndFee <= 0) {
694 if (!spendingAllCoins) {
@@ -426,11 +696,12 @@ abstract class ElectrumWalletBase
696 credentialsAmount,
697 outputs,
698 feeRate,
429 - inputsCount: utxos.length + 1,
699 + inputsCount: utxoDetails.utxos.length + 1,
700 memo: memo,
431 - useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins,
701 + hasSilentPayment: hasSilentPayment,
702 );
703 }
704 +
705 throw BitcoinTransactionWrongBalanceException();
706 }
707
@@ -444,17 +715,19 @@ abstract class ElectrumWalletBase
715 int estimatedSize;
716 if (network is BitcoinCashNetwork) {
717 estimatedSize = ForkedTransactionBuilder.estimateTransactionSize(
447 - utxos: utxos,
718 + utxos: utxoDetails.utxos,
719 outputs: outputs,
720 network: network as BitcoinCashNetwork,
721 memo: memo,
722 );
723 } else {
724 estimatedSize = BitcoinTransactionBuilder.estimateTransactionSize(
454 - utxos: utxos,
725 + utxos: utxoDetails.utxos,
726 outputs: outputs,
727 network: network,
728 memo: memo,
729 + inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos,
730 + vinOutpoints: utxoDetails.vinOutpoints,
731 );
732 }
733
@@ -482,7 +755,7 @@ abstract class ElectrumWalletBase
755 credentialsAmount,
756 outputs,
757 feeRate,
485 - inputsCount: utxos.length + 1,
758 + inputsCount: utxoDetails.utxos.length + 1,
759 memo: memo,
760 useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins,
761 );
@@ -499,7 +772,7 @@ abstract class ElectrumWalletBase
772 }
773
774 // Estimate to user how much is needed to send to cover the fee
502 - final maxAmountWithReturningChange = allInputsAmount - _dustAmount - fee - 1;
775 + final maxAmountWithReturningChange = utxoDetails.allInputsAmount - _dustAmount - fee - 1;
776 throw BitcoinTransactionNoDustOnChangeException(
777 bitcoinAmountToString(amount: maxAmountWithReturningChange),
778 bitcoinAmountToString(amount: estimatedSendAll.amount),
@@ -517,35 +790,34 @@ abstract class ElectrumWalletBase
790 throw BitcoinTransactionWrongBalanceException();
791 }
792
520 - if (totalAmount > allInputsAmount) {
793 + if (totalAmount > utxoDetails.allInputsAmount) {
794 if (spendingAllCoins) {
795 throw BitcoinTransactionWrongBalanceException();
796 } else {
524 - if (amountLeftForChangeAndFee > fee) {
525 - outputs.removeLast();
526 - }
527 -
797 + outputs.removeLast();
798 return estimateTxForAmount(
799 credentialsAmount,
800 outputs,
801 feeRate,
532 - inputsCount: utxos.length + 1,
802 + inputsCount: utxoDetails.utxos.length + 1,
803 memo: memo,
804 useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins,
805 + hasSilentPayment: hasSilentPayment,
806 );
807 }
808 }
809
810 return EstimatedTxResult(
540 - utxos: utxos,
541 - privateKeys: privateKeys,
542 - publicKeys: publicKeys,
811 + utxos: utxoDetails.utxos,
812 + inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos,
813 + publicKeys: utxoDetails.publicKeys,
814 fee: fee,
815 amount: amount,
816 hasChange: true,
817 isSendAll: false,
818 memo: memo,
548 - spendsUnconfirmedTX: spendsUnconfirmedTX,
819 + spendsUnconfirmedTX: utxoDetails.spendsUnconfirmedTX,
820 + spendsSilentPayment: utxoDetails.spendsSilentPayment,
821 );
822 }
823
@@ -559,6 +831,7 @@ abstract class ElectrumWalletBase
831 final memo = transactionCredentials.outputs.first.memo;
832
833 int credentialsAmount = 0;
834 + bool hasSilentPayment = false;
835
836 for (final out in transactionCredentials.outputs) {
837 final outputAmount = out.formattedCryptoAmount!;
@@ -578,6 +851,10 @@ abstract class ElectrumWalletBase
851 final address =
852 addressTypeFromStr(out.isParsedAddress ? out.extractedAddress! : out.address, network);
853
854 + if (address is SilentPaymentAddress) {
855 + hasSilentPayment = true;
856 + }
857 +
858 if (sendAll) {
859 // The value will be changed after estimating the Tx size and deducting the fee from the total to be sent
860 outputs.add(BitcoinOutput(address: address, value: BigInt.from(0)));
@@ -597,6 +874,7 @@ abstract class ElectrumWalletBase
874 feeRateInt,
875 memo: memo,
876 credentialsAmount: credentialsAmount,
877 + hasSilentPayment: hasSilentPayment,
878 );
879 } else {
880 estimatedTx = await estimateTxForAmount(
@@ -604,6 +882,7 @@ abstract class ElectrumWalletBase
882 outputs,
883 feeRateInt,
884 memo: memo,
885 + hasSilentPayment: hasSilentPayment,
886 );
887 }
888
@@ -662,8 +941,8 @@ abstract class ElectrumWalletBase
941 bool hasTaprootInputs = false;
942
943 final transaction = txb.buildTransaction((txDigest, utxo, publicKey, sighash) {
665 - final key = estimatedTx.privateKeys
666 - .firstWhereOrNull((element) => element.getPublic().toHex() == publicKey);
944 + final key = estimatedTx.inputPrivKeyInfos
945 + .firstWhereOrNull((element) => element.privkey.getPublic().toHex() == publicKey);
946
947 if (key == null) {
948 throw Exception("Cannot find private key");
@@ -671,9 +950,13 @@ abstract class ElectrumWalletBase
950
951 if (utxo.utxo.isP2tr()) {
952 hasTaprootInputs = true;
674 - return key.signTapRoot(txDigest, sighash: sighash);
953 + return key.privkey.signTapRoot(
954 + txDigest,
955 + sighash: sighash,
956 + tweak: utxo.utxo.isSilentPayment != true,
957 + );
958 } else {
676 - return key.signInput(txDigest, sigHash: sighash);
959 + return key.privkey.signInput(txDigest, sigHash: sighash);
960 }
961 });
962
@@ -690,6 +973,14 @@ abstract class ElectrumWalletBase
973 hasTaprootInputs: hasTaprootInputs,
974 )..addListener((transaction) async {
975 transactionHistory.addOne(transaction);
976 + if (estimatedTx.spendsSilentPayment) {
977 + transactionHistory.transactions.values.forEach((tx) {
978 + tx.unspents?.removeWhere(
979 + (unspent) => estimatedTx.utxos.any((e) => e.utxo.txHash == unspent.hash));
980 + transactionHistory.addOne(tx);
981 + });
982 + }
983 +
984 await updateBalance();
985 });
986 } catch (e) {
@@ -723,6 +1014,8 @@ abstract class ElectrumWalletBase
1014 'balance': balance[currency]?.toJSON(),
1015 'derivationTypeIndex': walletInfo.derivationInfo?.derivationType?.index,
1016 'derivationPath': walletInfo.derivationInfo?.derivationPath,
1017 + 'silent_addresses': walletAddresses.silentAddresses.map((addr) => addr.toJSON()).toList(),
1018 + 'silent_address_index': walletAddresses.currentSilentAddressIndex.toString(),
1019 });
1020
1021 int feeRate(TransactionPriority priority) {
@@ -827,35 +1120,40 @@ abstract class ElectrumWalletBase
1120 await transactionHistory.changePassword(password);
1121 }
1122
1123 + @action
1124 @override
831 - Future<void> rescan({required int height}) async => throw UnimplementedError();
1125 + Future<void> rescan(
1126 + {required int height, int? chainTip, ScanData? scanData, bool? doSingleScan}) async {
1127 + silentPaymentsScanningActive = true;
1128 + _setListeners(height, doSingleScan: doSingleScan);
1129 + }
1130
1131 @override
1132 Future<void> close() async {
1133 try {
1134 await electrumClient.close();
1135 } catch (_) {}
1136 + _autoSaveTimer?.cancel();
1137 }
1138
1139 Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
1140
842 - Future<void> updateUnspent() async {
1141 + @action
1142 + Future<void> updateAllUnspents() async {
1143 List<BitcoinUnspent> updatedUnspentCoins = [];
1144
845 - final addressesSet = walletAddresses.allAddresses.map((addr) => addr.address).toSet();
846 -
847 - await Future.wait(walletAddresses.allAddresses.map((address) => electrumClient
848 - .getListUnspentWithAddress(address.address, network)
849 - .then((unspent) => Future.forEach<Map<String, dynamic>>(unspent, (unspent) async {
850 - try {
851 - final coin = BitcoinUnspent.fromJSON(address, unspent);
852 - final tx = await fetchTransactionInfo(
853 - hash: coin.hash, height: 0, myAddresses: addressesSet);
854 - coin.isChange = tx?.direction == TransactionDirection.outgoing;
855 - coin.confirmations = tx?.confirmations;
856 - updatedUnspentCoins.add(coin);
857 - } catch (_) {}
858 - }))));
1145 + if (hasSilentPaymentsScanning) {
1146 + // Update unspents stored from scanned silent payment transactions
1147 + transactionHistory.transactions.values.forEach((tx) {
1148 + if (tx.unspents != null) {
1149 + updatedUnspentCoins.addAll(tx.unspents!);
1150 + }
1151 + });
1152 + }
1153 +
1154 + await Future.wait(walletAddresses.allAddresses.map((address) async {
1155 + updatedUnspentCoins.addAll(await fetchUnspent(address));
1156 + }));
1157
1158 unspentCoins = updatedUnspentCoins;
1159
@@ -877,6 +1175,8 @@ abstract class ElectrumWalletBase
1175 coin.isFrozen = coinInfo.isFrozen;
1176 coin.isSending = coinInfo.isSending;
1177 coin.note = coinInfo.note;
1178 + if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord)
1179 + coin.bitcoinAddressRecord.balance += coinInfo.value;
1180 } else {
1181 _addCoinInfo(coin);
1182 }
@@ -886,6 +1186,57 @@ abstract class ElectrumWalletBase
1186 await _refreshUnspentCoinsInfo();
1187 }
1188
1189 + @action
1190 + Future<void> updateUnspents(BitcoinAddressRecord address) async {
1191 + final newUnspentCoins = await fetchUnspent(address);
1192 +
1193 + if (newUnspentCoins.isNotEmpty) {
1194 + unspentCoins.addAll(newUnspentCoins);
1195 +
1196 + newUnspentCoins.forEach((coin) {
1197 + final coinInfoList = unspentCoinsInfo.values.where(
1198 + (element) =>
1199 + element.walletId.contains(id) &&
1200 + element.hash.contains(coin.hash) &&
1201 + element.vout == coin.vout,
1202 + );
1203 +
1204 + if (coinInfoList.isNotEmpty) {
1205 + final coinInfo = coinInfoList.first;
1206 +
1207 + coin.isFrozen = coinInfo.isFrozen;
1208 + coin.isSending = coinInfo.isSending;
1209 + coin.note = coinInfo.note;
1210 + if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord)
1211 + coin.bitcoinAddressRecord.balance += coinInfo.value;
1212 + } else {
1213 + _addCoinInfo(coin);
1214 + }
1215 + });
1216 + }
1217 + }
1218 +
1219 + @action
1220 + Future<List<BitcoinUnspent>> fetchUnspent(BitcoinAddressRecord address) async {
1221 + final unspents = await electrumClient.getListUnspent(address.getScriptHash(network));
1222 +
1223 + List<BitcoinUnspent> updatedUnspentCoins = [];
1224 +
1225 + await Future.wait(unspents.map((unspent) async {
1226 + try {
1227 + final coin = BitcoinUnspent.fromJSON(address, unspent);
1228 + final tx = await fetchTransactionInfo(hash: coin.hash, height: 0);
1229 + coin.isChange = address.isHidden;
1230 + coin.confirmations = tx?.confirmations;
1231 +
1232 + updatedUnspentCoins.add(coin);
1233 + } catch (_) {}
1234 + }));
1235 +
1236 + return updatedUnspentCoins;
1237 + }
1238 +
1239 + @action
1240 Future<void> _addCoinInfo(BitcoinUnspent coin) async {
1241 final newInfo = UnspentCoinsInfo(
1242 walletId: id,
@@ -897,6 +1248,7 @@ abstract class ElectrumWalletBase
1248 value: coin.value,
1249 vout: coin.vout,
1250 isChange: coin.isChange,
1251 + isSilentPayment: coin is BitcoinSilentPaymentsUnspent,
1252 );
1253
1254 await unspentCoinsInfo.add(newInfo);
@@ -1112,8 +1464,9 @@ abstract class ElectrumWalletBase
1464 (await http.get(Uri.parse("https://blockstream.info/testnet/api/tx/$hash/status"))).body);
1465
1466 time = status["block_time"] as int?;
1115 - final tip = await electrumClient.getCurrentBlockChainTip() ?? 0;
1116 - confirmations = tip - (status["block_height"] as int? ?? 0);
1467 + final height = status["block_height"] as int? ?? 0;
1468 + final tip = await getCurrentChainTip();
1469 + if (tip > 0) confirmations = height > 0 ? tip - height + 1 : 0;
1470 } else {
1471 final verboseTransaction = await electrumClient.getTransactionRaw(hash: hash);
1472
@@ -1122,13 +1475,11 @@ abstract class ElectrumWalletBase
1475 confirmations = verboseTransaction['confirmations'] as int? ?? 0;
1476 }
1477
1125 - final original = bitcoin_base.BtcTransaction.fromRaw(transactionHex);
1126 - final ins = <bitcoin_base.BtcTransaction>[];
1478 + final original = BtcTransaction.fromRaw(transactionHex);
1479 + final ins = <BtcTransaction>[];
1480
1481 for (final vin in original.inputs) {
1129 - final txHex = await electrumClient.getTransactionHex(hash: vin.txId);
1130 - final tx = bitcoin_base.BtcTransaction.fromRaw(txHex);
1131 - ins.add(tx);
1482 + ins.add(BtcTransaction.fromRaw(await electrumClient.getTransactionHex(hash: vin.txId)));
1483 }
1484
1485 return ElectrumTransactionBundle(
@@ -1140,18 +1491,15 @@ abstract class ElectrumWalletBase
1491 }
1492
1493 Future<ElectrumTransactionInfo?> fetchTransactionInfo(
1143 - {required String hash,
1144 - required int height,
1145 - required Set<String> myAddresses,
1146 - bool? retryOnFailure}) async {
1494 + {required String hash, required int height, bool? retryOnFailure}) async {
1495 try {
1496 return ElectrumTransactionInfo.fromElectrumBundle(
1497 await getTransactionExpanded(hash: hash), walletInfo.type, network,
1150 - addresses: myAddresses, height: height);
1498 + addresses: addressesSet, height: height);
1499 } catch (e) {
1500 if (e is FormatException && retryOnFailure == true) {
1501 await Future.delayed(const Duration(seconds: 2));
1154 - return fetchTransactionInfo(hash: hash, height: height, myAddresses: myAddresses);
1502 + return fetchTransactionInfo(hash: hash, height: height);
1503 }
1504 return null;
1505 }
@@ -1161,39 +1509,15 @@ abstract class ElectrumWalletBase
1509 Future<Map<String, ElectrumTransactionInfo>> fetchTransactions() async {
1510 try {
1511 final Map<String, ElectrumTransactionInfo> historiesWithDetails = {};
1164 - final addressesSet = walletAddresses.allAddresses.map((addr) => addr.address).toSet();
1165 - final currentHeight = await electrumClient.getCurrentBlockChainTip() ?? 0;
1166 -
1167 - await Future.wait(ADDRESS_TYPES.map((type) {
1168 - final addressesByType = walletAddresses.allAddresses.where((addr) => addr.type == type);
1169 -
1170 - return Future.wait(addressesByType.map((addressRecord) async {
1171 - final history = await _fetchAddressHistory(addressRecord, addressesSet, currentHeight);
1172 - final balance = await electrumClient.getBalance(addressRecord.scriptHash!);
1173 -
1174 - if (history.isNotEmpty) {
1175 - addressRecord.txCount = history.length;
1176 - addressRecord.balance = balance['confirmed'] as int? ?? 0;
1177 - historiesWithDetails.addAll(history);
1178 -
1179 - final matchedAddresses =
1180 - addressesByType.where((addr) => addr.isHidden == addressRecord.isHidden);
1181 -
1182 - final isLastUsedAddress =
1183 - history.isNotEmpty && addressRecord.address == matchedAddresses.last.address;
1184 -
1185 - if (isLastUsedAddress) {
1186 - await walletAddresses.discoverAddresses(
1187 - matchedAddresses.toList(),
1188 - addressRecord.isHidden,
1189 - (address, addressesSet) =>
1190 - _fetchAddressHistory(address, addressesSet, currentHeight)
1191 - .then((history) => history.isNotEmpty ? address.address : null),
1192 - type: type);
1193 - }
1194 - }
1195 - }));
1196 - }));
1512 +
1513 + if (type == WalletType.bitcoin) {
1514 + await Future.wait(ADDRESS_TYPES
1515 + .map((type) => fetchTransactionsForAddressType(historiesWithDetails, type)));
1516 + } else if (type == WalletType.bitcoinCash) {
1517 + await fetchTransactionsForAddressType(historiesWithDetails, P2pkhAddressType.p2pkh);
1518 + } else if (type == WalletType.litecoin) {
1519 + await fetchTransactionsForAddressType(historiesWithDetails, SegwitAddresType.p2wpkh);
1520 + }
1521
1522 return historiesWithDetails;
1523 } catch (e) {
@@ -1202,13 +1526,59 @@ abstract class ElectrumWalletBase
1526 }
1527 }
1528
1529 + Future<void> fetchTransactionsForAddressType(
1530 + Map<String, ElectrumTransactionInfo> historiesWithDetails,
1531 + BitcoinAddressType type,
1532 + ) async {
1533 + final addressesByType = walletAddresses.allAddresses.where((addr) => addr.type == type);
1534 + final hiddenAddresses = addressesByType.where((addr) => addr.isHidden == true);
1535 + final receiveAddresses = addressesByType.where((addr) => addr.isHidden == false);
1536 +
1537 + await Future.wait(addressesByType.map((addressRecord) async {
1538 + final history = await _fetchAddressHistory(addressRecord, await getCurrentChainTip());
1539 +
1540 + if (history.isNotEmpty) {
1541 + addressRecord.txCount = history.length;
1542 + historiesWithDetails.addAll(history);
1543 +
1544 + final matchedAddresses = addressRecord.isHidden ? hiddenAddresses : receiveAddresses;
1545 + final isUsedAddressUnderGap = matchedAddresses.toList().indexOf(addressRecord) >=
1546 + matchedAddresses.length -
1547 + (addressRecord.isHidden
1548 + ? ElectrumWalletAddressesBase.defaultChangeAddressesCount
1549 + : ElectrumWalletAddressesBase.defaultReceiveAddressesCount);
1550 +
1551 + if (isUsedAddressUnderGap) {
1552 + final prevLength = walletAddresses.allAddresses.length;
1553 +
1554 + // Discover new addresses for the same address type until the gap limit is respected
1555 + await walletAddresses.discoverAddresses(
1556 + matchedAddresses.toList(),
1557 + addressRecord.isHidden,
1558 + (address) async {
1559 + await _subscribeForUpdates();
1560 + return _fetchAddressHistory(address, await getCurrentChainTip())
1561 + .then((history) => history.isNotEmpty ? address.address : null);
1562 + },
1563 + type: type,
1564 + );
1565 +
1566 + final newLength = walletAddresses.allAddresses.length;
1567 +
1568 + if (newLength > prevLength) {
1569 + await fetchTransactionsForAddressType(historiesWithDetails, type);
1570 + }
1571 + }
1572 + }
1573 + }));
1574 + }
1575 +
1576 Future<Map<String, ElectrumTransactionInfo>> _fetchAddressHistory(
1206 - BitcoinAddressRecord addressRecord, Set<String> addressesSet, int currentHeight) async {
1577 + BitcoinAddressRecord addressRecord, int? currentHeight) async {
1578 try {
1579 final Map<String, ElectrumTransactionInfo> historiesWithDetails = {};
1580
1210 - final history = await electrumClient
1211 - .getHistory(addressRecord.scriptHash ?? addressRecord.updateScriptHash(network));
1581 + final history = await electrumClient.getHistory(addressRecord.getScriptHash(network));
1582
1583 if (history.isNotEmpty) {
1584 addressRecord.setAsUsed();
@@ -1222,14 +1592,13 @@ abstract class ElectrumWalletBase
1592 if (height > 0) {
1593 storedTx.height = height;
1594 // the tx's block itself is the first confirmation so add 1
1225 - storedTx.confirmations = currentHeight - height + 1;
1595 + if (currentHeight != null) storedTx.confirmations = currentHeight - height + 1;
1596 storedTx.isPending = storedTx.confirmations == 0;
1597 }
1598
1599 historiesWithDetails[txid] = storedTx;
1600 } else {
1231 - final tx = await fetchTransactionInfo(
1232 - hash: txid, height: height, myAddresses: addressesSet, retryOnFailure: true);
1601 + final tx = await fetchTransactionInfo(hash: txid, height: height, retryOnFailure: true);
1602
1603 if (tx != null) {
1604 historiesWithDetails[txid] = tx;
@@ -1258,6 +1627,12 @@ abstract class ElectrumWalletBase
1627 return;
1628 }
1629
1630 + transactionHistory.transactions.values.forEach((tx) async {
1631 + if (tx.unspents != null && tx.unspents!.isNotEmpty && tx.height > 0) {
1632 + tx.confirmations = await getCurrentChainTip() - tx.height + 1;
1633 + }
1634 + });
1635 +
1636 _isTransactionUpdating = true;
1637 await fetchTransactions();
1638 walletAddresses.updateReceiveAddresses();
@@ -1269,15 +1644,22 @@ abstract class ElectrumWalletBase
1644 }
1645 }
1646
1272 - void _subscribeForUpdates() {
1273 - scriptHashes.forEach((sh) async {
1647 + Future<void> _subscribeForUpdates() async {
1648 + final unsubscribedScriptHashes = walletAddresses.allAddresses.where(
1649 + (address) => !_scripthashesUpdateSubject.containsKey(address.getScriptHash(network)),
1650 + );
1651 +
1652 + await Future.wait(unsubscribedScriptHashes.map((address) async {
1653 + final sh = address.getScriptHash(network);
1654 await _scripthashesUpdateSubject[sh]?.close();
1275 - _scripthashesUpdateSubject[sh] = electrumClient.scripthashUpdate(sh);
1655 + _scripthashesUpdateSubject[sh] = await electrumClient.scripthashUpdate(sh);
1656 _scripthashesUpdateSubject[sh]?.listen((event) async {
1657 try {
1278 - await updateUnspent();
1658 + await updateUnspents(address);
1659 +
1660 await updateBalance();
1280 - await updateTransactions();
1661 +
1662 + await _fetchAddressHistory(address, await getCurrentChainTip());
1663 } catch (e, s) {
1664 print(e.toString());
1665 _onError?.call(FlutterErrorDetails(
@@ -1287,7 +1669,7 @@ abstract class ElectrumWalletBase
1669 ));
1670 }
1671 });
1290 - });
1672 + }));
1673 }
1674
1675 Future<ElectrumBalance> _fetchBalances() async {
@@ -1301,21 +1683,25 @@ abstract class ElectrumWalletBase
1683 }
1684
1685 var totalFrozen = 0;
1304 - unspentCoinsInfo.values.forEach((info) {
1305 - unspentCoins.forEach((element) {
1306 - if (element.hash == info.hash &&
1307 - element.vout == info.vout &&
1308 - info.isFrozen &&
1309 - element.bitcoinAddressRecord.address == info.address &&
1310 - element.value == info.value) {
1311 - totalFrozen += element.value;
1686 + var totalConfirmed = 0;
1687 + var totalUnconfirmed = 0;
1688 +
1689 + if (hasSilentPaymentsScanning) {
1690 + // Add values from unspent coins that are not fetched by the address list
1691 + // i.e. scanned silent payments
1692 + transactionHistory.transactions.values.forEach((tx) {
1693 + if (tx.unspents != null) {
1694 + tx.unspents!.forEach((unspent) {
1695 + if (unspent.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) {
1696 + if (unspent.isFrozen) totalFrozen += unspent.value;
1697 + totalConfirmed += unspent.value;
1698 + }
1699 + });
1700 }
1701 });
1314 - });
1702 + }
1703
1704 final balances = await Future.wait(balanceFutures);
1317 - var totalConfirmed = 0;
1318 - var totalUnconfirmed = 0;
1705
1706 for (var i = 0; i < balances.length; i++) {
1707 final addressRecord = addresses[i];
@@ -1363,6 +1749,29 @@ abstract class ElectrumWalletBase
1749 return base64Encode(HD.signMessage(message));
1750 }
1751
1752 + Future<void> _setInitialHeight() async {
1753 + if (_chainTipUpdateSubject != null) return;
1754 +
1755 + if ((_currentChainTip == null || _currentChainTip! == 0) && walletInfo.restoreHeight == 0) {
1756 + await getUpdatedChainTip();
1757 + await walletInfo.updateRestoreHeight(_currentChainTip!);
1758 + }
1759 +
1760 + _chainTipUpdateSubject = electrumClient.chainTipSubscribe();
1761 + _chainTipUpdateSubject?.listen((e) async {
1762 + final event = e as Map<String, dynamic>;
1763 + final height = int.tryParse(event['height'].toString());
1764 +
1765 + if (height != null) {
1766 + _currentChainTip = height;
1767 +
1768 + if (alwaysScan == true && syncStatus is SyncedSyncStatus) {
1769 + _setListeners(walletInfo.restoreHeight);
1770 + }
1771 + }
1772 + });
1773 + }
1774 +
1775 static BasedUtxoNetwork _getNetwork(bitcoin.NetworkType networkType, CryptoCurrency? currency) {
1776 if (networkType == bitcoin.bitcoin && currency == CryptoCurrency.bch) {
1777 return BitcoinCashNetwork.mainnet;
@@ -1383,39 +1792,248 @@ abstract class ElectrumWalletBase
1792 derivationPath.substring(0, derivationPath.lastIndexOf("'") + 1);
1793 }
1794
1386 -class EstimateTxParams {
1387 - EstimateTxParams(
1388 - {required this.amount,
1389 - required this.feeRate,
1390 - required this.priority,
1391 - required this.outputsCount,
1392 - required this.size});
1795 +class ScanNode {
1796 + final Uri uri;
1797 + final bool? useSSL;
1798
1394 - final int amount;
1395 - final int feeRate;
1396 - final TransactionPriority priority;
1397 - final int outputsCount;
1398 - final int size;
1799 + ScanNode(this.uri, this.useSSL);
1800 +}
1801 +
1802 +class ScanData {
1803 + final SendPort sendPort;
1804 + final SilentPaymentOwner silentAddress;
1805 + final int height;
1806 + final ScanNode node;
1807 + final BasedUtxoNetwork network;
1808 + final int chainTip;
1809 + final ElectrumClient electrumClient;
1810 + final List<String> transactionHistoryIds;
1811 + final Map<String, String> labels;
1812 + final List<int> labelIndexes;
1813 + final bool isSingleScan;
1814 +
1815 + ScanData({
1816 + required this.sendPort,
1817 + required this.silentAddress,
1818 + required this.height,
1819 + required this.node,
1820 + required this.network,
1821 + required this.chainTip,
1822 + required this.electrumClient,
1823 + required this.transactionHistoryIds,
1824 + required this.labels,
1825 + required this.labelIndexes,
1826 + required this.isSingleScan,
1827 + });
1828 +
1829 + factory ScanData.fromHeight(ScanData scanData, int newHeight) {
1830 + return ScanData(
1831 + sendPort: scanData.sendPort,
1832 + silentAddress: scanData.silentAddress,
1833 + height: newHeight,
1834 + node: scanData.node,
1835 + network: scanData.network,
1836 + chainTip: scanData.chainTip,
1837 + transactionHistoryIds: scanData.transactionHistoryIds,
1838 + electrumClient: scanData.electrumClient,
1839 + labels: scanData.labels,
1840 + labelIndexes: scanData.labelIndexes,
1841 + isSingleScan: scanData.isSingleScan,
1842 + );
1843 + }
1844 +}
1845 +
1846 +class SyncResponse {
1847 + final int height;
1848 + final SyncStatus syncStatus;
1849 +
1850 + SyncResponse(this.height, this.syncStatus);
1851 +}
1852 +
1853 +Future<void> startRefresh(ScanData scanData) async {
1854 + int syncHeight = scanData.height;
1855 + int initialSyncHeight = syncHeight;
1856 +
1857 + BehaviorSubject<Object>? tweaksSubscription = null;
1858 +
1859 + final syncingStatus = scanData.isSingleScan
1860 + ? SyncingSyncStatus(1, 0)
1861 + : SyncingSyncStatus.fromHeightValues(scanData.chainTip, initialSyncHeight, syncHeight);
1862 +
1863 + // Initial status UI update, send how many blocks left to scan
1864 + scanData.sendPort.send(SyncResponse(syncHeight, syncingStatus));
1865 +
1866 + final electrumClient = scanData.electrumClient;
1867 + await electrumClient.connectToUri(scanData.node.uri, useSSL: scanData.node.useSSL);
1868 +
1869 + if (tweaksSubscription == null) {
1870 + final count = scanData.isSingleScan ? 1 : TWEAKS_COUNT;
1871 + final receiver = Receiver(
1872 + scanData.silentAddress.b_scan.toHex(),
1873 + scanData.silentAddress.B_spend.toHex(),
1874 + scanData.network == BitcoinNetwork.testnet,
1875 + scanData.labelIndexes,
1876 + scanData.labelIndexes.length,
1877 + );
1878 +
1879 + tweaksSubscription = await electrumClient.tweaksSubscribe(height: syncHeight, count: count);
1880 + tweaksSubscription?.listen((t) async {
1881 + final tweaks = t as Map<String, dynamic>;
1882 +
1883 + if (tweaks["message"] != null) {
1884 + // re-subscribe to continue receiving messages
1885 + electrumClient.tweaksSubscribe(height: syncHeight, count: count);
1886 + return;
1887 + }
1888 +
1889 + final blockHeight = tweaks.keys.first;
1890 + final tweakHeight = int.parse(blockHeight);
1891 +
1892 + try {
1893 + final blockTweaks = tweaks[blockHeight] as Map<String, dynamic>;
1894 +
1895 + for (var j = 0; j < blockTweaks.keys.length; j++) {
1896 + final txid = blockTweaks.keys.elementAt(j);
1897 + final details = blockTweaks[txid] as Map<String, dynamic>;
1898 + final outputPubkeys = (details["output_pubkeys"] as Map<dynamic, dynamic>);
1899 + final tweak = details["tweak"].toString();
1900 +
1901 + try {
1902 + // scanOutputs called from rust here
1903 + final addToWallet = scanOutputs(
1904 + outputPubkeys.values.toList(),
1905 + tweak,
1906 + receiver,
1907 + );
1908 +
1909 + if (addToWallet.isEmpty) {
1910 + // no results tx, continue to next tx
1911 + continue;
1912 + }
1913 +
1914 + // placeholder ElectrumTransactionInfo object to update values based on new scanned unspent(s)
1915 + final txInfo = ElectrumTransactionInfo(
1916 + WalletType.bitcoin,
1917 + id: txid,
1918 + height: tweakHeight,
1919 + amount: 0,
1920 + fee: 0,
1921 + direction: TransactionDirection.incoming,
1922 + isPending: false,
1923 + date: scanData.network == BitcoinNetwork.mainnet
1924 + ? getDateByBitcoinHeight(tweakHeight)
1925 + : DateTime.now(),
1926 + confirmations: scanData.chainTip - tweakHeight + 1,
1927 + unspents: [],
1928 + );
1929 +
1930 + addToWallet.forEach((label, value) {
1931 + (value as Map<String, dynamic>).forEach((output, tweak) {
1932 + final t_k = tweak.toString();
1933 +
1934 + final receivingOutputAddress = ECPublic.fromHex(output)
1935 + .toTaprootAddress(tweak: false)
1936 + .toAddress(scanData.network);
1937 +
1938 + int? amount;
1939 + int? pos;
1940 + outputPubkeys.entries.firstWhere((k) {
1941 + final isMatchingOutput = k.value[0] == output;
1942 + if (isMatchingOutput) {
1943 + amount = int.parse(k.value[1].toString());
1944 + pos = int.parse(k.key.toString());
1945 + return true;
1946 + }
1947 + return false;
1948 + });
1949 +
1950 + final receivedAddressRecord = BitcoinSilentPaymentAddressRecord(
1951 + receivingOutputAddress,
1952 + index: 0,
1953 + isHidden: false,
1954 + isUsed: true,
1955 + network: scanData.network,
1956 + silentPaymentTweak: t_k,
1957 + type: SegwitAddresType.p2tr,
1958 + txCount: 1,
1959 + balance: amount!,
1960 + );
1961 +
1962 + final unspent = BitcoinSilentPaymentsUnspent(
1963 + receivedAddressRecord,
1964 + txid,
1965 + amount!,
1966 + pos!,
1967 + silentPaymentTweak: t_k,
1968 + silentPaymentLabel: label == "None" ? null : label,
1969 + );
1970 +
1971 + txInfo.unspents!.add(unspent);
1972 + txInfo.amount += unspent.value;
1973 + });
1974 + });
1975 +
1976 + scanData.sendPort.send({txInfo.id: txInfo});
1977 + } catch (_) {}
1978 + }
1979 + } catch (_) {}
1980 +
1981 + syncHeight = tweakHeight;
1982 + scanData.sendPort.send(
1983 + SyncResponse(
1984 + syncHeight,
1985 + SyncingSyncStatus.fromHeightValues(
1986 + scanData.chainTip,
1987 + initialSyncHeight,
1988 + syncHeight,
1989 + ),
1990 + ),
1991 + );
1992 +
1993 + if (tweakHeight >= scanData.chainTip || scanData.isSingleScan) {
1994 + if (tweakHeight >= scanData.chainTip)
1995 + scanData.sendPort.send(SyncResponse(
1996 + syncHeight,
1997 + SyncedTipSyncStatus(scanData.chainTip),
1998 + ));
1999 +
2000 + if (scanData.isSingleScan) {
2001 + scanData.sendPort.send(SyncResponse(syncHeight, SyncedSyncStatus()));
2002 + }
2003 +
2004 + await tweaksSubscription!.close();
2005 + await electrumClient.close();
2006 + }
2007 + });
2008 + }
2009 +
2010 + if (tweaksSubscription == null) {
2011 + return scanData.sendPort.send(
2012 + SyncResponse(syncHeight, UnsupportedSyncStatus()),
2013 + );
2014 + }
2015 }
2016
2017 class EstimatedTxResult {
2018 EstimatedTxResult({
2019 required this.utxos,
1404 - required this.privateKeys,
2020 + required this.inputPrivKeyInfos,
2021 required this.publicKeys,
2022 required this.fee,
2023 required this.amount,
2024 required this.hasChange,
2025 required this.isSendAll,
2026 this.memo,
2027 + required this.spendsSilentPayment,
2028 required this.spendsUnconfirmedTX,
2029 });
2030
2031 final List<UtxoWithAddress> utxos;
1415 - final List<ECPrivate> privateKeys;
2032 + final List<ECPrivateInfo> inputPrivKeyInfos;
2033 final Map<String, PublicKeyWithDerivationPath> publicKeys; // PubKey to derivationPath
2034 final int fee;
2035 final int amount;
2036 + final bool spendsSilentPayment;
2037 final bool hasChange;
2038 final bool isSendAll;
2039 final String? memo;
@@ -1447,6 +2065,8 @@ BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network)
2065 return P2wshAddress.fromAddress(address: address, network: network);
2066 } else if (P2trAddress.regex.hasMatch(address)) {
2067 return P2trAddress.fromAddress(address: address, network: network);
2068 + } else if (SilentPaymentAddress.regex.hasMatch(address)) {
2069 + return SilentPaymentAddress.fromAddress(address);
2070 } else {
2071 return P2wpkhAddress.fromAddress(address: address, network: network);
2072 }
@@ -1461,7 +2081,33 @@ BitcoinAddressType _getScriptType(BitcoinBaseAddress type) {
2081 return SegwitAddresType.p2wsh;
2082 } else if (type is P2trAddress) {
2083 return SegwitAddresType.p2tr;
2084 + } else if (type is SilentPaymentsAddresType) {
2085 + return SilentPaymentsAddresType.p2sp;
2086 } else {
2087 return SegwitAddresType.p2wpkh;
2088 }
2089 }
2090 +
2091 +class UtxoDetails {
2092 + final List<BitcoinUnspent> availableInputs;
2093 + final List<BitcoinUnspent> unconfirmedCoins;
2094 + final List<UtxoWithAddress> utxos;
2095 + final List<Outpoint> vinOutpoints;
2096 + final List<ECPrivateInfo> inputPrivKeyInfos;
2097 + final Map<String, PublicKeyWithDerivationPath> publicKeys; // PubKey to derivationPath
2098 + final int allInputsAmount;
2099 + final bool spendsSilentPayment;
2100 + final bool spendsUnconfirmedTX;
2101 +
2102 + UtxoDetails({
2103 + required this.availableInputs,
2104 + required this.unconfirmedCoins,
2105 + required this.utxos,
2106 + required this.vinOutpoints,
2107 + required this.inputPrivKeyInfos,
2108 + required this.publicKeys,
2109 + required this.allInputsAmount,
2110 + required this.spendsSilentPayment,
2111 + required this.spendsUnconfirmedTX,
2112 + });
2113 +}
cw_bitcoin/lib/electrum_wallet_addresses.dart
+221 -18
@@ -1,7 +1,7 @@
1 import 'package:bitcoin_base/bitcoin_base.dart';
2 import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
3 +import 'package:blockchain_utils/blockchain_utils.dart';
4 import 'package:cw_bitcoin/bitcoin_address_record.dart';
4 -import 'package:cw_bitcoin/electrum.dart';
5 import 'package:cw_core/wallet_addresses.dart';
6 import 'package:cw_core/wallet_info.dart';
7 import 'package:cw_core/wallet_type.dart';
@@ -24,15 +24,17 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
24 WalletInfo walletInfo, {
25 required this.mainHd,
26 required this.sideHd,
27 - required this.electrumClient,
27 required this.network,
28 List<BitcoinAddressRecord>? initialAddresses,
29 Map<String, int>? initialRegularAddressIndex,
30 Map<String, int>? initialChangeAddressIndex,
31 + List<BitcoinSilentPaymentAddressRecord>? initialSilentAddresses,
32 + int initialSilentAddressIndex = 0,
33 + bitcoin.HDWallet? masterHd,
34 BitcoinAddressType? initialAddressPageType,
35 }) : _addresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? []).toSet()),
36 addressesByReceiveType =
35 - ObservableList<BitcoinAddressRecord>.of((<BitcoinAddressRecord>[]).toSet()),
37 + ObservableList<BaseBitcoinAddressRecord>.of((<BitcoinAddressRecord>[]).toSet()),
38 receiveAddresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? [])
39 .where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed)
40 .toSet()),
@@ -45,7 +47,38 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
47 (walletInfo.addressPageType != null
48 ? BitcoinAddressType.fromValue(walletInfo.addressPageType!)
49 : SegwitAddresType.p2wpkh),
50 + silentAddresses = ObservableList<BitcoinSilentPaymentAddressRecord>.of(
51 + (initialSilentAddresses ?? []).toSet()),
52 + currentSilentAddressIndex = initialSilentAddressIndex,
53 super(walletInfo) {
54 + if (masterHd != null) {
55 + silentAddress = SilentPaymentOwner.fromPrivateKeys(
56 + b_scan: ECPrivate.fromHex(masterHd.derivePath(SCAN_PATH).privKey!),
57 + b_spend: ECPrivate.fromHex(masterHd.derivePath(SPEND_PATH).privKey!),
58 + hrp: network == BitcoinNetwork.testnet ? 'tsp' : 'sp');
59 +
60 + if (silentAddresses.length == 0) {
61 + silentAddresses.add(BitcoinSilentPaymentAddressRecord(
62 + silentAddress.toString(),
63 + index: 0,
64 + isHidden: false,
65 + name: "",
66 + silentPaymentTweak: null,
67 + network: network,
68 + type: SilentPaymentsAddresType.p2sp,
69 + ));
70 + silentAddresses.add(BitcoinSilentPaymentAddressRecord(
71 + silentAddress!.toLabeledSilentPaymentAddress(0).toString(),
72 + index: 0,
73 + isHidden: true,
74 + name: "",
75 + silentPaymentTweak: BytesUtils.toHexString(silentAddress!.generateLabel(0)),
76 + network: network,
77 + type: SilentPaymentsAddresType.p2sp,
78 + ));
79 + }
80 + }
81 +
82 updateAddressesByMatch();
83 }
84
@@ -54,27 +87,40 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
87 static const gap = 20;
88
89 final ObservableList<BitcoinAddressRecord> _addresses;
57 - // Matched by addressPageType
58 - late ObservableList<BitcoinAddressRecord> addressesByReceiveType;
90 + late ObservableList<BaseBitcoinAddressRecord> addressesByReceiveType;
91 final ObservableList<BitcoinAddressRecord> receiveAddresses;
92 final ObservableList<BitcoinAddressRecord> changeAddresses;
61 - final ElectrumClient electrumClient;
93 + final ObservableList<BitcoinSilentPaymentAddressRecord> silentAddresses;
94 final BasedUtxoNetwork network;
95 final bitcoin.HDWallet mainHd;
96 final bitcoin.HDWallet sideHd;
97
98 + @observable
99 + SilentPaymentOwner? silentAddress;
100 +
101 @observable
102 late BitcoinAddressType _addressPageType;
103
104 @computed
105 BitcoinAddressType get addressPageType => _addressPageType;
106
107 + @observable
108 + String? activeSilentAddress;
109 +
110 @computed
111 List<BitcoinAddressRecord> get allAddresses => _addresses;
112
113 @override
114 @computed
115 String get address {
116 + if (addressPageType == SilentPaymentsAddresType.p2sp) {
117 + if (activeSilentAddress != null) {
118 + return activeSilentAddress!;
119 + }
120 +
121 + return silentAddress.toString();
122 + }
123 +
124 String receiveAddress;
125
126 final typeMatchingReceiveAddresses =
@@ -103,6 +149,18 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
149
150 @override
151 set address(String addr) {
152 + if (addressPageType == SilentPaymentsAddresType.p2sp) {
153 + final selected = silentAddresses.firstWhere((addressRecord) => addressRecord.address == addr);
154 +
155 + if (selected.silentPaymentTweak != null && silentAddress != null) {
156 + activeSilentAddress =
157 + silentAddress!.toLabeledSilentPaymentAddress(selected.index).toString();
158 + } else {
159 + activeSilentAddress = silentAddress!.toString();
160 + }
161 + return;
162 + }
163 +
164 final addressRecord = _addresses.firstWhere((addressRecord) => addressRecord.address == addr);
165
166 previousAddressRecord = addressRecord;
@@ -129,6 +187,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
187 void set currentChangeAddressIndex(int index) =>
188 currentChangeAddressIndexByType[_addressPageType.toString()] = index;
189
190 + int currentSilentAddressIndex;
191 +
192 @observable
193 BitcoinAddressRecord? previousAddressRecord;
194
@@ -196,7 +256,50 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
256 return address;
257 }
258
199 - BitcoinAddressRecord generateNewAddress({String label = ''}) {
259 + Map<String, String> get labels {
260 + final G = ECPublic.fromBytes(BigintUtils.toBytes(Curves.generatorSecp256k1.x, length: 32));
261 + final labels = <String, String>{};
262 + for (int i = 0; i < silentAddresses.length; i++) {
263 + final silentAddressRecord = silentAddresses[i];
264 + final silentPaymentTweak = silentAddressRecord.silentPaymentTweak;
265 +
266 + if (silentPaymentTweak != null &&
267 + SilentPaymentAddress.regex.hasMatch(silentAddressRecord.address)) {
268 + labels[G
269 + .tweakMul(BigintUtils.fromBytes(BytesUtils.fromHexString(silentPaymentTweak)))
270 + .toHex()] = silentPaymentTweak;
271 + }
272 + }
273 + return labels;
274 + }
275 +
276 + @action
277 + BaseBitcoinAddressRecord generateNewAddress({String label = ''}) {
278 + if (addressPageType == SilentPaymentsAddresType.p2sp && silentAddress != null) {
279 + final currentSilentAddressIndex = silentAddresses
280 + .where((addressRecord) => addressRecord.type != SegwitAddresType.p2tr)
281 + .length -
282 + 1;
283 +
284 + this.currentSilentAddressIndex = currentSilentAddressIndex;
285 +
286 + final address = BitcoinSilentPaymentAddressRecord(
287 + silentAddress!.toLabeledSilentPaymentAddress(currentSilentAddressIndex).toString(),
288 + index: currentSilentAddressIndex,
289 + isHidden: false,
290 + name: label,
291 + silentPaymentTweak:
292 + BytesUtils.toHexString(silentAddress!.generateLabel(currentSilentAddressIndex)),
293 + network: network,
294 + type: SilentPaymentsAddresType.p2sp,
295 + );
296 +
297 + silentAddresses.add(address);
298 + updateAddressesByMatch();
299 +
300 + return address;
301 + }
302 +
303 final newAddressIndex = addressesByReceiveType.fold(
304 0, (int acc, addressRecord) => addressRecord.isHidden == false ? acc + 1 : acc);
305
@@ -221,12 +324,70 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
324 Future<void> updateAddressesInBox() async {
325 try {
326 addressesMap.clear();
224 - addressesMap[address] = '';
327 + addressesMap[address] = 'Active';
328
329 allAddressesMap.clear();
330 _addresses.forEach((addressRecord) {
331 allAddressesMap[addressRecord.address] = addressRecord.name;
332 });
333 +
334 + final lastP2wpkh = _addresses
335 + .where((addressRecord) =>
336 + _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh))
337 + .toList()
338 + .last;
339 + if (lastP2wpkh.address != address) {
340 + addressesMap[lastP2wpkh.address] = 'P2WPKH';
341 + } else {
342 + addressesMap[address] = 'Active - P2WPKH';
343 + }
344 +
345 + final lastP2pkh = _addresses.firstWhere(
346 + (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, P2pkhAddressType.p2pkh));
347 + if (lastP2pkh.address != address) {
348 + addressesMap[lastP2pkh.address] = 'P2PKH';
349 + } else {
350 + addressesMap[address] = 'Active - P2PKH';
351 + }
352 +
353 + final lastP2sh = _addresses.firstWhere((addressRecord) =>
354 + _isUnusedReceiveAddressByType(addressRecord, P2shAddressType.p2wpkhInP2sh));
355 + if (lastP2sh.address != address) {
356 + addressesMap[lastP2sh.address] = 'P2SH';
357 + } else {
358 + addressesMap[address] = 'Active - P2SH';
359 + }
360 +
361 + final lastP2tr = _addresses.firstWhere(
362 + (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2tr));
363 + if (lastP2tr.address != address) {
364 + addressesMap[lastP2tr.address] = 'P2TR';
365 + } else {
366 + addressesMap[address] = 'Active - P2TR';
367 + }
368 +
369 + final lastP2wsh = _addresses.firstWhere(
370 + (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wsh));
371 + if (lastP2wsh.address != address) {
372 + addressesMap[lastP2wsh.address] = 'P2WSH';
373 + } else {
374 + addressesMap[address] = 'Active - P2WSH';
375 + }
376 +
377 + silentAddresses.forEach((addressRecord) {
378 + if (addressRecord.type != SilentPaymentsAddresType.p2sp || addressRecord.isHidden) {
379 + return;
380 + }
381 +
382 + if (addressRecord.address != address) {
383 + addressesMap[addressRecord.address] = addressRecord.name.isEmpty
384 + ? "Silent Payments"
385 + : "Silent Payments - " + addressRecord.name;
386 + } else {
387 + addressesMap[address] = 'Active - Silent Payments';
388 + }
389 + });
390 +
391 await saveAddressesInBox();
392 } catch (e) {
393 print(e.toString());
@@ -235,18 +396,41 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
396
397 @action
398 void updateAddress(String address, String label) {
238 - final addressRecord =
239 - _addresses.firstWhere((addressRecord) => addressRecord.address == address);
240 - addressRecord.setNewName(label);
241 - final index = _addresses.indexOf(addressRecord);
242 - _addresses.remove(addressRecord);
243 - _addresses.insert(index, addressRecord);
399 + BaseBitcoinAddressRecord? foundAddress;
400 + _addresses.forEach((addressRecord) {
401 + if (addressRecord.address == address) {
402 + foundAddress = addressRecord;
403 + }
404 + });
405 + silentAddresses.forEach((addressRecord) {
406 + if (addressRecord.address == address) {
407 + foundAddress = addressRecord;
408 + }
409 + });
410
245 - updateAddressesByMatch();
411 + if (foundAddress != null) {
412 + foundAddress!.setNewName(label);
413 +
414 + if (foundAddress is BitcoinAddressRecord) {
415 + final index = _addresses.indexOf(foundAddress);
416 + _addresses.remove(foundAddress);
417 + _addresses.insert(index, foundAddress as BitcoinAddressRecord);
418 + } else {
419 + final index = silentAddresses.indexOf(foundAddress as BitcoinSilentPaymentAddressRecord);
420 + silentAddresses.remove(foundAddress);
421 + silentAddresses.insert(index, foundAddress as BitcoinSilentPaymentAddressRecord);
422 + }
423 + }
424 }
425
426 @action
427 void updateAddressesByMatch() {
428 + if (addressPageType == SilentPaymentsAddresType.p2sp) {
429 + addressesByReceiveType.clear();
430 + addressesByReceiveType.addAll(silentAddresses);
431 + return;
432 + }
433 +
434 addressesByReceiveType.clear();
435 addressesByReceiveType.addAll(_addresses.where(_isAddressPageTypeMatch).toList());
436 }
@@ -272,7 +456,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
456
457 @action
458 Future<void> discoverAddresses(List<BitcoinAddressRecord> addressList, bool isHidden,
275 - Future<String?> Function(BitcoinAddressRecord, Set<String>) getAddressHistory,
459 + Future<String?> Function(BitcoinAddressRecord) getAddressHistory,
460 {BitcoinAddressType type = SegwitAddresType.p2wpkh}) async {
461 if (!isHidden) {
462 _validateSideHdAddresses(addressList.toList());
@@ -282,8 +466,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
466 startIndex: addressList.length, isHidden: isHidden, type: type);
467 addAddresses(newAddresses);
468
285 - final addressesWithHistory = await Future.wait(newAddresses
286 - .map((addr) => getAddressHistory(addr, _addresses.map((e) => e.address).toSet())));
469 + final addressesWithHistory = await Future.wait(newAddresses.map(getAddressHistory));
470 final isLastAddressUsed = addressesWithHistory.last == addressList.last.address;
471
472 if (isLastAddressUsed) {
@@ -349,6 +532,15 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
532 updateAddressesByMatch();
533 }
534
535 + @action
536 + void addSilentAddresses(Iterable<BitcoinSilentPaymentAddressRecord> addresses) {
537 + final addressesSet = this.silentAddresses.toSet();
538 + addressesSet.addAll(addresses);
539 + this.silentAddresses.clear();
540 + this.silentAddresses.addAll(addressesSet);
541 + updateAddressesByMatch();
542 + }
543 +
544 void _validateSideHdAddresses(List<BitcoinAddressRecord> addrWithTransactions) {
545 addrWithTransactions.forEach((element) {
546 if (element.address !=
@@ -371,4 +563,15 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
563
564 bitcoin.HDWallet _getHd(bool isHidden) => isHidden ? sideHd : mainHd;
565 bool _isAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => addr.type == type;
566 + bool _isUnusedReceiveAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) =>
567 + !addr.isHidden && !addr.isUsed && addr.type == type;
568 +
569 + @action
570 + void deleteSilentPaymentAddress(String address) {
571 + final addressRecord = silentAddresses.firstWhere((addressRecord) =>
572 + addressRecord.type == SilentPaymentsAddresType.p2sp && addressRecord.address == address);
573 +
574 + silentAddresses.remove(addressRecord);
575 + updateAddressesByMatch();
576 + }
577 }
cw_bitcoin/lib/electrum_wallet_snapshot.dart
+19 -4
@@ -19,6 +19,8 @@ class ElectrumWalletSnapshot {
19 required this.regularAddressIndex,
20 required this.changeAddressIndex,
21 required this.addressPageType,
22 + required this.silentAddresses,
23 + required this.silentAddressIndex,
24 this.passphrase,
25 this.derivationType,
26 this.derivationPath,
@@ -32,9 +34,11 @@ class ElectrumWalletSnapshot {
34 String? mnemonic;
35 String? xpub;
36 List<BitcoinAddressRecord> addresses;
37 + List<BitcoinSilentPaymentAddressRecord> silentAddresses;
38 ElectrumBalance balance;
39 Map<String, int> regularAddressIndex;
40 Map<String, int> changeAddressIndex;
41 + int silentAddressIndex;
42 String? passphrase;
43 DerivationType? derivationType;
44 String? derivationPath;
@@ -50,15 +54,23 @@ class ElectrumWalletSnapshot {
54 final passphrase = data['passphrase'] as String? ?? '';
55 final addresses = addressesTmp
56 .whereType<String>()
53 - .map((addr) => BitcoinAddressRecord.fromJSON(addr, network))
57 + .map((addr) => BitcoinAddressRecord.fromJSON(addr, network: network))
58 .toList();
55 - final balance = ElectrumBalance.fromJSON(data['balance'] as String) ??
59 +
60 + final silentAddressesTmp = data['silent_addresses'] as List? ?? <Object>[];
61 + final silentAddresses = silentAddressesTmp
62 + .whereType<String>()
63 + .map((addr) => BitcoinSilentPaymentAddressRecord.fromJSON(addr, network: network))
64 + .toList();
65 +
66 + final balance = ElectrumBalance.fromJSON(data['balance'] as String?) ??
67 ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0);
68 var regularAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0};
69 var changeAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0};
70 + var silentAddressIndex = 0;
71
60 - final derivationType =
61 - DerivationType.values[(data['derivationTypeIndex'] as int?) ?? DerivationType.electrum.index];
72 + final derivationType = DerivationType
73 + .values[(data['derivationTypeIndex'] as int?) ?? DerivationType.electrum.index];
74 final derivationPath = data['derivationPath'] as String? ?? "m/0'/0";
75
76 try {
@@ -69,6 +81,7 @@ class ElectrumWalletSnapshot {
81 SegwitAddresType.p2wpkh.toString():
82 int.parse(data['change_address_index'] as String? ?? '0')
83 };
84 + silentAddressIndex = int.parse(data['silent_address_index'] as String? ?? '0');
85 } catch (_) {
86 try {
87 regularAddressIndexByType = data["account_index"] as Map<String, int>? ?? {};
@@ -90,6 +103,8 @@ class ElectrumWalletSnapshot {
103 addressPageType: data['address_page_type'] as String?,
104 derivationType: derivationType,
105 derivationPath: derivationPath,
106 + silentAddresses: silentAddresses,
107 + silentAddressIndex: silentAddressIndex,
108 );
109 }
110 }
cw_bitcoin/lib/exceptions.dart
+5 -1
@@ -2,7 +2,7 @@ import 'package:cw_core/crypto_currency.dart';
2 import 'package:cw_core/exceptions.dart';
3
4 class BitcoinTransactionWrongBalanceException extends TransactionWrongBalanceException {
5 - BitcoinTransactionWrongBalanceException() : super(CryptoCurrency.btc);
5 + BitcoinTransactionWrongBalanceException({super.amount}) : super(CryptoCurrency.btc);
6 }
7
8 class BitcoinTransactionNoInputsException extends TransactionNoInputsException {}
@@ -27,3 +27,7 @@ class BitcoinTransactionCommitFailedDustOutputSendAll
27 extends TransactionCommitFailedDustOutputSendAll {}
28
29 class BitcoinTransactionCommitFailedVoutNegative extends TransactionCommitFailedVoutNegative {}
30 +
31 +class BitcoinTransactionCommitFailedBIP68Final extends TransactionCommitFailedBIP68Final {}
32 +
33 +class BitcoinTransactionSilentPaymentsNotSupported extends TransactionInputNotSupported {}
cw_bitcoin/lib/litecoin_wallet.dart
-1
@@ -44,7 +44,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
44 currency: CryptoCurrency.ltc) {
45 walletAddresses = LitecoinWalletAddresses(
46 walletInfo,
47 - electrumClient: electrumClient,
47 initialAddresses: initialAddresses,
48 initialRegularAddressIndex: initialRegularAddressIndex,
49 initialChangeAddressIndex: initialChangeAddressIndex,
cw_bitcoin/lib/litecoin_wallet_addresses.dart
-1
@@ -15,7 +15,6 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
15 required super.mainHd,
16 required super.sideHd,
17 required super.network,
18 - required super.electrumClient,
18 super.initialAddresses,
19 super.initialRegularAddressIndex,
20 super.initialChangeAddressIndex,
cw_bitcoin/lib/pending_bitcoin_transaction.dart
+5
@@ -73,6 +73,11 @@ class PendingBitcoinTransaction with PendingTransaction {
73 if (error.contains("bad-txns-vout-negative")) {
74 throw BitcoinTransactionCommitFailedVoutNegative();
75 }
76 +
77 + if (error.contains("non-BIP68-final")) {
78 + throw BitcoinTransactionCommitFailedBIP68Final();
79 + }
80 +
81 throw BitcoinTransactionCommitFailed(errorMessage: error);
82 }
83
cw_bitcoin/pubspec.lock
+89 -39
@@ -29,10 +29,10 @@ packages:
29 dependency: transitive
30 description:
31 name: asn1lib
32 - sha256: c9c85fedbe2188b95133cbe960e16f5f448860f7133330e272edbbca5893ddc6
32 + sha256: "58082b3f0dca697204dbab0ef9ff208bfaea7767ea771076af9a343488428dda"
33 url: "https://pub.dev"
34 source: hosted
35 - version: "1.5.2"
35 + version: "1.5.3"
36 async:
37 dependency: transitive
38 description:
@@ -79,11 +79,11 @@ packages:
79 dependency: "direct main"
80 description:
81 path: "."
82 - ref: cake-update-v2
83 - resolved-ref: "01d844a5f5a520a31df5254e34169af4664aa769"
84 - url: "https://github.com/cake-tech/bitcoin_base.git"
82 + ref: cake-update-v3
83 + resolved-ref: cc99eedb1d28ee9376dda0465ef72aa627ac6149
84 + url: "https://github.com/cake-tech/bitcoin_base"
85 source: git
86 - version: "4.2.0"
86 + version: "4.2.1"
87 bitcoin_flutter:
88 dependency: "direct main"
89 description:
@@ -96,11 +96,12 @@ packages:
96 blockchain_utils:
97 dependency: "direct main"
98 description:
99 - name: blockchain_utils
100 - sha256: "38ef5f4a22441ac4370aed9071dc71c460acffc37c79b344533f67d15f24c13c"
101 - url: "https://pub.dev"
102 - source: hosted
103 - version: "2.1.1"
99 + path: "."
100 + ref: cake-update-v1
101 + resolved-ref: cabd7e0e16c4da9920338c76eff3aeb8af0211f3
102 + url: "https://github.com/cake-tech/blockchain_utils"
103 + source: git
104 + version: "2.1.2"
105 boolean_selector:
106 dependency: transitive
107 description:
@@ -197,6 +198,14 @@ packages:
198 url: "https://pub.dev"
199 source: hosted
200 version: "2.0.3"
201 + cli_util:
202 + dependency: transitive
203 + description:
204 + name: cli_util
205 + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19
206 + url: "https://pub.dev"
207 + source: hosted
208 + version: "0.4.1"
209 clock:
210 dependency: transitive
211 description:
@@ -241,10 +250,10 @@ packages:
250 dependency: "direct main"
251 description:
252 name: cryptography
244 - sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35
253 + sha256: d146b76d33d94548cf035233fbc2f4338c1242fa119013bead807d033fc4ae05
254 url: "https://pub.dev"
255 source: hosted
247 - version: "2.5.0"
256 + version: "2.7.0"
257 cw_core:
258 dependency: "direct main"
259 description:
@@ -288,10 +297,18 @@ packages:
297 dependency: transitive
298 description:
299 name: ffi
291 - sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
300 + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
301 url: "https://pub.dev"
302 source: hosted
294 - version: "2.1.0"
303 + version: "2.1.2"
304 + ffigen:
305 + dependency: transitive
306 + description:
307 + name: ffigen
308 + sha256: d3e76c2ad48a4e7f93a29a162006f00eba46ce7c08194a77bb5c5e97d1b5ff0a
309 + url: "https://pub.dev"
310 + source: hosted
311 + version: "8.0.2"
312 file:
313 dependency: transitive
314 description:
@@ -346,10 +363,10 @@ packages:
363 dependency: transitive
364 description:
365 name: functional_data
349 - sha256: aefdec4365452283b2a7cf420a3169654d51d3e9553069a22d76680d7a9d7c3d
366 + sha256: "76d17dc707c40e552014f5a49c0afcc3f1e3f05e800cd6b7872940bfe41a5039"
367 url: "https://pub.dev"
368 source: hosted
352 - version: "1.1.1"
369 + version: "1.2.0"
370 glob:
371 dependency: transitive
372 description:
@@ -394,10 +411,10 @@ packages:
411 dependency: "direct main"
412 description:
413 name: http
397 - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
414 + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
415 url: "https://pub.dev"
416 source: hosted
400 - version: "1.1.0"
417 + version: "1.2.1"
418 http_multi_server:
419 dependency: transitive
420 description:
@@ -442,10 +459,10 @@ packages:
459 dependency: transitive
460 description:
461 name: json_annotation
445 - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
462 + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
463 url: "https://pub.dev"
464 source: hosted
448 - version: "4.8.1"
465 + version: "4.9.0"
466 leak_tracker:
467 dependency: transitive
468 description:
@@ -531,10 +548,10 @@ packages:
548 dependency: transitive
549 description:
550 name: mime
534 - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
551 + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2"
552 url: "https://pub.dev"
553 source: hosted
537 - version: "1.0.4"
554 + version: "1.0.5"
555 mobx:
556 dependency: "direct main"
557 description:
@@ -579,26 +596,26 @@ packages:
596 dependency: "direct main"
597 description:
598 name: path_provider
582 - sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b
599 + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
600 url: "https://pub.dev"
601 source: hosted
585 - version: "2.1.2"
602 + version: "2.1.3"
603 path_provider_android:
604 dependency: transitive
605 description:
606 name: path_provider_android
590 - sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668"
607 + sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d
608 url: "https://pub.dev"
609 source: hosted
593 - version: "2.2.2"
610 + version: "2.2.4"
611 path_provider_foundation:
612 dependency: transitive
613 description:
614 name: path_provider_foundation
598 - sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
615 + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
616 url: "https://pub.dev"
617 source: hosted
601 - version: "2.3.2"
618 + version: "2.4.0"
619 path_provider_linux:
620 dependency: transitive
621 description:
@@ -643,10 +660,10 @@ packages:
660 dependency: transitive
661 description:
662 name: pointycastle
646 - sha256: "70fe966348fe08c34bf929582f1d8247d9d9408130723206472b4687227e4333"
663 + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
664 url: "https://pub.dev"
665 source: hosted
649 - version: "3.8.0"
666 + version: "3.9.1"
667 pool:
668 dependency: transitive
669 description:
@@ -687,6 +704,14 @@ packages:
704 url: "https://pub.dev"
705 source: hosted
706 version: "1.2.3"
707 + quiver:
708 + dependency: transitive
709 + description:
710 + name: quiver
711 + sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47
712 + url: "https://pub.dev"
713 + source: hosted
714 + version: "3.2.1"
715 reactive_ble_mobile:
716 dependency: transitive
717 description:
@@ -736,10 +761,10 @@ packages:
761 dependency: transitive
762 description:
763 name: socks5_proxy
739 - sha256: "1d21b5606169654bbf4cfb904e8e6ed897e9f763358709f87310c757096d909a"
764 + sha256: "045cbba84f6e2b01c1c77634a63e926352bf110ef5f07fc462c6d43bbd4b6a83"
765 url: "https://pub.dev"
766 source: hosted
742 - version: "1.0.4"
767 + version: "1.0.5+dev.2"
768 source_gen:
769 dependency: transitive
770 description:
@@ -764,6 +789,15 @@ packages:
789 url: "https://pub.dev"
790 source: hosted
791 version: "1.10.0"
792 + sp_scanner:
793 + dependency: "direct main"
794 + description:
795 + path: "."
796 + ref: "sp_v1.0.0"
797 + resolved-ref: a9a4c6d051f37a15a3a52cc2a0094f24c68b62c5
798 + url: "https://github.com/cake-tech/sp_scanner"
799 + source: git
800 + version: "0.0.1"
801 stack_trace:
802 dependency: transitive
803 description:
@@ -860,22 +894,30 @@ packages:
894 url: "https://pub.dev"
895 source: hosted
896 version: "1.1.0"
897 + web:
898 + dependency: transitive
899 + description:
900 + name: web
901 + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
902 + url: "https://pub.dev"
903 + source: hosted
904 + version: "0.5.1"
905 web_socket_channel:
906 dependency: transitive
907 description:
908 name: web_socket_channel
867 - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
909 + sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42"
910 url: "https://pub.dev"
911 source: hosted
870 - version: "2.4.0"
912 + version: "2.4.5"
913 win32:
914 dependency: transitive
915 description:
916 name: win32
875 - sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3"
917 + sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb"
918 url: "https://pub.dev"
919 source: hosted
878 - version: "5.0.9"
920 + version: "5.5.0"
921 xdg_directories:
922 dependency: transitive
923 description:
@@ -892,6 +934,14 @@ packages:
934 url: "https://pub.dev"
935 source: hosted
936 version: "3.1.2"
937 + yaml_edit:
938 + dependency: transitive
939 + description:
940 + name: yaml_edit
941 + sha256: e9c1a3543d2da0db3e90270dbb1e4eebc985ee5e3ffe468d83224472b2194a5f
942 + url: "https://pub.dev"
943 + source: hosted
944 + version: "2.2.1"
945 sdks:
896 - dart: ">=3.2.0-0 <4.0.0"
897 - flutter: ">=3.10.0"
946 + dart: ">=3.3.0 <4.0.0"
947 + flutter: ">=3.16.6"
cw_bitcoin/pubspec.yaml
+11 -3
@@ -32,13 +32,21 @@ dependencies:
32 cryptography: ^2.0.5
33 bitcoin_base:
34 git:
35 - url: https://github.com/cake-tech/bitcoin_base.git
36 - ref: cake-update-v2
37 - blockchain_utils: ^2.1.1
35 + url: https://github.com/cake-tech/bitcoin_base
36 + ref: cake-update-v3
37 + blockchain_utils:
38 + git:
39 + url: https://github.com/cake-tech/blockchain_utils
40 + ref: cake-update-v1
41 ledger_flutter: ^1.0.1
42 ledger_bitcoin:
43 git:
44 url: https://github.com/cake-tech/ledger-bitcoin
45 + sp_scanner:
46 + git:
47 + url: https://github.com/cake-tech/sp_scanner
48 + ref: sp_v2.0.0
49 +
50
51 dev_dependencies:
52 flutter_test:
cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart
-1
@@ -46,7 +46,6 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
46 currency: CryptoCurrency.bch) {
47 walletAddresses = BitcoinCashWalletAddresses(
48 walletInfo,
49 - electrumClient: electrumClient,
49 initialAddresses: initialAddresses,
50 initialRegularAddressIndex: initialRegularAddressIndex,
51 initialChangeAddressIndex: initialChangeAddressIndex,
cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart
-1
@@ -15,7 +15,6 @@ abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses wi
15 required super.mainHd,
16 required super.sideHd,
17 required super.network,
18 - required super.electrumClient,
18 super.initialAddresses,
19 super.initialRegularAddressIndex,
20 super.initialChangeAddressIndex,
cw_bitcoin_cash/pubspec.yaml
+6 -4
@@ -31,10 +31,12 @@ dependencies:
31 ref: Add-Support-For-OP-Return-data
32 bitcoin_base:
33 git:
34 - url: https://github.com/cake-tech/bitcoin_base.git
35 - ref: cake-update-v2
36 -
37 -
34 + url: https://github.com/cake-tech/bitcoin_base
35 + ref: cake-update-v3
36 + blockchain_utils:
37 + git:
38 + url: https://github.com/cake-tech/blockchain_utils
39 + ref: cake-update-v1
40
41 dev_dependencies:
42 flutter_test:
cw_core/lib/crypto_currency.dart
+3 -1
@@ -104,6 +104,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
104 CryptoCurrency.digibyte,
105 CryptoCurrency.usdtSol,
106 CryptoCurrency.usdcTrc20,
107 + CryptoCurrency.tbtc,
108 ];
109
110 static const havenCurrencies = [
@@ -218,7 +219,8 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
219 static const kaspa = CryptoCurrency(title: 'KAS', fullName: 'Kaspa', raw: 89, name: 'kas', iconPath: 'assets/images/kaspa_icon.png', decimals: 8);
220 static const digibyte = CryptoCurrency(title: 'DGB', fullName: 'DigiByte', raw: 90, name: 'dgb', iconPath: 'assets/images/digibyte.png', decimals: 8);
221 static const usdtSol = CryptoCurrency(title: 'USDT', tag: 'SOL', fullName: 'USDT Tether', raw: 91, name: 'usdtsol', iconPath: 'assets/images/usdt_icon.png', decimals: 6);
221 - static const usdcTrc20 = CryptoCurrency(title: 'USDC', tag: 'TRX', fullName: 'USDC Coin', raw: 92, name: 'usdctrc20', iconPath: 'assets/images/usdc_icon.png', decimals: 6);
222 + static const usdcTrc20 = CryptoCurrency(title: 'USDC', tag: 'TRX', fullName: 'USDC Coin', raw: 92, name: 'usdctrc20', iconPath: 'assets/images/usdc_icon.png', decimals: 6);
223 + static const tbtc = CryptoCurrency(title: 'tBTC', fullName: 'Testnet Bitcoin', raw: 93, name: 'tbtc', iconPath: 'assets/images/tbtc.png', decimals: 8);
224
225
226 static final Map<int, CryptoCurrency> _rawCurrencyMap =
cw_core/lib/currency_for_wallet_type.dart
+4 -1
@@ -1,9 +1,12 @@
1 import 'package:cw_core/crypto_currency.dart';
2 import 'package:cw_core/wallet_type.dart';
3
4 -CryptoCurrency currencyForWalletType(WalletType type) {
4 +CryptoCurrency currencyForWalletType(WalletType type, {bool? isTestnet}) {
5 switch (type) {
6 case WalletType.bitcoin:
7 + if (isTestnet == true) {
8 + return CryptoCurrency.tbtc;
9 + }
10 return CryptoCurrency.btc;
11 case WalletType.monero:
12 return CryptoCurrency.xmr;
cw_core/lib/exceptions.dart
+6 -1
@@ -1,9 +1,10 @@
1 import 'package:cw_core/crypto_currency.dart';
2
3 class TransactionWrongBalanceException implements Exception {
4 - TransactionWrongBalanceException(this.currency);
4 + TransactionWrongBalanceException(this.currency, {this.amount});
5
6 final CryptoCurrency currency;
7 + final int? amount;
8 }
9
10 class TransactionNoInputsException implements Exception {}
@@ -32,3 +33,7 @@ class TransactionCommitFailedDustOutput implements Exception {}
33 class TransactionCommitFailedDustOutputSendAll implements Exception {}
34
35 class TransactionCommitFailedVoutNegative implements Exception {}
36 +
37 +class TransactionCommitFailedBIP68Final implements Exception {}
38 +
39 +class TransactionInputNotSupported implements Exception {}
cw_core/lib/get_height_by_date.dart
+54
@@ -242,3 +242,57 @@ Future<int> getHavenCurrentHeight() async {
242 throw Exception('Failed to load current blockchain height');
243 }
244 }
245 +
246 +// Data taken from https://timechaincalendar.com/
247 +const bitcoinDates = {
248 + "2024-05": 841590,
249 + "2024-04": 837182,
250 + "2024-03": 832623,
251 + "2024-02": 828319,
252 + "2024-01": 823807,
253 + "2023-12": 819206,
254 + "2023-11": 814765,
255 + "2023-10": 810098,
256 + "2023-09": 805675,
257 + "2023-08": 801140,
258 + "2023-07": 796640,
259 + "2023-06": 792330,
260 + "2023-05": 787733,
261 + "2023-04": 783403,
262 + "2023-03": 778740,
263 + "2023-02": 774525,
264 + "2023-01": 769810,
265 +};
266 +
267 +int getBitcoinHeightByDate({required DateTime date}) {
268 + String dateKey = '${date.year}-${date.month.toString().padLeft(2, '0')}';
269 + final closestKey = bitcoinDates.keys
270 + .firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => bitcoinDates.keys.last);
271 + final beginningBlock = bitcoinDates[dateKey] ?? bitcoinDates[closestKey]!;
272 +
273 + final startOfMonth = DateTime(date.year, date.month);
274 + final daysDifference = date.difference(startOfMonth).inDays;
275 +
276 + // approximately 6 blocks per hour, 24 hours per day
277 + int estimatedBlocksSinceStartOfMonth = (daysDifference * 24 * 6);
278 +
279 + return beginningBlock + estimatedBlocksSinceStartOfMonth;
280 +}
281 +
282 +DateTime getDateByBitcoinHeight(int height) {
283 + final closestEntry = bitcoinDates.entries
284 + .lastWhere((entry) => entry.value >= height, orElse: () => bitcoinDates.entries.first);
285 + final beginningBlock = closestEntry.value;
286 +
287 + final startOfMonth = formatMapKey(closestEntry.key);
288 + final blocksDifference = height - beginningBlock;
289 + final hoursDifference = blocksDifference / 5.5;
290 +
291 + final estimatedDate = startOfMonth.add(Duration(hours: hoursDifference.ceil()));
292 +
293 + if (estimatedDate.isAfter(DateTime.now())) {
294 + return DateTime.now();
295 + }
296 +
297 + return estimatedDate;
298 +}
cw_core/lib/node.dart
+6 -2
@@ -244,8 +244,12 @@ class Node extends HiveObject with Keyable {
244
245 Future<bool> requestElectrumServer() async {
246 try {
247 - await SecureSocket.connect(uri.host, uri.port,
248 - timeout: Duration(seconds: 5), onBadCertificate: (_) => true);
247 + if (useSSL == true) {
248 + await SecureSocket.connect(uri.host, uri.port,
249 + timeout: Duration(seconds: 5), onBadCertificate: (_) => true);
250 + } else {
251 + await Socket.connect(uri.host, uri.port, timeout: Duration(seconds: 5));
252 + }
253 return true;
254 } catch (_) {
255 return false;
cw_core/lib/sync_status.dart
+32 -7
@@ -14,6 +14,16 @@ class SyncingSyncStatus extends SyncStatus {
14
15 @override
16 String toString() => '$blocksLeft';
17 +
18 + factory SyncingSyncStatus.fromHeightValues(int chainTip, int initialSyncHeight, int syncHeight) {
19 + final track = chainTip - initialSyncHeight;
20 + final diff = track - (chainTip - syncHeight);
21 + final ptc = diff <= 0 ? 0.0 : diff / track;
22 + final left = chainTip - syncHeight;
23 +
24 + // sum 1 because if at the chain tip, will say "0 blocks left"
25 + return SyncingSyncStatus(left + 1, ptc);
26 + }
27 }
28
29 class SyncedSyncStatus extends SyncStatus {
@@ -21,6 +31,17 @@ class SyncedSyncStatus extends SyncStatus {
31 double progress() => 1.0;
32 }
33
34 +class SyncedTipSyncStatus extends SyncedSyncStatus {
35 + SyncedTipSyncStatus(this.tip);
36 +
37 + final int tip;
38 +}
39 +
40 +class SyncronizingSyncStatus extends SyncStatus {
41 + @override
42 + double progress() => 0.0;
43 +}
44 +
45 class NotConnectedSyncStatus extends SyncStatus {
46 const NotConnectedSyncStatus();
47
@@ -33,10 +54,7 @@ class AttemptingSyncStatus extends SyncStatus {
54 double progress() => 0.0;
55 }
56
36 -class FailedSyncStatus extends SyncStatus {
37 - @override
38 - double progress() => 1.0;
39 -}
57 +class FailedSyncStatus extends NotConnectedSyncStatus {}
58
59 class ConnectingSyncStatus extends SyncStatus {
60 @override
@@ -48,7 +66,14 @@ class ConnectedSyncStatus extends SyncStatus {
66 double progress() => 0.0;
67 }
68
51 -class LostConnectionSyncStatus extends SyncStatus {
69 +class UnsupportedSyncStatus extends NotConnectedSyncStatus {}
70 +
71 +class TimedOutSyncStatus extends NotConnectedSyncStatus {
72 @override
53 - double progress() => 1.0;
54 -}
\ No newline at end of file
73 + String toString() => 'Timed out';
74 +}
75 +
76 +class LostConnectionSyncStatus extends NotConnectedSyncStatus {
77 + @override
78 + String toString() => 'Reconnecting';
79 +}
cw_core/lib/unspent_coins_info.dart
+6 -2
@@ -16,7 +16,8 @@ class UnspentCoinsInfo extends HiveObject {
16 required this.value,
17 this.keyImage = null,
18 this.isChange = false,
19 - this.accountIndex = 0
19 + this.accountIndex = 0,
20 + this.isSilentPayment = false,
21 });
22
23 static const typeId = UNSPENT_COINS_INFO_TYPE_ID;
@@ -49,13 +50,16 @@ class UnspentCoinsInfo extends HiveObject {
50
51 @HiveField(8, defaultValue: null)
52 String? keyImage;
52 -
53 +
54 @HiveField(9, defaultValue: false)
55 bool isChange;
56
57 @HiveField(10, defaultValue: 0)
58 int accountIndex;
59
60 + @HiveField(11, defaultValue: false)
61 + bool? isSilentPayment;
62 +
63 String get note => noteRaw ?? '';
64
65 set note(String value) => noteRaw = value;
cw_core/lib/unspent_transaction_output.dart
+2 -1
@@ -17,5 +17,6 @@ class Unspent {
17 int? confirmations;
18 String note;
19
20 - bool get isP2wpkh => address.startsWith('bc') || address.startsWith('ltc');
20 + bool get isP2wpkh =>
21 + address.startsWith('bc') || address.startsWith('tb') || address.startsWith('ltc');
22 }
cw_core/lib/wallet_base.dart
+1 -1
@@ -24,7 +24,7 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
24
25 WalletType get type => walletInfo.type;
26
27 - CryptoCurrency get currency => currencyForWalletType(type);
27 + CryptoCurrency get currency => currencyForWalletType(type, isTestnet: isTestnet);
28
29 String get id => walletInfo.id;
30
cw_core/lib/wallet_info.dart
+20 -15
@@ -66,21 +66,21 @@ class DerivationInfo extends HiveObject {
66 @HiveType(typeId: WalletInfo.typeId)
67 class WalletInfo extends HiveObject {
68 WalletInfo(
69 - this.id,
70 - this.name,
71 - this.type,
72 - this.isRecovery,
73 - this.restoreHeight,
74 - this.timestamp,
75 - this.dirPath,
76 - this.path,
77 - this.address,
78 - this.yatEid,
79 - this.yatLastUsedAddressRaw,
80 - this.showIntroCakePayCard,
81 - this.derivationInfo,
82 - this.hardwareWalletType,
83 - ): _yatLastUsedAddressController = StreamController<String>.broadcast();
69 + this.id,
70 + this.name,
71 + this.type,
72 + this.isRecovery,
73 + this.restoreHeight,
74 + this.timestamp,
75 + this.dirPath,
76 + this.path,
77 + this.address,
78 + this.yatEid,
79 + this.yatLastUsedAddressRaw,
80 + this.showIntroCakePayCard,
81 + this.derivationInfo,
82 + this.hardwareWalletType,
83 + ) : _yatLastUsedAddressController = StreamController<String>.broadcast();
84
85 factory WalletInfo.external({
86 required String id,
@@ -207,4 +207,9 @@ class WalletInfo extends HiveObject {
207 Stream<String> get yatLastUsedAddressStream => _yatLastUsedAddressController.stream;
208
209 StreamController<String> _yatLastUsedAddressController;
210 +
211 + Future<void> updateRestoreHeight(int height) async {
212 + restoreHeight = height;
213 + await save();
214 + }
215 }
cw_core/lib/wallet_type.dart
+4 -1
@@ -173,11 +173,14 @@ String walletTypeToDisplayName(WalletType type) {
173 }
174 }
175
176 -CryptoCurrency walletTypeToCryptoCurrency(WalletType type) {
176 +CryptoCurrency walletTypeToCryptoCurrency(WalletType type, {bool isTestnet = false}) {
177 switch (type) {
178 case WalletType.monero:
179 return CryptoCurrency.xmr;
180 case WalletType.bitcoin:
181 + if (isTestnet) {
182 + return CryptoCurrency.tbtc;
183 + }
184 return CryptoCurrency.btc;
185 case WalletType.litecoin:
186 return CryptoCurrency.ltc;
cw_shared_external/pubspec.lock deleted
-147
@@ -1,147 +0,0 @@
1 -# Generated by pub
2 -# See https://dart.dev/tools/pub/glossary#lockfile
3 -packages:
4 - async:
5 - dependency: transitive
6 - description:
7 - name: async
8 - url: "https://pub.dartlang.org"
9 - source: hosted
10 - version: "2.5.0"
11 - boolean_selector:
12 - dependency: transitive
13 - description:
14 - name: boolean_selector
15 - url: "https://pub.dartlang.org"
16 - source: hosted
17 - version: "2.1.0"
18 - characters:
19 - dependency: transitive
20 - description:
21 - name: characters
22 - url: "https://pub.dartlang.org"
23 - source: hosted
24 - version: "1.1.0"
25 - charcode:
26 - dependency: transitive
27 - description:
28 - name: charcode
29 - url: "https://pub.dartlang.org"
30 - source: hosted
31 - version: "1.2.0"
32 - clock:
33 - dependency: transitive
34 - description:
35 - name: clock
36 - url: "https://pub.dartlang.org"
37 - source: hosted
38 - version: "1.1.0"
39 - collection:
40 - dependency: transitive
41 - description:
42 - name: collection
43 - url: "https://pub.dartlang.org"
44 - source: hosted
45 - version: "1.15.0"
46 - fake_async:
47 - dependency: transitive
48 - description:
49 - name: fake_async
50 - url: "https://pub.dartlang.org"
51 - source: hosted
52 - version: "1.2.0"
53 - flutter:
54 - dependency: "direct main"
55 - description: flutter
56 - source: sdk
57 - version: "0.0.0"
58 - flutter_test:
59 - dependency: "direct dev"
60 - description: flutter
61 - source: sdk
62 - version: "0.0.0"
63 - matcher:
64 - dependency: transitive
65 - description:
66 - name: matcher
67 - url: "https://pub.dartlang.org"
68 - source: hosted
69 - version: "0.12.10"
70 - meta:
71 - dependency: transitive
72 - description:
73 - name: meta
74 - url: "https://pub.dartlang.org"
75 - source: hosted
76 - version: "1.3.0"
77 - path:
78 - dependency: transitive
79 - description:
80 - name: path
81 - url: "https://pub.dartlang.org"
82 - source: hosted
83 - version: "1.8.0"
84 - sky_engine:
85 - dependency: transitive
86 - description: flutter
87 - source: sdk
88 - version: "0.0.99"
89 - source_span:
90 - dependency: transitive
91 - description:
92 - name: source_span
93 - url: "https://pub.dartlang.org"
94 - source: hosted
95 - version: "1.8.0"
96 - stack_trace:
97 - dependency: transitive
98 - description:
99 - name: stack_trace
100 - url: "https://pub.dartlang.org"
101 - source: hosted
102 - version: "1.10.0"
103 - stream_channel:
104 - dependency: transitive
105 - description:
106 - name: stream_channel
107 - url: "https://pub.dartlang.org"
108 - source: hosted
109 - version: "2.1.0"
110 - string_scanner:
111 - dependency: transitive
112 - description:
113 - name: string_scanner
114 - url: "https://pub.dartlang.org"
115 - source: hosted
116 - version: "1.1.0"
117 - term_glyph:
118 - dependency: transitive
119 - description:
120 - name: term_glyph
121 - url: "https://pub.dartlang.org"
122 - source: hosted
123 - version: "1.2.0"
124 - test_api:
125 - dependency: transitive
126 - description:
127 - name: test_api
128 - url: "https://pub.dartlang.org"
129 - source: hosted
130 - version: "0.2.19"
131 - typed_data:
132 - dependency: transitive
133 - description:
134 - name: typed_data
135 - url: "https://pub.dartlang.org"
136 - source: hosted
137 - version: "1.3.0"
138 - vector_math:
139 - dependency: transitive
140 - description:
141 - name: vector_math
142 - url: "https://pub.dartlang.org"
143 - source: hosted
144 - version: "2.1.0"
145 -sdks:
146 - dart: ">=2.12.0-0.0 <3.0.0"
147 - flutter: ">=1.20.0"
cw_tron/pubspec.yaml
+8 -2
@@ -15,8 +15,14 @@ dependencies:
15 path: ../cw_core
16 cw_evm:
17 path: ../cw_evm
18 - on_chain: ^3.0.1
19 - blockchain_utils: ^2.1.1
18 + on_chain:
19 + git:
20 + url: https://github.com/cake-tech/On_chain
21 + ref: cake-update-v1
22 + blockchain_utils:
23 + git:
24 + url: https://github.com/cake-tech/blockchain_utils
25 + ref: cake-update-v1
26 mobx: ^2.3.0+1
27 bip39: ^1.0.6
28 hive: ^2.2.3
howto-build-android.md
+1 -19
@@ -142,27 +142,9 @@ Then we need to generate localization files.
142
143 `$ flutter packages pub run tool/generate_localization.dart`
144
145 -Lastly, we will generate mobx models for the project.
146 -
147 -Generate mobx models for `cw_core`:
148 -
149 -`cd cw_core && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..`
150 -
151 -Generate mobx models for `cw_monero`:
152 -
153 -`cd cw_monero && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..`
154 -
155 -Generate mobx models for `cw_bitcoin`:
156 -
157 -`cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..`
158 -
159 -Generate mobx models for `cw_haven`:
160 -
161 -`cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..`
162 -
145 Finally build mobx models for the app:
146
165 -`$ flutter packages pub run build_runner build --delete-conflicting-outputs`
147 +`$ ./model_generator.sh`
148
149 ### 9. Build!
150
ios/Podfile.lock
+6
@@ -147,6 +147,8 @@ PODS:
147 - shared_preferences_foundation (0.0.1):
148 - Flutter
149 - FlutterMacOS
150 + - sp_scanner (0.0.1):
151 + - Flutter
152 - SwiftProtobuf (1.25.2)
153 - SwiftyGif (5.4.4)
154 - Toast (4.1.0)
@@ -188,6 +190,7 @@ DEPENDENCIES:
190 - sensitive_clipboard (from `.symlinks/plugins/sensitive_clipboard/ios`)
191 - share_plus (from `.symlinks/plugins/share_plus/ios`)
192 - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
193 + - sp_scanner (from `.symlinks/plugins/sp_scanner/ios`)
194 - uni_links (from `.symlinks/plugins/uni_links/ios`)
195 - UnstoppableDomainsResolution (~> 4.0.0)
196 - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
@@ -259,6 +262,8 @@ EXTERNAL SOURCES:
262 :path: ".symlinks/plugins/share_plus/ios"
263 shared_preferences_foundation:
264 :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
265 + sp_scanner:
266 + :path: ".symlinks/plugins/sp_scanner/ios"
267 uni_links:
268 :path: ".symlinks/plugins/uni_links/ios"
269 url_launcher_ios:
@@ -302,6 +307,7 @@ SPEC CHECKSUMS:
307 sensitive_clipboard: d4866e5d176581536c27bb1618642ee83adca986
308 share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
309 shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
310 + sp_scanner: eaa617fa827396b967116b7f1f43549ca62e9a12
311 SwiftProtobuf: 407a385e97fd206c4fbe880cc84123989167e0d1
312 SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f
313 Toast: ec33c32b8688982cecc6348adeae667c1b9938da
lib/bitcoin/cw_bitcoin.dart
+143 -12
@@ -121,20 +121,12 @@ class CWBitcoin extends Bitcoin {
121 priority: priority != null ? priority as BitcoinTransactionPriority : null,
122 feeRate: feeRate);
123
124 - @override
125 - List<String> getAddresses(Object wallet) {
126 - final bitcoinWallet = wallet as ElectrumWallet;
127 - return bitcoinWallet.walletAddresses.addressesByReceiveType
128 - .map((BitcoinAddressRecord addr) => addr.address)
129 - .toList();
130 - }
131 -
124 @override
125 @computed
126 List<ElectrumSubAddress> getSubAddresses(Object wallet) {
127 final electrumWallet = wallet as ElectrumWallet;
128 return electrumWallet.walletAddresses.addressesByReceiveType
137 - .map((BitcoinAddressRecord addr) => ElectrumSubAddress(
129 + .map((BaseBitcoinAddressRecord addr) => ElectrumSubAddress(
130 id: addr.index,
131 name: addr.name,
132 address: addr.address,
@@ -207,12 +199,12 @@ class CWBitcoin extends Bitcoin {
199
200 Future<void> updateUnspents(Object wallet) async {
201 final bitcoinWallet = wallet as ElectrumWallet;
210 - await bitcoinWallet.updateUnspent();
202 + await bitcoinWallet.updateAllUnspents();
203 }
204
205 WalletService createBitcoinWalletService(
214 - Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) {
215 - return BitcoinWalletService(walletInfoSource, unspentCoinSource);
206 + Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan) {
207 + return BitcoinWalletService(walletInfoSource, unspentCoinSource, alwaysScan);
208 }
209
210 WalletService createLitecoinWalletService(
@@ -247,6 +239,12 @@ class CWBitcoin extends Bitcoin {
239 return BitcoinReceivePageOption.fromType(bitcoinWallet.walletAddresses.addressPageType);
240 }
241
242 + @override
243 + bool hasSelectedSilentPayments(Object wallet) {
244 + final bitcoinWallet = wallet as ElectrumWallet;
245 + return bitcoinWallet.walletAddresses.addressPageType == SilentPaymentsAddresType.p2sp;
246 + }
247 +
248 @override
249 List<ReceivePageOption> getBitcoinReceivePageOptions() => BitcoinReceivePageOption.all;
250
@@ -465,4 +463,137 @@ class CWBitcoin extends Bitcoin {
463 throw err;
464 }
465 }
466 +
467 + @override
468 + List<ElectrumSubAddress> getSilentPaymentAddresses(Object wallet) {
469 + final bitcoinWallet = wallet as ElectrumWallet;
470 + return bitcoinWallet.walletAddresses.silentAddresses
471 + .where((addr) => addr.type != SegwitAddresType.p2tr)
472 + .map((addr) => ElectrumSubAddress(
473 + id: addr.index,
474 + name: addr.name,
475 + address: addr.address,
476 + txCount: addr.txCount,
477 + balance: addr.balance,
478 + isChange: addr.isHidden))
479 + .toList();
480 + }
481 +
482 + @override
483 + List<ElectrumSubAddress> getSilentPaymentReceivedAddresses(Object wallet) {
484 + final bitcoinWallet = wallet as ElectrumWallet;
485 + return bitcoinWallet.walletAddresses.silentAddresses
486 + .where((addr) => addr.type == SegwitAddresType.p2tr)
487 + .map((addr) => ElectrumSubAddress(
488 + id: addr.index,
489 + name: addr.name,
490 + address: addr.address,
491 + txCount: addr.txCount,
492 + balance: addr.balance,
493 + isChange: addr.isHidden))
494 + .toList();
495 + }
496 +
497 + @override
498 + bool isBitcoinReceivePageOption(ReceivePageOption option) {
499 + return option is BitcoinReceivePageOption;
500 + }
501 +
502 + @override
503 + BitcoinAddressType getOptionToType(ReceivePageOption option) {
504 + return (option as BitcoinReceivePageOption).toType();
505 + }
506 +
507 + @override
508 + @computed
509 + bool getScanningActive(Object wallet) {
510 + final bitcoinWallet = wallet as ElectrumWallet;
511 + return bitcoinWallet.silentPaymentsScanningActive;
512 + }
513 +
514 + @override
515 + Future<void> setScanningActive(Object wallet, bool active) async {
516 + final bitcoinWallet = wallet as ElectrumWallet;
517 +
518 + if (active && !(await getNodeIsElectrsSPEnabled(wallet))) {
519 + final node = Node(
520 + useSSL: false,
521 + uri: 'electrs.cakewallet.com:${(wallet.network == BitcoinNetwork.testnet ? 50002 : 50001)}',
522 + );
523 + node.type = WalletType.bitcoin;
524 +
525 + await bitcoinWallet.connectToNode(node: node);
526 + }
527 +
528 + bitcoinWallet.setSilentPaymentsScanning(active);
529 + }
530 +
531 + @override
532 + bool isTestnet(Object wallet) {
533 + final bitcoinWallet = wallet as ElectrumWallet;
534 + return bitcoinWallet.isTestnet ?? false;
535 + }
536 +
537 + @override
538 + int getHeightByDate({required DateTime date}) => getBitcoinHeightByDate(date: date);
539 +
540 + @override
541 + Future<void> rescan(Object wallet, {required int height, bool? doSingleScan}) async {
542 + final bitcoinWallet = wallet as ElectrumWallet;
543 + if (!(await getNodeIsElectrsSPEnabled(wallet))) {
544 + final node = Node(
545 + useSSL: false,
546 + uri: 'electrs.cakewallet.com:${(wallet.network == BitcoinNetwork.testnet ? 50002 : 50001)}',
547 + );
548 + node.type = WalletType.bitcoin;
549 + await bitcoinWallet.connectToNode(node: node);
550 + }
551 + bitcoinWallet.rescan(height: height, doSingleScan: doSingleScan);
552 + }
553 +
554 + Future<bool> getNodeIsElectrs(Object wallet) async {
555 + final bitcoinWallet = wallet as ElectrumWallet;
556 +
557 + final version = await bitcoinWallet.electrumClient.version();
558 +
559 + if (version.isEmpty) {
560 + return false;
561 + }
562 +
563 + final server = version[0];
564 +
565 + if (server.toLowerCase().contains('electrs')) {
566 + return true;
567 + }
568 +
569 + return false;
570 + }
571 +
572 + @override
573 + Future<bool> getNodeIsElectrsSPEnabled(Object wallet) async {
574 + if (!(await getNodeIsElectrs(wallet))) {
575 + return false;
576 + }
577 +
578 + final bitcoinWallet = wallet as ElectrumWallet;
579 + final tweaksResponse = await bitcoinWallet.electrumClient.getTweaks(height: 0);
580 +
581 + if (tweaksResponse != null) {
582 + return true;
583 + }
584 +
585 + return false;
586 + }
587 +
588 + @override
589 + void deleteSilentPaymentAddress(Object wallet, String address) {
590 + final bitcoinWallet = wallet as ElectrumWallet;
591 + bitcoinWallet.walletAddresses.deleteSilentPaymentAddress(address);
592 + }
593 +
594 + @override
595 + Future<void> updateFeeRates(Object wallet) async {
596 + final bitcoinWallet = wallet as ElectrumWallet;
597 + await bitcoinWallet.updateFeeRates();
598 + }
599 }
lib/core/address_validator.dart
+4 -2
@@ -26,7 +26,7 @@ class AddressValidator extends TextValidator {
26 return '^[0-9a-zA-Z]{59}\$|^[0-9a-zA-Z]{92}\$|^[0-9a-zA-Z]{104}\$'
27 '|^[0-9a-zA-Z]{105}\$|^addr1[0-9a-zA-Z]{98}\$';
28 case CryptoCurrency.btc:
29 - return '^${P2pkhAddress.regex.pattern}\$|^${P2shAddress.regex.pattern}\$|^${P2wpkhAddress.regex.pattern}\$|${P2trAddress.regex.pattern}\$|^${P2wshAddress.regex.pattern}\$';
29 + return '^${P2pkhAddress.regex.pattern}\$|^${P2shAddress.regex.pattern}\$|^${P2wpkhAddress.regex.pattern}\$|${P2trAddress.regex.pattern}\$|^${P2wshAddress.regex.pattern}\$|^${SilentPaymentAddress.regex.pattern}\$';
30 case CryptoCurrency.nano:
31 return '[0-9a-zA-Z_]';
32 case CryptoCurrency.banano:
@@ -274,7 +274,9 @@ class AddressValidator extends TextValidator {
274 '|([^0-9a-zA-Z]|^)([23][a-km-zA-HJ-NP-Z1-9]{25,34})([^0-9a-zA-Z]|\$)' //P2shAddress type
275 '|([^0-9a-zA-Z]|^)((bc|tb)1q[ac-hj-np-z02-9]{25,39})([^0-9a-zA-Z]|\$)' //P2wpkhAddress type
276 '|([^0-9a-zA-Z]|^)((bc|tb)1q[ac-hj-np-z02-9]{40,80})([^0-9a-zA-Z]|\$)' //P2wshAddress type
277 - '|([^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
277 + '|([^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
278 + '|${SilentPaymentAddress.regex.pattern}\$';
279 +
280 case CryptoCurrency.ltc:
281 return '([^0-9a-zA-Z]|^)^L[a-zA-Z0-9]{26,33}([^0-9a-zA-Z]|\$)'
282 '|([^0-9a-zA-Z]|^)[LM][a-km-zA-HJ-NP-Z1-9]{26,33}([^0-9a-zA-Z]|\$)'
lib/core/sync_status_title.dart
+20 -2
@@ -3,7 +3,13 @@ import 'package:cw_core/sync_status.dart';
3
4 String syncStatusTitle(SyncStatus syncStatus) {
5 if (syncStatus is SyncingSyncStatus) {
6 - return S.current.Blocks_remaining('${syncStatus.blocksLeft}');
6 + return syncStatus.blocksLeft == 1
7 + ? S.current.block_remaining
8 + : S.current.Blocks_remaining('${syncStatus.blocksLeft}');
9 + }
10 +
11 + if (syncStatus is SyncedTipSyncStatus) {
12 + return S.current.silent_payments_scanned_tip(syncStatus.tip.toString());
13 }
14
15 if (syncStatus is SyncedSyncStatus) {
@@ -34,5 +40,17 @@ String syncStatusTitle(SyncStatus syncStatus) {
40 return S.current.sync_status_failed_connect;
41 }
42
43 + if (syncStatus is UnsupportedSyncStatus) {
44 + return S.current.sync_status_unsupported;
45 + }
46 +
47 + if (syncStatus is TimedOutSyncStatus) {
48 + return S.current.sync_status_timed_out;
49 + }
50 +
51 + if (syncStatus is SyncronizingSyncStatus) {
52 + return S.current.sync_status_syncronizing;
53 + }
54 +
55 return '';
38 -}
\ No newline at end of file
56 +}
lib/di.dart
+18 -7
@@ -6,6 +6,7 @@ import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
6 import 'package:cake_wallet/anypay/anypay_api.dart';
7 import 'package:cake_wallet/bitcoin/bitcoin.dart';
8 import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
9 +import 'package:cake_wallet/buy/dfx/dfx_buy_provider.dart';
10 import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
11 import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
12 import 'package:cake_wallet/buy/order.dart';
@@ -15,6 +16,7 @@ import 'package:cake_wallet/core/auth_service.dart';
16 import 'package:cake_wallet/core/backup_service.dart';
17 import 'package:cake_wallet/core/key_service.dart';
18 import 'package:cake_wallet/core/secure_storage.dart';
19 +import 'package:cake_wallet/core/totp_request_details.dart';
20 import 'package:cake_wallet/core/wallet_connect/wallet_connect_key_service.dart';
21 import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart';
22 import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart';
@@ -102,12 +104,14 @@ import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart';
104 import 'package:cake_wallet/src/screens/send/send_page.dart';
105 import 'package:cake_wallet/src/screens/send/send_template_page.dart';
106 import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart';
107 +import 'package:cake_wallet/src/screens/settings/desktop_settings/desktop_settings_page.dart';
108 import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
109 import 'package:cake_wallet/src/screens/settings/domain_lookups_page.dart';
110 import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart';
111 import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
112 import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
113 import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
114 +import 'package:cake_wallet/src/screens/settings/silent_payments_settings.dart';
115 import 'package:cake_wallet/src/screens/settings/tor_page.dart';
116 import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
117 import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
@@ -159,10 +163,10 @@ import 'package:cake_wallet/view_model/buy/buy_view_model.dart';
163 import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
164 import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart';
165 import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
166 +import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart';
167 import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
168 import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart';
169 import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart';
165 -import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
170 import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
171 import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
172 import 'package:cake_wallet/view_model/edit_backup_password_view_model.dart';
@@ -199,6 +203,7 @@ import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart
203 import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
204 import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart';
205 import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart';
206 +import 'package:cake_wallet/view_model/settings/silent_payments_settings_view_model.dart';
207 import 'package:cake_wallet/view_model/settings/trocador_providers_view_model.dart';
208 import 'package:cake_wallet/view_model/setup_pin_code_view_model.dart';
209 import 'package:cake_wallet/view_model/support_view_model.dart';
@@ -235,10 +240,6 @@ import 'package:hive/hive.dart';
240 import 'package:mobx/mobx.dart';
241 import 'package:shared_preferences/shared_preferences.dart';
242
238 -import 'buy/dfx/dfx_buy_provider.dart';
239 -import 'core/totp_request_details.dart';
240 -import 'src/screens/settings/desktop_settings/desktop_settings_page.dart';
241 -
243 final getIt = GetIt.instance;
244
245 var _isSetupFinished = false;
@@ -745,6 +746,9 @@ Future<void> setup({
746 return DisplaySettingsViewModel(getIt.get<SettingsStore>());
747 });
748
749 + getIt.registerFactory(() =>
750 + SilentPaymentsSettingsViewModel(getIt.get<SettingsStore>(), getIt.get<AppStore>().wallet!));
751 +
752 getIt.registerFactory(() {
753 return PrivacySettingsViewModel(getIt.get<SettingsStore>(), getIt.get<AppStore>().wallet!);
754 });
@@ -806,6 +810,9 @@ Future<void> setup({
810
811 getIt.registerFactory(() => DisplaySettingsPage(getIt.get<DisplaySettingsViewModel>()));
812
813 + getIt.registerFactory(
814 + () => SilentPaymentsSettingsPage(getIt.get<SilentPaymentsSettingsViewModel>()));
815 +
816 getIt.registerFactory(() => OtherSettingsPage(getIt.get<OtherSettingsViewModel>()));
817
818 getIt.registerFactory(() => NanoChangeRepPage(
@@ -893,7 +900,11 @@ Future<void> setup({
900 case WalletType.monero:
901 return monero!.createMoneroWalletService(_walletInfoSource, _unspentCoinsInfoSource);
902 case WalletType.bitcoin:
896 - return bitcoin!.createBitcoinWalletService(_walletInfoSource, _unspentCoinsInfoSource);
903 + return bitcoin!.createBitcoinWalletService(
904 + _walletInfoSource,
905 + _unspentCoinsInfoSource,
906 + getIt.get<SettingsStore>().silentPaymentsAlwaysScan,
907 + );
908 case WalletType.litecoin:
909 return bitcoin!.createLitecoinWalletService(_walletInfoSource, _unspentCoinsInfoSource);
910 case WalletType.ethereum:
@@ -1089,7 +1100,7 @@ Future<void> setup({
1100
1101 getIt.registerFactory(() => IoniaGiftCardsListViewModel(ioniaService: getIt.get<IoniaService>()));
1102
1092 - getIt.registerFactory(() => MarketPlaceViewModel(getIt.get<IoniaService>()));
1103 + getIt.registerFactory(() => CakeFeaturesViewModel(getIt.get<IoniaService>()));
1104
1105 getIt.registerFactory(() => IoniaAuthViewModel(ioniaService: getIt.get<IoniaService>()));
1106
lib/entities/default_settings_migration.dart
+42 -7
@@ -24,8 +24,9 @@ import 'package:collection/collection.dart';
24
25 const newCakeWalletMoneroUri = 'xmr-node.cakewallet.com:18081';
26 const cakeWalletBitcoinElectrumUri = 'electrum.cakewallet.com:50002';
27 -const publicBitcoinTestnetElectrumAddress = 'electrum.blockstream.info';
28 -const publicBitcoinTestnetElectrumPort = '60002';
27 +const cakeWalletSilentPaymentsElectrsUri = 'electrs.cakewallet.com:50001';
28 +const publicBitcoinTestnetElectrumAddress = 'electrs.cakewallet.com';
29 +const publicBitcoinTestnetElectrumPort = '50002';
30 const publicBitcoinTestnetElectrumUri =
31 '$publicBitcoinTestnetElectrumAddress:$publicBitcoinTestnetElectrumPort';
32 const cakeWalletLitecoinElectrumUri = 'ltc-electrum.cakewallet.com:50002';
@@ -224,6 +225,9 @@ Future<void> defaultSettingsMigration(
225 await addTronNodeList(nodes: nodes);
226 await changeTronCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes);
227 break;
228 + case 34:
229 + await _addElectRsNode(nodes, sharedPreferences);
230 + break;
231 default:
232 break;
233 }
@@ -790,7 +794,8 @@ Future<void> changeDefaultBitcoinNode(
794 final needToReplaceCurrentBitcoinNode =
795 currentBitcoinNode.uri.toString().contains(cakeWalletBitcoinNodeUriPattern);
796
793 - final newCakeWalletBitcoinNode = Node(uri: newCakeWalletBitcoinUri, type: WalletType.bitcoin);
797 + final newCakeWalletBitcoinNode =
798 + Node(uri: newCakeWalletBitcoinUri, type: WalletType.bitcoin, useSSL: false);
799
800 await nodeSource.add(newCakeWalletBitcoinNode);
801
@@ -800,6 +805,26 @@ Future<void> changeDefaultBitcoinNode(
805 }
806 }
807
808 +Future<void> _addElectRsNode(Box<Node> nodeSource, SharedPreferences sharedPreferences) async {
809 + const cakeWalletBitcoinNodeUriPattern = '.cakewallet.com';
810 + final currentBitcoinNodeId =
811 + sharedPreferences.getInt(PreferencesKey.currentBitcoinElectrumSererIdKey);
812 + final currentBitcoinNode =
813 + nodeSource.values.firstWhere((node) => node.key == currentBitcoinNodeId);
814 + final needToReplaceCurrentBitcoinNode =
815 + currentBitcoinNode.uri.toString().contains(cakeWalletBitcoinNodeUriPattern);
816 +
817 + final newElectRsBitcoinNode =
818 + Node(uri: cakeWalletSilentPaymentsElectrsUri, type: WalletType.bitcoin, useSSL: false);
819 +
820 + await nodeSource.add(newElectRsBitcoinNode);
821 +
822 + if (needToReplaceCurrentBitcoinNode) {
823 + await sharedPreferences.setInt(
824 + PreferencesKey.currentBitcoinElectrumSererIdKey, newElectRsBitcoinNode.key as int);
825 + }
826 +}
827 +
828 Future<void> checkCurrentNodes(
829 Box<Node> nodeSource, Box<Node> powNodeSource, SharedPreferences sharedPreferences) async {
830 final currentMoneroNodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
@@ -845,14 +870,19 @@ Future<void> checkCurrentNodes(
870 }
871
872 if (currentBitcoinElectrumServer == null) {
848 - final cakeWalletElectrum = Node(uri: cakeWalletBitcoinElectrumUri, type: WalletType.bitcoin);
873 + final cakeWalletElectrum =
874 + Node(uri: cakeWalletBitcoinElectrumUri, type: WalletType.bitcoin, useSSL: false);
875 await nodeSource.add(cakeWalletElectrum);
876 + final cakeWalletElectrumTestnet =
877 + Node(uri: publicBitcoinTestnetElectrumUri, type: WalletType.bitcoin, useSSL: false);
878 + await nodeSource.add(cakeWalletElectrumTestnet);
879 await sharedPreferences.setInt(
880 PreferencesKey.currentBitcoinElectrumSererIdKey, cakeWalletElectrum.key as int);
881 }
882
883 if (currentLitecoinElectrumServer == null) {
855 - final cakeWalletElectrum = Node(uri: cakeWalletLitecoinElectrumUri, type: WalletType.litecoin);
884 + final cakeWalletElectrum =
885 + Node(uri: cakeWalletLitecoinElectrumUri, type: WalletType.litecoin, useSSL: false);
886 await nodeSource.add(cakeWalletElectrum);
887 await sharedPreferences.setInt(
888 PreferencesKey.currentLitecoinElectrumSererIdKey, cakeWalletElectrum.key as int);
@@ -887,7 +917,8 @@ Future<void> checkCurrentNodes(
917 }
918
919 if (currentBitcoinCashNodeServer == null) {
890 - final node = Node(uri: cakeWalletBitcoinCashDefaultNodeUri, type: WalletType.bitcoinCash);
920 + final node =
921 + Node(uri: cakeWalletBitcoinCashDefaultNodeUri, type: WalletType.bitcoinCash, useSSL: false);
922 await nodeSource.add(node);
923 await sharedPreferences.setInt(PreferencesKey.currentBitcoinCashNodeIdKey, node.key as int);
924 }
@@ -921,7 +952,11 @@ Future<void> resetBitcoinElectrumServer(
952 .firstWhereOrNull((node) => node.uriRaw.toString() == cakeWalletBitcoinElectrumUri);
953
954 if (cakeWalletNode == null) {
924 - cakeWalletNode = Node(uri: cakeWalletBitcoinElectrumUri, type: WalletType.bitcoin);
955 + cakeWalletNode =
956 + Node(uri: cakeWalletBitcoinElectrumUri, type: WalletType.bitcoin, useSSL: false);
957 + // final cakeWalletElectrumTestnet =
958 + // Node(uri: publicBitcoinTestnetElectrumUri, type: WalletType.bitcoin, useSSL: false);
959 + // await nodeSource.add(cakeWalletElectrumTestnet);
960 await nodeSource.add(cakeWalletNode);
961 }
962
lib/entities/preferences_key.dart
+2
@@ -44,6 +44,8 @@ class PreferencesKey {
44 static const polygonTransactionPriority = 'current_fee_priority_polygon';
45 static const bitcoinCashTransactionPriority = 'current_fee_priority_bitcoin_cash';
46 static const customBitcoinFeeRate = 'custom_electrum_fee_rate';
47 + static const silentPaymentsCardDisplay = 'silentPaymentsCardDisplay';
48 + static const silentPaymentsAlwaysScan = 'silentPaymentsAlwaysScan';
49 static const shouldShowReceiveWarning = 'should_show_receive_warning';
50 static const shouldShowYatPopup = 'should_show_yat_popup';
51 static const shouldShowRepWarning = 'should_show_rep_warning';
lib/main.dart
+1 -1
@@ -202,7 +202,7 @@ Future<void> initializeAppConfigs() async {
202 transactionDescriptions: transactionDescriptions,
203 secureStorage: secureStorage,
204 anonpayInvoiceInfo: anonpayInvoiceInfo,
205 - initialMigrationVersion: 33,
205 + initialMigrationVersion: 34,
206 );
207 }
208
lib/reactions/check_connection.dart
+11 -10
@@ -3,15 +3,19 @@ import 'dart:async';
3 import 'package:connectivity_plus/connectivity_plus.dart';
4 import 'package:cw_core/wallet_base.dart';
5 import 'package:cw_core/sync_status.dart';
6 +import 'package:cw_core/wallet_type.dart';
7 import 'package:cake_wallet/store/settings_store.dart';
8 +
9 Timer? _checkConnectionTimer;
10
9 -void startCheckConnectionReaction(
10 - WalletBase wallet, SettingsStore settingsStore,
11 +void startCheckConnectionReaction(WalletBase wallet, SettingsStore settingsStore,
12 {int timeInterval = 5}) {
13 _checkConnectionTimer?.cancel();
13 - _checkConnectionTimer =
14 - Timer.periodic(Duration(seconds: timeInterval), (_) async {
14 + _checkConnectionTimer = Timer.periodic(Duration(seconds: timeInterval), (_) async {
15 + if (wallet.type == WalletType.bitcoin && wallet.syncStatus is SyncingSyncStatus) {
16 + return;
17 + }
18 +
19 try {
20 final connectivityResult = await (Connectivity().checkConnectivity());
21
@@ -20,14 +24,11 @@ void startCheckConnectionReaction(
24 return;
25 }
26
23 - if (wallet.syncStatus is LostConnectionSyncStatus ||
24 - wallet.syncStatus is FailedSyncStatus) {
25 - final alive =
26 - await settingsStore.getCurrentNode(wallet.type).requestNode();
27 + if (wallet.syncStatus is LostConnectionSyncStatus || wallet.syncStatus is FailedSyncStatus) {
28 + final alive = await settingsStore.getCurrentNode(wallet.type).requestNode();
29
30 if (alive) {
29 - await wallet.connectToNode(
30 - node: settingsStore.getCurrentNode(wallet.type));
31 + await wallet.connectToNode(node: settingsStore.getCurrentNode(wallet.type));
32 }
33 }
34 } catch (e) {
lib/reactions/on_wallet_sync_status_change.dart
+3 -5
@@ -12,12 +12,10 @@ import 'package:wakelock_plus/wakelock_plus.dart';
12 ReactionDisposer? _onWalletSyncStatusChangeReaction;
13
14 void startWalletSyncStatusChangeReaction(
15 - WalletBase<Balance, TransactionHistoryBase<TransactionInfo>,
16 - TransactionInfo> wallet,
15 + WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet,
16 FiatConversionStore fiatConversionStore) {
17 _onWalletSyncStatusChangeReaction?.reaction.dispose();
19 - _onWalletSyncStatusChangeReaction =
20 - reaction((_) => wallet.syncStatus, (SyncStatus status) async {
18 + _onWalletSyncStatusChangeReaction = reaction((_) => wallet.syncStatus, (SyncStatus status) async {
19 try {
20 if (status is ConnectedSyncStatus) {
21 await wallet.startSync();
@@ -32,7 +30,7 @@ void startWalletSyncStatusChangeReaction(
30 if (status is SyncedSyncStatus || status is FailedSyncStatus) {
31 await WakelockPlus.disable();
32 }
35 - } catch(e) {
33 + } catch (e) {
34 print(e.toString());
35 }
36 });
lib/router.dart
+5
@@ -76,6 +76,7 @@ import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart';
76 import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
77 import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
78 import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
79 +import 'package:cake_wallet/src/screens/settings/silent_payments_settings.dart';
80 import 'package:cake_wallet/src/screens/settings/tor_page.dart';
81 import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
82 import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
@@ -366,6 +367,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
367 param1: settings.arguments as OnAuthenticationFinished, param2: false),
368 onWillPop: () async => false));
369
370 + case Routes.silentPaymentsSettings:
371 + return CupertinoPageRoute<void>(
372 + fullscreenDialog: true, builder: (_) => getIt.get<SilentPaymentsSettingsPage>());
373 +
374 case Routes.connectionSync:
375 return CupertinoPageRoute<void>(
376 fullscreenDialog: true, builder: (_) => getIt.get<ConnectionSyncPage>());
lib/routes.dart
+1
@@ -81,6 +81,7 @@ class Routes {
81 static const ioniaMoreOptionsPage = '/ionia_more_options_page';
82 static const ioniaCustomRedeemPage = '/ionia_custom_redeem_page';
83 static const webViewPage = '/web_view_page';
84 + static const silentPaymentsSettings = '/silent_payments_settings';
85 static const connectionSync = '/connection_sync_page';
86 static const securityBackupPage = '/security_and_backup_page';
87 static const privacyPage = '/privacy_page';
lib/src/screens/dashboard/dashboard_page.dart
+5 -5
@@ -4,7 +4,7 @@ import 'package:cake_wallet/entities/preferences_key.dart';
4 import 'package:cake_wallet/di.dart';
5 import 'package:cake_wallet/entities/main_actions.dart';
6 import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart';
7 -import 'package:cake_wallet/src/screens/dashboard/pages/market_place_page.dart';
7 +import 'package:cake_wallet/src/screens/dashboard/pages/cake_features_page.dart';
8 import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/bottom_sheet_listener.dart';
9 import 'package:cake_wallet/src/widgets/gradient_background.dart';
10 import 'package:cake_wallet/src/widgets/services_updates_widget.dart';
@@ -12,7 +12,7 @@ import 'package:cake_wallet/src/widgets/vulnerable_seeds_popup.dart';
12 import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart';
13 import 'package:cake_wallet/utils/device_info.dart';
14 import 'package:cake_wallet/utils/version_comparator.dart';
15 -import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
15 +import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart';
16 import 'package:cake_wallet/generated/i18n.dart';
17 import 'package:cake_wallet/routes.dart';
18 import 'package:cake_wallet/src/screens/yat_emoji_id.dart';
@@ -330,10 +330,10 @@ class _DashboardPageView extends BasePage {
330 if (dashboardViewModel.shouldShowMarketPlaceInDashboard) {
331 pages.add(
332 Semantics(
333 - label: S.of(context).market_place,
334 - child: MarketPlacePage(
333 + label: 'Cake ${S.of(context).features}',
334 + child: CakeFeaturesPage(
335 dashboardViewModel: dashboardViewModel,
336 - marketPlaceViewModel: getIt.get<MarketPlaceViewModel>(),
336 + cakeFeaturesViewModel: getIt.get<CakeFeaturesViewModel>(),
337 ),
338 ),
339 );
lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart
+4 -4
@@ -1,9 +1,9 @@
1 import 'package:cake_wallet/di.dart';
2 import 'package:cake_wallet/entities/main_actions.dart';
3 import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_action_button.dart';
4 -import 'package:cake_wallet/src/screens/dashboard/pages/market_place_page.dart';
4 +import 'package:cake_wallet/src/screens/dashboard/pages/cake_features_page.dart';
5 import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
6 -import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
6 +import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart';
7 import 'package:flutter/material.dart';
8 import 'package:flutter_mobx/flutter_mobx.dart';
9
@@ -74,9 +74,9 @@ class DesktopDashboardActions extends StatelessWidget {
74 ],
75 ),
76 Expanded(
77 - child: MarketPlacePage(
77 + child: CakeFeaturesPage(
78 dashboardViewModel: dashboardViewModel,
79 - marketPlaceViewModel: getIt.get<MarketPlaceViewModel>(),
79 + cakeFeaturesViewModel: getIt.get<CakeFeaturesViewModel>(),
80 ),
81 ),
82 ],
lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart
+42 -35
@@ -30,6 +30,7 @@ class DesktopWalletSelectionDropDown extends StatefulWidget {
30 class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionDropDown> {
31 final moneroIcon = Image.asset('assets/images/monero_logo.png', height: 24, width: 24);
32 final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
33 + final tBitcoinIcon = Image.asset('assets/images/tbtc.png', height: 24, width: 24);
34 final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24);
35 final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
36 final ethereumIcon = Image.asset('assets/images/eth_icon.png', height: 24, width: 24);
@@ -68,8 +69,11 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
69 child: ConstrainedBox(
70 constraints: BoxConstraints(maxWidth: 500),
71 child: DropDownItemWidget(
71 - title: wallet.name,
72 - image: wallet.isEnabled ? _imageFor(type: wallet.type) : nonWalletTypeIcon),
72 + title: wallet.name,
73 + image: wallet.isEnabled
74 + ? _imageFor(type: wallet.type, isTestnet: wallet.isTestnet)
75 + : nonWalletTypeIcon,
76 + ),
77 ),
78 onSelected: () => _onSelectedWallet(wallet),
79 ))
@@ -120,16 +124,16 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
124
125 WidgetsBinding.instance.addPostFrameCallback((_) async {
126 final confirmed = await showPopUp<bool>(
123 - context: context,
124 - builder: (dialogContext) {
125 - return AlertWithTwoActions(
126 - alertTitle: S.of(context).change_wallet_alert_title,
127 - alertContent: S.of(context).change_wallet_alert_content(selectedWallet.name),
128 - leftButtonText: S.of(context).cancel,
129 - rightButtonText: S.of(context).change,
130 - actionLeftButton: () => Navigator.of(dialogContext).pop(false),
131 - actionRightButton: () => Navigator.of(dialogContext).pop(true));
132 - }) ??
127 + context: context,
128 + builder: (dialogContext) {
129 + return AlertWithTwoActions(
130 + alertTitle: S.of(context).change_wallet_alert_title,
131 + alertContent: S.of(context).change_wallet_alert_content(selectedWallet.name),
132 + leftButtonText: S.of(context).cancel,
133 + rightButtonText: S.of(context).change,
134 + actionLeftButton: () => Navigator.of(dialogContext).pop(false),
135 + actionRightButton: () => Navigator.of(dialogContext).pop(true));
136 + }) ??
137 false;
138
139 if (confirmed) {
@@ -138,9 +142,12 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
142 });
143 }
144
141 - Image _imageFor({required WalletType type}) {
145 + Image _imageFor({required WalletType type, bool? isTestnet}) {
146 switch (type) {
147 case WalletType.bitcoin:
148 + if (isTestnet == true) {
149 + return tBitcoinIcon;
150 + }
151 return bitcoinIcon;
152 case WalletType.monero:
153 return moneroIcon;
@@ -160,7 +167,7 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
167 return polygonIcon;
168 case WalletType.solana:
169 return solanaIcon;
163 - case WalletType.tron:
170 + case WalletType.tron:
171 return tronIcon;
172 default:
173 return nonWalletTypeIcon;
@@ -168,24 +175,25 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
175 }
176
177 Future<void> _loadWallet(WalletListItem wallet) async {
171 - widget._authService.authenticateAction(context,
172 - onAuthSuccess: (isAuthenticatedSuccessfully) async {
173 - if (!isAuthenticatedSuccessfully) {
174 - return;
175 - }
176 -
177 - try {
178 - if (context.mounted) {
179 - changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
178 + widget._authService.authenticateAction(
179 + context,
180 + onAuthSuccess: (isAuthenticatedSuccessfully) async {
181 + if (!isAuthenticatedSuccessfully) {
182 + return;
183 }
181 - await widget.walletListViewModel.loadWallet(wallet);
182 - hideProgressText();
183 - setState(() {});
184 - } catch (e) {
185 - if (context.mounted) {
186 - changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
184 +
185 + try {
186 + if (context.mounted) {
187 + changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
188 + }
189 + await widget.walletListViewModel.loadWallet(wallet);
190 + hideProgressText();
191 + setState(() {});
192 + } catch (e) {
193 + if (context.mounted) {
194 + changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
195 + }
196 }
188 - }
197 },
198 conditionToDetermineIfToUse2FA:
199 widget.walletListViewModel.shouldRequireTOTP2FAForAccessingWallet,
@@ -198,17 +206,16 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
206 context,
207 route: Routes.newWallet,
208 arguments: widget.walletListViewModel.currentWalletType,
201 - conditionToDetermineIfToUse2FA: widget
202 - .walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
209 + conditionToDetermineIfToUse2FA:
210 + widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
211 );
212 } else {
213 widget._authService.authenticateAction(
214 context,
215 route: Routes.newWalletType,
208 - conditionToDetermineIfToUse2FA: widget
209 - .walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
216 + conditionToDetermineIfToUse2FA:
217 + widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
218 );
211 -
219 }
220 }
221
lib/src/screens/dashboard/pages/address_page.dart
+5
@@ -187,6 +187,11 @@ class AddressPage extends BasePage {
187 }
188
189 reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) {
190 + if (bitcoin!.isBitcoinReceivePageOption(option)) {
191 + addressListViewModel.setAddressType(bitcoin!.getOptionToType(option));
192 + return;
193 + }
194 +
195 switch (option) {
196 case ReceivePageOption.anonPayInvoice:
197 Navigator.pushNamed(
lib/src/screens/dashboard/pages/balance_page.dart
+183 -63
@@ -1,15 +1,18 @@
1 import 'dart:math';
2
3 import 'package:auto_size_text/auto_size_text.dart';
4 +import 'package:cake_wallet/bitcoin/bitcoin.dart';
5 import 'package:cake_wallet/generated/i18n.dart';
6 import 'package:cake_wallet/reactions/wallet_connect.dart';
7 import 'package:cake_wallet/routes.dart';
8 import 'package:cake_wallet/src/screens/dashboard/pages/nft_listing_page.dart';
9 import 'package:cake_wallet/src/screens/dashboard/widgets/home_screen_account_widget.dart';
10 +import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
11 import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
12 import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart';
13 import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart';
14 import 'package:cake_wallet/src/widgets/introducing_card.dart';
15 +import 'package:cake_wallet/src/widgets/standard_switch.dart';
16 import 'package:cake_wallet/store/settings_store.dart';
17 import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
18 import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
@@ -21,6 +24,7 @@ import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
24 import 'package:cw_core/crypto_currency.dart';
25 import 'package:flutter/material.dart';
26 import 'package:flutter_mobx/flutter_mobx.dart';
27 +import 'package:url_launcher/url_launcher.dart';
28
29 class BalancePage extends StatelessWidget {
30 BalancePage({
@@ -221,30 +225,136 @@ class CryptoBalanceWidget extends StatelessWidget {
225 itemBuilder: (__, index) {
226 final balance =
227 dashboardViewModel.balanceViewModel.formattedBalances.elementAt(index);
224 - return BalanceRowWidget(
225 - availableBalanceLabel:
226 - '${dashboardViewModel.balanceViewModel.availableBalanceLabel}',
227 - availableBalance: balance.availableBalance,
228 - availableFiatBalance: balance.fiatAvailableBalance,
229 - additionalBalanceLabel:
230 - '${dashboardViewModel.balanceViewModel.additionalBalanceLabel}',
231 - additionalBalance: balance.additionalBalance,
232 - additionalFiatBalance: balance.fiatAdditionalBalance,
233 - frozenBalance: balance.frozenBalance,
234 - frozenFiatBalance: balance.fiatFrozenBalance,
235 - currency: balance.asset,
236 - hasAdditionalBalance:
237 - dashboardViewModel.balanceViewModel.hasAdditionalBalance,
238 - );
228 + return Observer(builder: (_) {
229 + return BalanceRowWidget(
230 + availableBalanceLabel:
231 + '${dashboardViewModel.balanceViewModel.availableBalanceLabel}',
232 + availableBalance: balance.availableBalance,
233 + availableFiatBalance: balance.fiatAvailableBalance,
234 + additionalBalanceLabel:
235 + '${dashboardViewModel.balanceViewModel.additionalBalanceLabel}',
236 + additionalBalance: balance.additionalBalance,
237 + additionalFiatBalance: balance.fiatAdditionalBalance,
238 + frozenBalance: balance.frozenBalance,
239 + frozenFiatBalance: balance.fiatFrozenBalance,
240 + currency: balance.asset,
241 + hasAdditionalBalance:
242 + dashboardViewModel.balanceViewModel.hasAdditionalBalance,
243 + isTestnet: dashboardViewModel.isTestnet,
244 + );
245 + });
246 },
247 );
248 },
242 - )
249 + ),
250 + Observer(builder: (context) {
251 + return Column(
252 + children: [
253 + if (dashboardViewModel.showSilentPaymentsCard) ...[
254 + SizedBox(height: 10),
255 + Padding(
256 + padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
257 + child: DashBoardRoundedCardWidget(
258 + customBorder: 30,
259 + title: S.of(context).silent_payments,
260 + subTitle: S.of(context).enable_silent_payments_scanning,
261 + hint: Column(
262 + children: [
263 + Row(
264 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
265 + children: [
266 + GestureDetector(
267 + behavior: HitTestBehavior.opaque,
268 + onTap: () => launchUrl(
269 + Uri.parse(
270 + "https://guides.cakewallet.com/docs/cryptos/bitcoin/#silent-payments"),
271 + mode: LaunchMode.externalApplication,
272 + ),
273 + child: Row(
274 + children: [
275 + Text(
276 + S.of(context).what_is_silent_payments,
277 + style: TextStyle(
278 + fontSize: 12,
279 + fontFamily: 'Lato',
280 + fontWeight: FontWeight.w400,
281 + color: Theme.of(context)
282 + .extension<BalancePageTheme>()!
283 + .labelTextColor,
284 + height: 1,
285 + ),
286 + softWrap: true,
287 + ),
288 + Padding(
289 + padding: const EdgeInsets.symmetric(horizontal: 4),
290 + child: Icon(Icons.help_outline,
291 + size: 16,
292 + color: Theme.of(context)
293 + .extension<BalancePageTheme>()!
294 + .labelTextColor),
295 + )
296 + ],
297 + ),
298 + ),
299 + Observer(
300 + builder: (_) => StandardSwitch(
301 + value: dashboardViewModel.silentPaymentsScanningActive,
302 + onTaped: () => _toggleSilentPaymentsScanning(context),
303 + ),
304 + )
305 + ],
306 + ),
307 + ],
308 + ),
309 + onTap: () => _toggleSilentPaymentsScanning(context),
310 + icon: Icon(
311 + Icons.lock,
312 + color:
313 + Theme.of(context).extension<DashboardPageTheme>()!.pageTitleTextColor,
314 + size: 50,
315 + ),
316 + ),
317 + ),
318 + ]
319 + ],
320 + );
321 + }),
322 ],
323 ),
324 ),
325 );
326 }
327 +
328 + Future<void> _toggleSilentPaymentsScanning(BuildContext context) async {
329 + final isSilentPaymentsScanningActive = dashboardViewModel.silentPaymentsScanningActive;
330 + final newValue = !isSilentPaymentsScanningActive;
331 +
332 + dashboardViewModel.silentPaymentsScanningActive = newValue;
333 +
334 + final needsToSwitch = !isSilentPaymentsScanningActive &&
335 + await bitcoin!.getNodeIsElectrsSPEnabled(dashboardViewModel.wallet) == false;
336 +
337 + if (needsToSwitch) {
338 + return showPopUp<void>(
339 + context: context,
340 + builder: (BuildContext context) => AlertWithTwoActions(
341 + alertTitle: S.of(context).change_current_node_title,
342 + alertContent: S.of(context).confirm_silent_payments_switch_node,
343 + rightButtonText: S.of(context).ok,
344 + leftButtonText: S.of(context).cancel,
345 + actionRightButton: () {
346 + dashboardViewModel.setSilentPaymentsScanning(newValue);
347 + Navigator.of(context).pop();
348 + },
349 + actionLeftButton: () {
350 + dashboardViewModel.silentPaymentsScanningActive = isSilentPaymentsScanningActive;
351 + Navigator.of(context).pop();
352 + },
353 + ));
354 + }
355 +
356 + return dashboardViewModel.setSilentPaymentsScanning(newValue);
357 + }
358 }
359
360 class BalanceRowWidget extends StatelessWidget {
@@ -259,6 +369,7 @@ class BalanceRowWidget extends StatelessWidget {
369 required this.frozenFiatBalance,
370 required this.currency,
371 required this.hasAdditionalBalance,
372 + required this.isTestnet,
373 super.key,
374 });
375
@@ -272,6 +383,7 @@ class BalanceRowWidget extends StatelessWidget {
383 final String frozenFiatBalance;
384 final CryptoCurrency currency;
385 final bool hasAdditionalBalance;
386 + final bool isTestnet;
387
388 // void _showBalanceDescription(BuildContext context) {
389 // showPopUp<void>(
@@ -346,14 +458,24 @@ class BalanceRowWidget extends StatelessWidget {
458 maxLines: 1,
459 textAlign: TextAlign.start),
460 SizedBox(height: 6),
349 - Text('${availableFiatBalance}',
350 - textAlign: TextAlign.center,
351 - style: TextStyle(
352 - fontSize: 16,
353 - fontFamily: 'Lato',
354 - fontWeight: FontWeight.w500,
355 - color: Theme.of(context).extension<BalancePageTheme>()!.textColor,
356 - height: 1)),
461 + if (isTestnet)
462 + Text(S.current.testnet_coins_no_value,
463 + textAlign: TextAlign.center,
464 + style: TextStyle(
465 + fontSize: 14,
466 + fontFamily: 'Lato',
467 + fontWeight: FontWeight.w400,
468 + color: Theme.of(context).extension<BalancePageTheme>()!.textColor,
469 + height: 1)),
470 + if (!isTestnet)
471 + Text('${availableFiatBalance}',
472 + textAlign: TextAlign.center,
473 + style: TextStyle(
474 + fontSize: 16,
475 + fontFamily: 'Lato',
476 + fontWeight: FontWeight.w500,
477 + color: Theme.of(context).extension<BalancePageTheme>()!.textColor,
478 + height: 1)),
479 ],
480 ),
481 ),
@@ -362,27 +484,23 @@ class BalanceRowWidget extends StatelessWidget {
484 child: Center(
485 child: Column(
486 children: [
365 - Container(
366 - clipBehavior: Clip.antiAlias,
367 - decoration: BoxDecoration(shape: BoxShape.circle),
368 - child: CakeImageWidget(
369 - imageUrl: currency.iconPath,
370 - height: 40,
371 - width: 40,
372 - displayOnError: Container(
373 - height: 30.0,
374 - width: 30.0,
375 - child: Center(
376 - child: Text(
377 - currency.title.substring(0, min(currency.title.length, 2)),
378 - style: TextStyle(fontSize: 11),
379 - ),
380 - ),
381 - decoration: BoxDecoration(
382 - shape: BoxShape.circle,
383 - color: Colors.grey.shade400,
487 + CakeImageWidget(
488 + imageUrl: currency.iconPath,
489 + height: 40,
490 + width: 40,
491 + displayOnError: Container(
492 + height: 30.0,
493 + width: 30.0,
494 + child: Center(
495 + child: Text(
496 + currency.title.substring(0, min(currency.title.length, 2)),
497 + style: TextStyle(fontSize: 11),
498 ),
499 ),
500 + decoration: BoxDecoration(
501 + shape: BoxShape.circle,
502 + color: Colors.grey.shade400,
503 + ),
504 ),
505 ),
506 const SizedBox(height: 10),
@@ -449,17 +567,18 @@ class BalanceRowWidget extends StatelessWidget {
567 textAlign: TextAlign.center,
568 ),
569 SizedBox(height: 4),
452 - Text(
453 - frozenFiatBalance,
454 - textAlign: TextAlign.center,
455 - style: TextStyle(
456 - fontSize: 12,
457 - fontFamily: 'Lato',
458 - fontWeight: FontWeight.w400,
459 - color: Theme.of(context).extension<BalancePageTheme>()!.textColor,
460 - height: 1,
570 + if (!isTestnet)
571 + Text(
572 + frozenFiatBalance,
573 + textAlign: TextAlign.center,
574 + style: TextStyle(
575 + fontSize: 12,
576 + fontFamily: 'Lato',
577 + fontWeight: FontWeight.w400,
578 + color: Theme.of(context).extension<BalancePageTheme>()!.textColor,
579 + height: 1,
580 + ),
581 ),
462 - ),
582 ],
583 ),
584 ),
@@ -493,17 +612,18 @@ class BalanceRowWidget extends StatelessWidget {
612 textAlign: TextAlign.center,
613 ),
614 SizedBox(height: 4),
496 - Text(
497 - '${additionalFiatBalance}',
498 - textAlign: TextAlign.center,
499 - style: TextStyle(
500 - fontSize: 12,
501 - fontFamily: 'Lato',
502 - fontWeight: FontWeight.w400,
503 - color: Theme.of(context).extension<BalancePageTheme>()!.textColor,
504 - height: 1,
615 + if (!isTestnet)
616 + Text(
617 + '${additionalFiatBalance}',
618 + textAlign: TextAlign.center,
619 + style: TextStyle(
620 + fontSize: 12,
621 + fontFamily: 'Lato',
622 + fontWeight: FontWeight.w400,
623 + color: Theme.of(context).extension<BalancePageTheme>()!.textColor,
624 + height: 1,
625 + ),
626 ),
506 - ),
627 ],
628 ),
629 ],
lib/src/screens/dashboard/pages/cake_features_page.dart renamed
+18 -44
@@ -1,23 +1,20 @@
1 -import 'package:cake_wallet/routes.dart';
2 -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
1 import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart';
4 -import 'package:cake_wallet/utils/show_pop_up.dart';
2 import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
6 -import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
7 -import 'package:cw_core/wallet_type.dart';
3 +import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart';
4 import 'package:flutter/material.dart';
5 import 'package:cake_wallet/generated/i18n.dart';
6 import 'package:url_launcher/url_launcher.dart';
7 import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
8 +import 'package:flutter_svg/flutter_svg.dart';
9
13 -class MarketPlacePage extends StatelessWidget {
14 - MarketPlacePage({
10 +class CakeFeaturesPage extends StatelessWidget {
11 + CakeFeaturesPage({
12 required this.dashboardViewModel,
16 - required this.marketPlaceViewModel,
13 + required this.cakeFeaturesViewModel,
14 });
15
16 final DashboardViewModel dashboardViewModel;
20 - final MarketPlaceViewModel marketPlaceViewModel;
17 + final CakeFeaturesViewModel cakeFeaturesViewModel;
18 final _scrollController = ScrollController();
19
20 @override
@@ -37,7 +34,7 @@ class MarketPlacePage extends StatelessWidget {
34 children: [
35 SizedBox(height: 50),
36 Text(
40 - S.of(context).market_place,
37 + 'Cake ${S.of(context).features}',
38 style: TextStyle(
39 fontSize: 24,
40 fontWeight: FontWeight.w500,
@@ -59,15 +56,21 @@ class MarketPlacePage extends StatelessWidget {
56 // ),
57 SizedBox(height: 20),
58 DashBoardRoundedCardWidget(
59 + onTap: () => _launchUrl("buy.cakepay.com"),
60 title: S.of(context).cake_pay_web_cards_title,
61 subTitle: S.of(context).cake_pay_web_cards_subtitle,
64 - onTap: () => _launchMarketPlaceUrl("buy.cakepay.com"),
62 + svgPicture: SvgPicture.asset(
63 + 'assets/images/cards.svg',
64 + height: 125,
65 + width: 125,
66 + fit: BoxFit.cover,
67 + ),
68 ),
66 - const SizedBox(height: 20),
69 + SizedBox(height: 10),
70 DashBoardRoundedCardWidget(
71 title: "NanoGPT",
72 subTitle: S.of(context).nanogpt_subtitle,
70 - onTap: () => _launchMarketPlaceUrl("cake.nano-gpt.com"),
73 + onTap: () => _launchUrl("cake.nano-gpt.com"),
74 ),
75 ],
76 ),
@@ -79,41 +82,12 @@ class MarketPlacePage extends StatelessWidget {
82 );
83 }
84
82 - void _launchMarketPlaceUrl(String url) async {
85 + void _launchUrl(String url) {
86 try {
87 launchUrl(
88 Uri.https(url),
89 mode: LaunchMode.externalApplication,
90 );
88 - } catch (e) {
89 - print(e);
90 - }
91 - }
92 -
93 - // TODO: Remove ionia flow/files if we will discard it
94 - void _navigatorToGiftCardsPage(BuildContext context) {
95 - final walletType = dashboardViewModel.type;
96 -
97 - switch (walletType) {
98 - case WalletType.haven:
99 - showPopUp<void>(
100 - context: context,
101 - builder: (BuildContext context) {
102 - return AlertWithOneAction(
103 - alertTitle: S.of(context).error,
104 - alertContent: S.of(context).gift_cards_unavailable,
105 - buttonText: S.of(context).ok,
106 - buttonAction: () => Navigator.of(context).pop());
107 - });
108 - break;
109 - default:
110 - marketPlaceViewModel.isIoniaUserAuthenticated().then((value) {
111 - if (value) {
112 - Navigator.pushNamed(context, Routes.ioniaManageCardsPage);
113 - return;
114 - }
115 - Navigator.of(context).pushNamed(Routes.ioniaWelcomePage);
116 - });
117 - }
91 + } catch (_) {}
92 }
93 }
lib/src/screens/dashboard/widgets/menu_widget.dart
+6
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/generated/i18n.dart';
2 import 'package:cake_wallet/src/widgets/setting_action_button.dart';
3 import 'package:cake_wallet/src/widgets/setting_actions.dart';
4 import 'package:cake_wallet/themes/extensions/menu_theme.dart';
@@ -180,6 +181,11 @@ class MenuWidgetState extends State<MenuWidget> {
181
182 final item = SettingActions.all[index];
183
184 + if (!widget.dashboardViewModel.hasSilentPayments &&
185 + item.name(context) == S.of(context).silent_payments_settings) {
186 + return Container();
187 + }
188 +
189 final isLastTile = index == itemCount - 1;
190
191 return SettingActionButton(
lib/src/screens/dashboard/widgets/present_receive_option_picker.dart
+75 -66
@@ -10,8 +10,7 @@ import 'package:flutter_mobx/flutter_mobx.dart';
10 import 'package:cake_wallet/generated/i18n.dart';
11
12 class PresentReceiveOptionPicker extends StatelessWidget {
13 - PresentReceiveOptionPicker(
14 - {required this.receiveOptionViewModel, required this.color});
13 + PresentReceiveOptionPicker({required this.receiveOptionViewModel, required this.color});
14
15 final ReceiveOptionViewModel receiveOptionViewModel;
16 final Color color;
@@ -43,17 +42,17 @@ class PresentReceiveOptionPicker extends StatelessWidget {
42 Text(
43 S.current.receive,
44 style: TextStyle(
46 - fontSize: 18.0,
47 - fontWeight: FontWeight.bold,
48 - fontFamily: 'Lato',
49 - color: color),
45 + fontSize: 18.0, fontWeight: FontWeight.bold, fontFamily: 'Lato', color: color),
46 ),
47 Observer(
52 - builder: (_) => Text(receiveOptionViewModel.selectedReceiveOption.toString(),
53 - style: TextStyle(
54 - fontSize: 10.0,
55 - fontWeight: FontWeight.w500,
56 - color: color)))
48 + builder: (_) => Text(
49 + receiveOptionViewModel.selectedReceiveOption
50 + .toString()
51 + .replaceAll(RegExp(r'silent payments', caseSensitive: false),
52 + S.current.silent_payments)
53 + .replaceAll(
54 + RegExp(r'default', caseSensitive: false), S.current.string_default),
55 + style: TextStyle(fontSize: 10.0, fontWeight: FontWeight.w500, color: color)))
56 ],
57 ),
58 SizedBox(width: 5),
@@ -73,65 +72,75 @@ class PresentReceiveOptionPicker extends StatelessWidget {
72 backgroundColor: Colors.transparent,
73 body: Stack(
74 alignment: AlignmentDirectional.center,
76 - children:[ AlertBackground(
77 - child: Column(
78 - mainAxisSize: MainAxisSize.min,
79 - mainAxisAlignment: MainAxisAlignment.center,
80 - children: [
81 - Spacer(),
82 - Container(
83 - margin: EdgeInsets.symmetric(horizontal: 24),
84 - decoration: BoxDecoration(
85 - borderRadius: BorderRadius.circular(30),
86 - color: Theme.of(context).colorScheme.background,
87 - ),
88 - child: Padding(
89 - padding: const EdgeInsets.only(top: 24, bottom: 24),
90 - child: (ListView.separated(
91 - padding: EdgeInsets.zero,
92 - shrinkWrap: true,
93 - itemCount: receiveOptionViewModel.options.length,
94 - itemBuilder: (_, index) {
95 - final option = receiveOptionViewModel.options[index];
96 - return InkWell(
97 - onTap: () {
98 - Navigator.pop(popUpContext);
75 + children: [
76 + AlertBackground(
77 + child: Column(
78 + mainAxisSize: MainAxisSize.min,
79 + mainAxisAlignment: MainAxisAlignment.center,
80 + children: [
81 + Spacer(),
82 + Container(
83 + margin: EdgeInsets.symmetric(horizontal: 24),
84 + decoration: BoxDecoration(
85 + borderRadius: BorderRadius.circular(30),
86 + color: Theme.of(context).colorScheme.background,
87 + ),
88 + child: Padding(
89 + padding: const EdgeInsets.only(top: 24, bottom: 24),
90 + child: (ListView.separated(
91 + padding: EdgeInsets.zero,
92 + shrinkWrap: true,
93 + itemCount: receiveOptionViewModel.options.length,
94 + itemBuilder: (_, index) {
95 + final option = receiveOptionViewModel.options[index];
96 + return InkWell(
97 + onTap: () {
98 + Navigator.pop(popUpContext);
99
100 - receiveOptionViewModel.selectReceiveOption(option);
101 - },
102 - child: Padding(
103 - padding: const EdgeInsets.only(left: 24, right: 24),
104 - child: Observer(builder: (_) {
105 - final value = receiveOptionViewModel.selectedReceiveOption;
100 + receiveOptionViewModel.selectReceiveOption(option);
101 + },
102 + child: Padding(
103 + padding: const EdgeInsets.only(left: 24, right: 24),
104 + child: Observer(builder: (_) {
105 + final value = receiveOptionViewModel.selectedReceiveOption;
106
107 - return Row(
108 - mainAxisAlignment: MainAxisAlignment.spaceBetween,
109 - children: [
110 - Text(option.toString(),
111 - textAlign: TextAlign.left,
112 - style: textSmall(
113 - color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
114 - ).copyWith(
115 - fontWeight:
116 - value == option ? FontWeight.w800 : FontWeight.w500,
117 - )),
118 - RoundedCheckbox(
119 - value: value == option,
120 - )
121 - ],
122 - );
123 - }),
124 - ),
125 - );
126 - },
127 - separatorBuilder: (_, index) => SizedBox(height: 30),
128 - )),
107 + return Row(
108 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
109 + children: [
110 + Text(
111 + option
112 + .toString()
113 + .replaceAll(
114 + RegExp(r'silent payments', caseSensitive: false),
115 + S.current.silent_payments)
116 + .replaceAll(RegExp(r'default', caseSensitive: false),
117 + S.current.string_default),
118 + textAlign: TextAlign.left,
119 + style: textSmall(
120 + color: Theme.of(context)
121 + .extension<CakeTextTheme>()!
122 + .titleColor,
123 + ).copyWith(
124 + fontWeight:
125 + value == option ? FontWeight.w800 : FontWeight.w500,
126 + )),
127 + RoundedCheckbox(
128 + value: value == option,
129 + )
130 + ],
131 + );
132 + }),
133 + ),
134 + );
135 + },
136 + separatorBuilder: (_, index) => SizedBox(height: 30),
137 + )),
138 + ),
139 ),
130 - ),
131 - Spacer()
132 - ],
140 + Spacer()
141 + ],
142 + ),
143 ),
134 - ),
144 AlertCloseButton(onTap: () => Navigator.of(popUpContext).pop(), bottom: 40)
145 ],
146 ),
lib/src/screens/receive/receive_page.dart
+139 -111
@@ -67,8 +67,7 @@ class ReceivePage extends BasePage {
67
68 @override
69 Widget Function(BuildContext, Widget) get rootWrapper =>
70 - (BuildContext context, Widget scaffold) =>
71 - GradientBackground(scaffold: scaffold);
70 + (BuildContext context, Widget scaffold) => GradientBackground(scaffold: scaffold);
71
72 @override
73 Widget trailing(BuildContext context) {
@@ -99,115 +98,144 @@ class ReceivePage extends BasePage {
98
99 @override
100 Widget body(BuildContext context) {
102 - return KeyboardActions(
103 - config: KeyboardActionsConfig(
104 - keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
105 - keyboardBarColor: Theme.of(context).extension<KeyboardTheme>()!.keyboardBarColor,
106 - nextFocus: false,
107 - actions: [
108 - KeyboardActionsItem(
109 - focusNode: _cryptoAmountFocus,
110 - toolbarButtons: [(_) => KeyboardDoneButton()],
111 - )
112 - ]),
113 - child: SingleChildScrollView(
114 - child: Column(
115 - children: <Widget>[
116 - Padding(
117 - padding: EdgeInsets.fromLTRB(24, 50, 24, 24),
118 - child: QRWidget(
119 - addressListViewModel: addressListViewModel,
120 - formKey: _formKey,
121 - heroTag: _heroTag,
122 - amountTextFieldFocusNode: _cryptoAmountFocus,
123 - amountController: _amountController,
124 - isLight: currentTheme.type == ThemeType.light),
125 - ),
126 - Observer(
127 - builder: (_) => ListView.separated(
128 - padding: EdgeInsets.all(0),
129 - separatorBuilder: (context, _) => const HorizontalSectionDivider(),
130 - shrinkWrap: true,
131 - physics: NeverScrollableScrollPhysics(),
132 - itemCount: addressListViewModel.items.length,
133 - itemBuilder: (context, index) {
134 - final item = addressListViewModel.items[index];
135 - Widget cell = Container();
136 -
137 - if (item is WalletAccountListHeader) {
138 - cell = HeaderTile(
139 - showTrailingButton: true,
140 - walletAddressListViewModel: addressListViewModel,
141 - trailingButtonTap: () async {
142 - if (addressListViewModel.type == WalletType.monero ||
143 - addressListViewModel.type == WalletType.haven) {
144 - await showPopUp<void>(
145 - context: context,
146 - builder: (_) => getIt.get<MoneroAccountListPage>());
147 - } else {
148 - await showPopUp<void>(
149 - context: context,
150 - builder: (_) => getIt.get<NanoAccountListPage>());
151 - }
152 - },
153 - title: S.of(context).accounts,
154 - trailingIcon: Icon(
155 - Icons.arrow_forward_ios,
156 - size: 14,
157 - color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
158 - ));
159 - }
160 -
161 - if (item is WalletAddressListHeader) {
162 - cell = HeaderTile(
163 - title: S.of(context).addresses,
164 - walletAddressListViewModel: addressListViewModel,
165 - showTrailingButton: !addressListViewModel.isAutoGenerateSubaddressEnabled,
166 - showSearchButton: true,
167 - trailingButtonTap: () =>
168 - Navigator.of(context).pushNamed(Routes.newSubaddress),
169 - trailingIcon: Icon(
170 - Icons.add,
171 - size: 20,
172 - color: Theme.of(context)
173 - .extension<ReceivePageTheme>()!
174 - .iconsColor,
175 - ));
176 - }
177 -
178 - if (item is WalletAddressListItem) {
179 - cell = Observer(builder: (_) {
180 - final isCurrent =
181 - item.address == addressListViewModel.address.address;
182 - final backgroundColor = isCurrent
183 - ? Theme.of(context).extension<ReceivePageTheme>()!.currentTileBackgroundColor
184 - : Theme.of(context).extension<ReceivePageTheme>()!.tilesBackgroundColor;
185 - final textColor = isCurrent
186 - ? Theme.of(context).extension<ReceivePageTheme>()!.currentTileTextColor
187 - : Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor;
188 -
189 - return AddressCell.fromItem(item,
190 - isCurrent: isCurrent,
191 - hasBalance: addressListViewModel.isElectrumWallet,
192 - backgroundColor: backgroundColor,
193 - textColor: textColor,
194 - onTap: (_) => addressListViewModel.setAddress(item),
195 - onEdit: () => Navigator.of(context)
196 - .pushNamed(Routes.newSubaddress, arguments: item));
197 - });
198 - }
199 -
200 - return index != 0
201 - ? cell
202 - : ClipRRect(
203 - borderRadius: BorderRadius.only(
204 - topLeft: Radius.circular(30),
205 - topRight: Radius.circular(30)),
206 - child: cell,
207 - );
208 - })),
209 - ],
101 + return KeyboardActions(
102 + config: KeyboardActionsConfig(
103 + keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
104 + keyboardBarColor: Theme.of(context).extension<KeyboardTheme>()!.keyboardBarColor,
105 + nextFocus: false,
106 + actions: [
107 + KeyboardActionsItem(
108 + focusNode: _cryptoAmountFocus,
109 + toolbarButtons: [(_) => KeyboardDoneButton()],
110 + )
111 + ]),
112 + child: SingleChildScrollView(
113 + child: Column(
114 + children: <Widget>[
115 + Padding(
116 + padding: EdgeInsets.fromLTRB(24, 50, 24, 24),
117 + child: QRWidget(
118 + addressListViewModel: addressListViewModel,
119 + formKey: _formKey,
120 + heroTag: _heroTag,
121 + amountTextFieldFocusNode: _cryptoAmountFocus,
122 + amountController: _amountController,
123 + isLight: currentTheme.type == ThemeType.light),
124 ),
211 - ));
125 + Observer(
126 + builder: (_) => ListView.separated(
127 + padding: EdgeInsets.all(0),
128 + separatorBuilder: (context, _) => const HorizontalSectionDivider(),
129 + shrinkWrap: true,
130 + physics: NeverScrollableScrollPhysics(),
131 + itemCount: addressListViewModel.items.length,
132 + itemBuilder: (context, index) {
133 + final item = addressListViewModel.items[index];
134 + Widget cell = Container();
135 +
136 + if (item is WalletAccountListHeader) {
137 + cell = HeaderTile(
138 + showTrailingButton: true,
139 + walletAddressListViewModel: addressListViewModel,
140 + trailingButtonTap: () async {
141 + if (addressListViewModel.type == WalletType.monero ||
142 + addressListViewModel.type == WalletType.haven) {
143 + await showPopUp<void>(
144 + context: context,
145 + builder: (_) => getIt.get<MoneroAccountListPage>());
146 + } else {
147 + await showPopUp<void>(
148 + context: context,
149 + builder: (_) => getIt.get<NanoAccountListPage>());
150 + }
151 + },
152 + title: S.of(context).accounts,
153 + trailingIcon: Icon(
154 + Icons.arrow_forward_ios,
155 + size: 14,
156 + color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
157 + ));
158 + }
159 +
160 + if (item is WalletAddressListHeader) {
161 + final hasTitle = item.title != null;
162 +
163 + cell = HeaderTile(
164 + title: hasTitle ? item.title! : S.of(context).addresses,
165 + walletAddressListViewModel: addressListViewModel,
166 + showTrailingButton:
167 + !addressListViewModel.isAutoGenerateSubaddressEnabled && !hasTitle,
168 + showSearchButton: true,
169 + trailingButtonTap: () =>
170 + Navigator.of(context).pushNamed(Routes.newSubaddress),
171 + trailingIcon: hasTitle
172 + ? null
173 + : Icon(
174 + Icons.add,
175 + size: 20,
176 + color:
177 + Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
178 + ),
179 + );
180 + }
181 +
182 + if (item is WalletAddressListItem) {
183 + cell = Observer(builder: (_) {
184 + final isCurrent = item.address == addressListViewModel.address.address;
185 + final backgroundColor = isCurrent
186 + ? Theme.of(context)
187 + .extension<ReceivePageTheme>()!
188 + .currentTileBackgroundColor
189 + : Theme.of(context)
190 + .extension<ReceivePageTheme>()!
191 + .tilesBackgroundColor;
192 + final textColor = isCurrent
193 + ? Theme.of(context)
194 + .extension<ReceivePageTheme>()!
195 + .currentTileTextColor
196 + : Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor;
197 +
198 + return AddressCell.fromItem(
199 + item,
200 + isCurrent: isCurrent,
201 + hasBalance: addressListViewModel.isElectrumWallet,
202 + backgroundColor: backgroundColor,
203 + textColor: textColor,
204 + onTap: item.isOneTimeReceiveAddress == true
205 + ? null
206 + : (_) => addressListViewModel.setAddress(item),
207 + onEdit: item.isOneTimeReceiveAddress == true || item.isPrimary
208 + ? null
209 + : () => Navigator.of(context)
210 + .pushNamed(Routes.newSubaddress, arguments: item),
211 + onDelete: !addressListViewModel.isSilentPayments || item.isPrimary
212 + ? null
213 + : () => addressListViewModel.deleteAddress(item),
214 + );
215 + });
216 + }
217 +
218 + return index != 0
219 + ? cell
220 + : ClipRRect(
221 + borderRadius: BorderRadius.only(
222 + topLeft: Radius.circular(30), topRight: Radius.circular(30)),
223 + child: cell,
224 + );
225 + })),
226 + Padding(
227 + padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
228 + child: Text(
229 + addressListViewModel.isSilentPayments
230 + ? S.of(context).silent_payments_disclaimer
231 + : S.of(context).electrum_address_disclaimer,
232 + textAlign: TextAlign.center,
233 + style: TextStyle(
234 + fontSize: 15,
235 + color: Theme.of(context).extension<BalancePageTheme>()!.labelTextColor)),
236 + ),
237 + ],
238 + ),
239 + ));
240 }
241 }
lib/src/screens/receive/widgets/address_cell.dart
+25 -10
@@ -15,18 +15,22 @@ class AddressCell extends StatelessWidget {
15 required this.textColor,
16 this.onTap,
17 this.onEdit,
18 + this.onDelete,
19 this.txCount,
20 this.balance,
21 this.isChange = false,
22 this.hasBalance = false});
23
23 - factory AddressCell.fromItem(WalletAddressListItem item,
24 - {required bool isCurrent,
25 - required Color backgroundColor,
26 - required Color textColor,
27 - Function(String)? onTap,
28 - bool hasBalance = false,
29 - Function()? onEdit}) =>
24 + factory AddressCell.fromItem(
25 + WalletAddressListItem item, {
26 + required bool isCurrent,
27 + required Color backgroundColor,
28 + required Color textColor,
29 + Function(String)? onTap,
30 + bool hasBalance = false,
31 + Function()? onEdit,
32 + Function()? onDelete,
33 + }) =>
34 AddressCell(
35 address: item.address,
36 name: item.name ?? '',
@@ -36,6 +40,7 @@ class AddressCell extends StatelessWidget {
40 textColor: textColor,
41 onTap: onTap,
42 onEdit: onEdit,
43 + onDelete: onDelete,
44 txCount: item.txCount,
45 balance: item.balance,
46 isChange: item.isChange,
@@ -49,6 +54,7 @@ class AddressCell extends StatelessWidget {
54 final Color textColor;
55 final Function(String)? onTap;
56 final Function()? onEdit;
57 + final Function()? onDelete;
58 final int? txCount;
59 final String? balance;
60 final bool isChange;
@@ -64,7 +70,8 @@ class AddressCell extends StatelessWidget {
70 } else {
71 return formatIfCashAddr.substring(0, addressPreviewLength) +
72 '...' +
67 - formatIfCashAddr.substring(formatIfCashAddr.length - addressPreviewLength, formatIfCashAddr.length);
73 + formatIfCashAddr.substring(
74 + formatIfCashAddr.length - addressPreviewLength, formatIfCashAddr.length);
75 }
76 }
77
@@ -139,7 +146,7 @@ class AddressCell extends StatelessWidget {
146 mainAxisSize: MainAxisSize.max,
147 children: [
148 Text(
142 - 'Balance: $balance',
149 + '${S.of(context).balance}: $balance',
150 style: TextStyle(
151 fontSize: 16,
152 fontWeight: FontWeight.w600,
@@ -180,7 +187,7 @@ class AddressCell extends StatelessWidget {
187
188 ActionPane _actionPane(BuildContext context) => ActionPane(
189 motion: const ScrollMotion(),
183 - extentRatio: 0.3,
190 + extentRatio: onDelete != null ? 0.4 : 0.3,
191 children: [
192 SlidableAction(
193 onPressed: (_) => onEdit?.call(),
@@ -189,6 +196,14 @@ class AddressCell extends StatelessWidget {
196 icon: Icons.edit,
197 label: S.of(context).edit,
198 ),
199 + if (onDelete != null)
200 + SlidableAction(
201 + onPressed: (_) => onDelete!.call(),
202 + backgroundColor: Colors.red,
203 + foregroundColor: Colors.white,
204 + icon: Icons.delete,
205 + label: S.of(context).delete,
206 + ),
207 ],
208 );
209 }
lib/src/screens/rescan/rescan_page.dart
+52 -11
@@ -1,3 +1,7 @@
1 +import 'package:cake_wallet/bitcoin/bitcoin.dart';
2 +import 'package:cake_wallet/main.dart';
3 +import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
4 +import 'package:cake_wallet/utils/show_pop_up.dart';
5 import 'package:flutter/material.dart';
6 import 'package:flutter_mobx/flutter_mobx.dart';
7 import 'package:cake_wallet/view_model/rescan_view_model.dart';
@@ -11,7 +15,8 @@ class RescanPage extends BasePage {
15 : _blockchainHeightWidgetKey = GlobalKey<BlockchainHeightState>();
16
17 @override
14 - String get title => S.current.rescan;
18 + String get title =>
19 + _rescanViewModel.isSilentPaymentsScan ? S.current.silent_payments_scanning : S.current.rescan;
20 final GlobalKey<BlockchainHeightState> _blockchainHeightWidgetKey;
21 final RescanViewModel _rescanViewModel;
22
@@ -19,20 +24,28 @@ class RescanPage extends BasePage {
24 Widget body(BuildContext context) {
25 return Padding(
26 padding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
22 - child:
23 - Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
24 - BlockchainHeightWidget(key: _blockchainHeightWidgetKey,
25 - onHeightOrDateEntered: (value) =>
26 - _rescanViewModel.isButtonEnabled = value),
27 + child: Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
28 + Observer(
29 + builder: (_) => BlockchainHeightWidget(
30 + key: _blockchainHeightWidgetKey,
31 + onHeightOrDateEntered: (value) => _rescanViewModel.isButtonEnabled = value,
32 + isSilentPaymentsScan: _rescanViewModel.isSilentPaymentsScan,
33 + doSingleScan: _rescanViewModel.doSingleScan,
34 + toggleSingleScan: () =>
35 + _rescanViewModel.doSingleScan = !_rescanViewModel.doSingleScan,
36 + )),
37 Observer(
38 builder: (_) => LoadingPrimaryButton(
29 - isLoading:
30 - _rescanViewModel.state == RescanWalletState.rescaning,
39 + isLoading: _rescanViewModel.state == RescanWalletState.rescaning,
40 text: S.of(context).rescan,
41 onPressed: () async {
33 - await _rescanViewModel.rescanCurrentWallet(
34 - restoreHeight:
35 - _blockchainHeightWidgetKey.currentState!.height);
42 + if (_rescanViewModel.isSilentPaymentsScan) {
43 + return _toggleSilentPaymentsScanning(context);
44 + }
45 +
46 + _rescanViewModel.rescanCurrentWallet(
47 + restoreHeight: _blockchainHeightWidgetKey.currentState!.height);
48 +
49 Navigator.of(context).pop();
50 },
51 color: Theme.of(context).primaryColor,
@@ -42,4 +55,32 @@ class RescanPage extends BasePage {
55 ]),
56 );
57 }
58 +
59 + Future<void> _toggleSilentPaymentsScanning(BuildContext context) async {
60 + final height = _blockchainHeightWidgetKey.currentState!.height;
61 +
62 + Navigator.of(context).pop();
63 +
64 + final needsToSwitch =
65 + await bitcoin!.getNodeIsElectrsSPEnabled(_rescanViewModel.wallet) == false;
66 +
67 + if (needsToSwitch) {
68 + return showPopUp<void>(
69 + context: navigatorKey.currentState!.context,
70 + builder: (BuildContext _dialogContext) => AlertWithTwoActions(
71 + alertTitle: S.of(_dialogContext).change_current_node_title,
72 + alertContent: S.of(_dialogContext).confirm_silent_payments_switch_node,
73 + rightButtonText: S.of(_dialogContext).ok,
74 + leftButtonText: S.of(_dialogContext).cancel,
75 + actionRightButton: () async {
76 + Navigator.of(_dialogContext).pop();
77 +
78 + _rescanViewModel.rescanCurrentWallet(restoreHeight: height);
79 + },
80 + actionLeftButton: () => Navigator.of(_dialogContext).pop(),
81 + ));
82 + }
83 +
84 + _rescanViewModel.rescanCurrentWallet(restoreHeight: height);
85 + }
86 }
lib/src/screens/send/send_page.dart
+5
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/bitcoin/bitcoin.dart';
2 import 'package:cake_wallet/core/auth_service.dart';
3 import 'package:cake_wallet/entities/contact_record.dart';
4 import 'package:cake_wallet/core/execution_state.dart';
@@ -420,6 +421,10 @@ class SendPage extends BasePage {
421 return;
422 }
423
424 + if (sendViewModel.isElectrumWallet) {
425 + bitcoin!.updateFeeRates(sendViewModel.wallet);
426 + }
427 +
428 reaction((_) => sendViewModel.state, (ExecutionState state) {
429 if (dialogContext != null && dialogContext?.mounted == true) {
430 Navigator.of(dialogContext!).pop();
lib/src/screens/settings/connection_sync_page.dart
+3 -1
@@ -39,7 +39,9 @@ class ConnectionSyncPage extends BasePage {
39 ),
40 if (dashboardViewModel.hasRescan) ...[
41 SettingsCellWithArrow(
42 - title: S.current.rescan,
42 + title: dashboardViewModel.hasSilentPayments
43 + ? S.current.silent_payments_scanning
44 + : S.current.rescan,
45 handler: (context) => Navigator.of(context).pushNamed(Routes.rescan),
46 ),
47 if (DeviceInfo.instance.isMobile && FeatureFlag.isBackgroundSyncEnabled) ...[
lib/src/screens/settings/silent_payments_settings.dart new
+50
@@ -0,0 +1,50 @@
1 +import 'package:cake_wallet/generated/i18n.dart';
2 +import 'package:cake_wallet/routes.dart';
3 +import 'package:cake_wallet/src/screens/base_page.dart';
4 +import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
5 +import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
6 +import 'package:cake_wallet/view_model/settings/silent_payments_settings_view_model.dart';
7 +import 'package:flutter/material.dart';
8 +import 'package:flutter_mobx/flutter_mobx.dart';
9 +
10 +class SilentPaymentsSettingsPage extends BasePage {
11 + SilentPaymentsSettingsPage(this._silentPaymentsSettingsViewModel);
12 +
13 + @override
14 + String get title => S.current.silent_payments_settings;
15 +
16 + final SilentPaymentsSettingsViewModel _silentPaymentsSettingsViewModel;
17 +
18 + @override
19 + Widget body(BuildContext context) {
20 + return SingleChildScrollView(
21 + child: Observer(builder: (_) {
22 + return Container(
23 + padding: EdgeInsets.only(top: 10),
24 + child: Column(
25 + children: [
26 + SettingsSwitcherCell(
27 + title: S.current.silent_payments_display_card,
28 + value: _silentPaymentsSettingsViewModel.silentPaymentsCardDisplay,
29 + onValueChange: (_, bool value) {
30 + _silentPaymentsSettingsViewModel.setSilentPaymentsCardDisplay(value);
31 + },
32 + ),
33 + SettingsSwitcherCell(
34 + title: S.current.silent_payments_always_scan,
35 + value: _silentPaymentsSettingsViewModel.silentPaymentsAlwaysScan,
36 + onValueChange: (_, bool value) {
37 + _silentPaymentsSettingsViewModel.setSilentPaymentsAlwaysScan(value);
38 + },
39 + ),
40 + SettingsCellWithArrow(
41 + title: S.current.silent_payments_scanning,
42 + handler: (BuildContext context) => Navigator.of(context).pushNamed(Routes.rescan),
43 + ),
44 + ],
45 + ),
46 + );
47 + }),
48 + );
49 + }
50 +}
lib/src/screens/unspent_coins/unspent_coins_list_page.dart
+1
@@ -57,6 +57,7 @@ class UnspentCoinsListFormState extends State<UnspentCoinsListForm> {
57 isSending: item.isSending,
58 isFrozen: item.isFrozen,
59 isChange: item.isChange,
60 + isSilentPayment: item.isSilentPayment,
61 onCheckBoxTap: item.isFrozen
62 ? null
63 : () async {
lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart
+22 -6
@@ -12,6 +12,7 @@ class UnspentCoinsListItem extends StatelessWidget {
12 required this.isSending,
13 required this.isFrozen,
14 required this.isChange,
15 + required this.isSilentPayment,
16 this.onCheckBoxTap,
17 });
18
@@ -21,18 +22,16 @@ class UnspentCoinsListItem extends StatelessWidget {
22 final bool isSending;
23 final bool isFrozen;
24 final bool isChange;
25 + final bool isSilentPayment;
26 final Function()? onCheckBoxTap;
27
28 @override
29 Widget build(BuildContext context) {
30 final unselectedItemColor = Theme.of(context).cardColor;
31 final selectedItemColor = Theme.of(context).primaryColor;
30 - final itemColor = isSending
31 - ? selectedItemColor
32 - : unselectedItemColor;
33 - final amountColor = isSending
34 - ? Colors.white
35 - : Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor;
32 + final itemColor = isSending ? selectedItemColor : unselectedItemColor;
33 + final amountColor =
34 + isSending ? Colors.white : Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor;
35 final addressColor = isSending
36 ? Colors.white.withOpacity(0.5)
37 : Theme.of(context).extension<CakeTextTheme>()!.buttonSecondaryTextColor;
@@ -121,6 +120,23 @@ class UnspentCoinsListItem extends StatelessWidget {
120 ),
121 ),
122 ),
123 + if (isSilentPayment)
124 + Container(
125 + height: 17,
126 + padding: EdgeInsets.only(left: 6, right: 6),
127 + decoration: BoxDecoration(
128 + borderRadius: BorderRadius.all(Radius.circular(8.5)),
129 + color: Colors.white),
130 + alignment: Alignment.center,
131 + child: Text(
132 + S.of(context).silent_payments,
133 + style: TextStyle(
134 + color: itemColor,
135 + fontSize: 7,
136 + fontWeight: FontWeight.w600,
137 + ),
138 + ),
139 + ),
140 ],
141 ),
142 ),
lib/src/screens/wallet_list/wallet_list_page.dart
+9 -2
@@ -96,6 +96,7 @@ class WalletListBody extends StatefulWidget {
96 class WalletListBodyState extends State<WalletListBody> {
97 final moneroIcon = Image.asset('assets/images/monero_logo.png', height: 24, width: 24);
98 final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
99 + final tBitcoinIcon = Image.asset('assets/images/tbtc.png', height: 24, width: 24);
100 final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24);
101 final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24);
102 final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
@@ -162,7 +163,10 @@ class WalletListBodyState extends State<WalletListBody> {
163 crossAxisAlignment: CrossAxisAlignment.center,
164 children: <Widget>[
165 wallet.isEnabled
165 - ? _imageFor(type: wallet.type)
166 + ? _imageFor(
167 + type: wallet.type,
168 + isTestnet: wallet.isTestnet,
169 + )
170 : nonWalletTypeIcon,
171 SizedBox(width: 10),
172 Flexible(
@@ -297,9 +301,12 @@ class WalletListBodyState extends State<WalletListBody> {
301 );
302 }
303
300 - Image _imageFor({required WalletType type}) {
304 + Image _imageFor({required WalletType type, bool? isTestnet}) {
305 switch (type) {
306 case WalletType.bitcoin:
307 + if (isTestnet == true) {
308 + return tBitcoinIcon;
309 + }
310 return bitcoinIcon;
311 case WalletType.monero:
312 return moneroIcon;
lib/src/widgets/blockchain_height_widget.dart
+52 -14
@@ -1,3 +1,5 @@
1 +import 'package:cake_wallet/bitcoin/bitcoin.dart';
2 +import 'package:cake_wallet/src/widgets/standard_switch.dart';
3 import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
4 import 'package:cake_wallet/utils/date_picker.dart';
5 import 'package:flutter/material.dart';
@@ -12,13 +14,19 @@ class BlockchainHeightWidget extends StatefulWidget {
14 this.onHeightChange,
15 this.focusNode,
16 this.onHeightOrDateEntered,
15 - this.hasDatePicker = true})
16 - : super(key: key);
17 + this.hasDatePicker = true,
18 + this.isSilentPaymentsScan = false,
19 + this.toggleSingleScan,
20 + this.doSingleScan = false,
21 + }) : super(key: key);
22
23 final Function(int)? onHeightChange;
24 final Function(bool)? onHeightOrDateEntered;
25 final FocusNode? focusNode;
26 final bool hasDatePicker;
27 + final bool isSilentPaymentsScan;
28 + final bool doSingleScan;
29 + final Function()? toggleSingleScan;
30
31 @override
32 State<StatefulWidget> createState() => BlockchainHeightState();
@@ -64,9 +72,10 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
72 child: BaseTextFormField(
73 focusNode: widget.focusNode,
74 controller: restoreHeightController,
67 - keyboardType: TextInputType.numberWithOptions(
68 - signed: false, decimal: false),
69 - hintText: S.of(context).widgets_restore_from_blockheight,
75 + keyboardType: TextInputType.numberWithOptions(signed: false, decimal: false),
76 + hintText: widget.isSilentPaymentsScan
77 + ? S.of(context).silent_payments_scan_from_height
78 + : S.of(context).widgets_restore_from_blockheight,
79 )))
80 ],
81 ),
@@ -78,8 +87,7 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
87 style: TextStyle(
88 fontSize: 16.0,
89 fontWeight: FontWeight.w500,
81 - color:
82 - Theme.of(context).extension<CakeTextTheme>()!.titleColor),
90 + color: Theme.of(context).extension<CakeTextTheme>()!.titleColor),
91 ),
92 ),
93 Row(
@@ -91,22 +99,47 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
99 child: IgnorePointer(
100 child: BaseTextFormField(
101 controller: dateController,
94 - hintText: S.of(context).widgets_restore_from_date,
102 + hintText: widget.isSilentPaymentsScan
103 + ? S.of(context).silent_payments_scan_from_date
104 + : S.of(context).widgets_restore_from_date,
105 )),
106 ),
107 ))
108 ],
109 ),
110 + if (widget.isSilentPaymentsScan)
111 + Padding(
112 + padding: EdgeInsets.only(top: 24),
113 + child: Row(
114 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
115 + children: [
116 + Text(
117 + S.of(context).scan_one_block,
118 + style: TextStyle(
119 + fontSize: 14,
120 + fontWeight: FontWeight.normal,
121 + color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
122 + ),
123 + ),
124 + Padding(
125 + padding: const EdgeInsets.only(right: 8),
126 + child: StandardSwitch(
127 + value: widget.doSingleScan,
128 + onTaped: () => widget.toggleSingleScan?.call(),
129 + ),
130 + )
131 + ],
132 + ),
133 + ),
134 Padding(
135 padding: EdgeInsets.only(left: 40, right: 40, top: 24),
136 child: Text(
103 - S.of(context).restore_from_date_or_blockheight,
137 + widget.isSilentPaymentsScan
138 + ? S.of(context).silent_payments_scan_from_date_or_blockheight
139 + : S.of(context).restore_from_date_or_blockheight,
140 textAlign: TextAlign.center,
141 style: TextStyle(
106 - fontSize: 12,
107 - fontWeight: FontWeight.normal,
108 - color: Theme.of(context).hintColor
109 - ),
142 + fontSize: 12, fontWeight: FontWeight.normal, color: Theme.of(context).hintColor),
143 ),
144 )
145 ]
@@ -123,7 +156,12 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
156 lastDate: now);
157
158 if (date != null) {
126 - final height = monero!.getHeightByDate(date: date);
159 + int height;
160 + if (widget.isSilentPaymentsScan) {
161 + height = bitcoin!.getHeightByDate(date: date);
162 + } else {
163 + height = monero!.getHeightByDate(date: date);
164 + }
165 setState(() {
166 dateController.text = DateFormat('yyyy-MM-dd').format(date);
167 restoreHeightController.text = '$height';
lib/src/widgets/dashboard_card_widget.dart
+49 -18
@@ -2,19 +2,28 @@ import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
2 import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart';
3 import 'package:flutter/material.dart';
4 import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
5 +import 'package:flutter_svg/flutter_svg.dart';
6
7 class DashBoardRoundedCardWidget extends StatelessWidget {
8 DashBoardRoundedCardWidget({
9 required this.onTap,
10 required this.title,
11 required this.subTitle,
12 + this.hint,
13 + this.svgPicture,
14 + this.icon,
15 this.onClose,
16 + this.customBorder,
17 });
18
19 final VoidCallback onTap;
20 final VoidCallback? onClose;
21 final String title;
22 final String subTitle;
23 + final Widget? hint;
24 + final SvgPicture? svgPicture;
25 + final Icon? icon;
26 + final double? customBorder;
27
28 @override
29 Widget build(BuildContext context) {
@@ -26,34 +35,56 @@ class DashBoardRoundedCardWidget extends StatelessWidget {
35 child: Stack(
36 children: [
37 Container(
29 - padding: EdgeInsets.fromLTRB(20, 20, 40, 20),
38 + padding: EdgeInsets.all(20),
39 width: double.infinity,
40 decoration: BoxDecoration(
41 color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
33 - borderRadius: BorderRadius.circular(20),
42 + borderRadius: BorderRadius.circular(customBorder ?? 20),
43 border: Border.all(
44 color: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
45 ),
46 ),
47 child: Column(
39 - crossAxisAlignment: CrossAxisAlignment.start,
48 children: [
41 - Text(
42 - title,
43 - style: TextStyle(
44 - color: Theme.of(context).extension<DashboardPageTheme>()!.cardTextColor,
45 - fontSize: 24,
46 - fontWeight: FontWeight.w900,
47 - ),
49 + Row(
50 + crossAxisAlignment: CrossAxisAlignment.start,
51 + children: [
52 + Expanded(
53 + child: Column(
54 + crossAxisAlignment: CrossAxisAlignment.start,
55 + children: [
56 + Text(
57 + title,
58 + style: TextStyle(
59 + color:
60 + Theme.of(context).extension<DashboardPageTheme>()!.cardTextColor,
61 + fontSize: 24,
62 + fontWeight: FontWeight.w900,
63 + ),
64 + softWrap: true,
65 + ),
66 + SizedBox(height: 5),
67 + Text(
68 + subTitle,
69 + style: TextStyle(
70 + color: Theme.of(context)
71 + .extension<DashboardPageTheme>()!
72 + .cardTextColor,
73 + fontWeight: FontWeight.w500,
74 + fontFamily: 'Lato'),
75 + softWrap: true,
76 + ),
77 + ],
78 + ),
79 + ),
80 + if (svgPicture != null) svgPicture!,
81 + if (icon != null) icon!
82 + ],
83 ),
49 - SizedBox(height: 5),
50 - Text(
51 - subTitle,
52 - style: TextStyle(
53 - color: Theme.of(context).extension<DashboardPageTheme>()!.cardTextColor,
54 - fontWeight: FontWeight.w500,
55 - fontFamily: 'Lato'),
56 - )
84 + if (hint != null) ...[
85 + SizedBox(height: 10),
86 + hint!,
87 + ]
88 ],
89 ),
90 ),
lib/src/widgets/setting_actions.dart
+10
@@ -17,6 +17,7 @@ class SettingActions {
17 connectionSettingAction,
18 walletSettingAction,
19 addressBookSettingAction,
20 + silentPaymentsSettingAction,
21 securityBackupSettingAction,
22 privacySettingAction,
23 displaySettingAction,
@@ -35,6 +36,15 @@ class SettingActions {
36 supportSettingAction,
37 ];
38
39 + static SettingActions silentPaymentsSettingAction = SettingActions._(
40 + name: (context) => S.of(context).silent_payments_settings,
41 + image: 'assets/images/bitcoin_menu.png',
42 + onTap: (BuildContext context) {
43 + Navigator.pop(context);
44 + Navigator.of(context).pushNamed(Routes.silentPaymentsSettings);
45 + },
46 + );
47 +
48 static SettingActions connectionSettingAction = SettingActions._(
49 name: (context) => S.of(context).connection_sync,
50 image: 'assets/images/nodes_menu.png',
lib/store/settings_store.dart
+28 -1
@@ -1,5 +1,4 @@
1 import 'dart:io';
2 -
2 import 'package:cake_wallet/bitcoin/bitcoin.dart';
3 import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
4 import 'package:cake_wallet/core/secure_storage.dart';
@@ -108,6 +107,8 @@ abstract class SettingsStoreBase with Store {
107 required this.lookupsOpenAlias,
108 required this.lookupsENS,
109 required this.customBitcoinFeeRate,
110 + required this.silentPaymentsCardDisplay,
111 + required this.silentPaymentsAlwaysScan,
112 TransactionPriority? initialBitcoinTransactionPriority,
113 TransactionPriority? initialMoneroTransactionPriority,
114 TransactionPriority? initialHavenTransactionPriority,
@@ -518,6 +519,16 @@ abstract class SettingsStoreBase with Store {
519 (int customBitcoinFeeRate) =>
520 _sharedPreferences.setInt(PreferencesKey.customBitcoinFeeRate, customBitcoinFeeRate));
521
522 + reaction((_) => silentPaymentsCardDisplay, (bool silentPaymentsCardDisplay) {
523 + _sharedPreferences.setBool(
524 + PreferencesKey.silentPaymentsCardDisplay, silentPaymentsCardDisplay);
525 + });
526 +
527 + reaction(
528 + (_) => silentPaymentsAlwaysScan,
529 + (bool silentPaymentsAlwaysScan) => _sharedPreferences.setBool(
530 + PreferencesKey.silentPaymentsAlwaysScan, silentPaymentsAlwaysScan));
531 +
532 this.nodes.observe((change) {
533 if (change.newValue != null && change.key != null) {
534 _saveCurrentNode(change.newValue!, change.key!);
@@ -713,6 +724,12 @@ abstract class SettingsStoreBase with Store {
724 @observable
725 int customBitcoinFeeRate;
726
727 + @observable
728 + bool silentPaymentsCardDisplay;
729 +
730 + @observable
731 + bool silentPaymentsAlwaysScan;
732 +
733 final SecureStorage _secureStorage;
734 final SharedPreferences _sharedPreferences;
735 final BackgroundTasks _backgroundTasks;
@@ -859,6 +876,10 @@ abstract class SettingsStoreBase with Store {
876 final lookupsOpenAlias = sharedPreferences.getBool(PreferencesKey.lookupsOpenAlias) ?? true;
877 final lookupsENS = sharedPreferences.getBool(PreferencesKey.lookupsENS) ?? true;
878 final customBitcoinFeeRate = sharedPreferences.getInt(PreferencesKey.customBitcoinFeeRate) ?? 1;
879 + final silentPaymentsCardDisplay =
880 + sharedPreferences.getBool(PreferencesKey.silentPaymentsCardDisplay) ?? true;
881 + final silentPaymentsAlwaysScan =
882 + sharedPreferences.getBool(PreferencesKey.silentPaymentsAlwaysScan) ?? false;
883
884 // If no value
885 if (pinLength == null || pinLength == 0) {
@@ -1103,6 +1124,8 @@ abstract class SettingsStoreBase with Store {
1124 lookupsOpenAlias: lookupsOpenAlias,
1125 lookupsENS: lookupsENS,
1126 customBitcoinFeeRate: customBitcoinFeeRate,
1127 + silentPaymentsCardDisplay: silentPaymentsCardDisplay,
1128 + silentPaymentsAlwaysScan: silentPaymentsAlwaysScan,
1129 initialMoneroTransactionPriority: moneroTransactionPriority,
1130 initialBitcoinTransactionPriority: bitcoinTransactionPriority,
1131 initialHavenTransactionPriority: havenTransactionPriority,
@@ -1242,6 +1265,10 @@ abstract class SettingsStoreBase with Store {
1265 lookupsOpenAlias = sharedPreferences.getBool(PreferencesKey.lookupsOpenAlias) ?? true;
1266 lookupsENS = sharedPreferences.getBool(PreferencesKey.lookupsENS) ?? true;
1267 customBitcoinFeeRate = sharedPreferences.getInt(PreferencesKey.customBitcoinFeeRate) ?? 1;
1268 + silentPaymentsCardDisplay =
1269 + sharedPreferences.getBool(PreferencesKey.silentPaymentsCardDisplay) ?? true;
1270 + silentPaymentsAlwaysScan =
1271 + sharedPreferences.getBool(PreferencesKey.silentPaymentsAlwaysScan) ?? false;
1272 final nodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
1273 final bitcoinElectrumServerId =
1274 sharedPreferences.getInt(PreferencesKey.currentBitcoinElectrumSererIdKey);
lib/view_model/contact_list/contact_list_view_model.dart
+11 -5
@@ -2,6 +2,7 @@ import 'dart:async';
2 import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
3 import 'package:cake_wallet/entities/contact_base.dart';
4 import 'package:cake_wallet/entities/wallet_contact.dart';
5 +import 'package:cake_wallet/generated/i18n.dart';
6 import 'package:cake_wallet/store/settings_store.dart';
7 import 'package:cw_core/wallet_info.dart';
8 import 'package:cw_core/wallet_type.dart';
@@ -40,16 +41,19 @@ abstract class ContactListViewModelBase with Store {
41 });
42 } else if (info.addresses?.isNotEmpty == true) {
43 info.addresses!.forEach((address, label) {
44 + if (label.isEmpty) {
45 + return;
46 + }
47 final name = _createName(info.name, label);
48 walletContacts.add(WalletContact(
49 address,
50 name,
47 - walletTypeToCryptoCurrency(info.type),
51 + walletTypeToCryptoCurrency(info.type,
52 + isTestnet:
53 + info.network == null ? false : info.network!.toLowerCase().contains("testnet")),
54 ));
49 - // Only one contact address per wallet
50 - return;
55 });
52 - } else if (info.address != null) {
56 + } else {
57 walletContacts.add(WalletContact(
58 info.address,
59 info.name,
@@ -64,7 +68,9 @@ abstract class ContactListViewModelBase with Store {
68 }
69
70 String _createName(String walletName, String label) {
67 - return label.isNotEmpty ? '$walletName ($label)' : walletName;
71 + return label.isNotEmpty
72 + ? '$walletName (${label.replaceAll(RegExp(r'active', caseSensitive: false), S.current.active).replaceAll(RegExp(r'silent payments', caseSensitive: false), S.current.silent_payments)})'
73 + : walletName;
74 }
75
76 final bool isAutoGenerateEnabled;
lib/view_model/dashboard/balance_view_model.dart
+3
@@ -60,6 +60,9 @@ abstract class BalanceViewModelBase with Store {
60 @observable
61 WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet;
62
63 + @computed
64 + bool get hasSilentPayments => wallet.type == WalletType.bitcoin;
65 +
66 @computed
67 double get price {
68 final price = fiatConvertationStore.prices[appStore.wallet!.currency];
lib/view_model/dashboard/cake_features_view_model.dart new
+16
@@ -0,0 +1,16 @@
1 +import 'package:cake_wallet/ionia/ionia_service.dart';
2 +import 'package:mobx/mobx.dart';
3 +
4 +part 'cake_features_view_model.g.dart';
5 +
6 +class CakeFeaturesViewModel = CakeFeaturesViewModelBase with _$CakeFeaturesViewModel;
7 +
8 +abstract class CakeFeaturesViewModelBase with Store {
9 + final IoniaService _ioniaService;
10 +
11 + CakeFeaturesViewModelBase(this._ioniaService);
12 +
13 + Future<bool> isIoniaUserAuthenticated() async {
14 + return await _ioniaService.isLogined();
15 + }
16 +}
lib/view_model/dashboard/dashboard_view_model.dart
+35 -1
@@ -45,6 +45,7 @@ import 'package:cw_core/wallet_type.dart';
45 import 'package:eth_sig_util/util/utils.dart';
46 import 'package:flutter/services.dart';
47 import 'package:mobx/mobx.dart';
48 +import 'package:cake_wallet/bitcoin/bitcoin.dart';
49 import 'package:http/http.dart' as http;
50 import 'package:shared_preferences/shared_preferences.dart';
51
@@ -201,6 +202,14 @@ abstract class DashboardViewModelBase with Store {
202
203 return true;
204 });
205 +
206 + if (hasSilentPayments) {
207 + silentPaymentsScanningActive = bitcoin!.getScanningActive(wallet);
208 +
209 + reaction((_) => wallet.syncStatus, (SyncStatus syncStatus) {
210 + silentPaymentsScanningActive = bitcoin!.getScanningActive(wallet);
211 + });
212 + }
213 }
214
215 @observable
@@ -287,11 +296,36 @@ abstract class DashboardViewModelBase with Store {
296 @observable
297 WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet;
298
290 - bool get hasRescan => wallet.type == WalletType.monero || wallet.type == WalletType.haven;
299 + @computed
300 + bool get isTestnet => wallet.type == WalletType.bitcoin && bitcoin!.isTestnet(wallet);
301 +
302 + @computed
303 + bool get hasRescan =>
304 + wallet.type == WalletType.bitcoin ||
305 + wallet.type == WalletType.monero ||
306 + wallet.type == WalletType.haven;
307 +
308 + @computed
309 + bool get hasSilentPayments => wallet.type == WalletType.bitcoin;
310 +
311 + @computed
312 + bool get showSilentPaymentsCard => hasSilentPayments && settingsStore.silentPaymentsCardDisplay;
313
314 final KeyService keyService;
315 final SharedPreferences sharedPreferences;
316
317 + @observable
318 + bool silentPaymentsScanningActive = false;
319 +
320 + @action
321 + void setSilentPaymentsScanning(bool active) {
322 + silentPaymentsScanningActive = active;
323 +
324 + if (hasSilentPayments) {
325 + bitcoin!.setScanningActive(wallet, active);
326 + }
327 + }
328 +
329 BalanceViewModel balanceViewModel;
330
331 AppStore appStore;
lib/view_model/dashboard/market_place_view_model.dart deleted
-17
@@ -1,17 +0,0 @@
1 -import 'package:cake_wallet/ionia/ionia_service.dart';
2 -import 'package:mobx/mobx.dart';
3 -
4 -part 'market_place_view_model.g.dart';
5 -
6 -class MarketPlaceViewModel = MarketPlaceViewModelBase with _$MarketPlaceViewModel;
7 -
8 -abstract class MarketPlaceViewModelBase with Store {
9 - final IoniaService _ioniaService;
10 -
11 - MarketPlaceViewModelBase(this._ioniaService);
12 -
13 -
14 - Future<bool> isIoniaUserAuthenticated() async {
15 - return await _ioniaService.isLogined();
16 - }
17 -}
\ No newline at end of file
lib/view_model/rescan_view_model.dart
+20 -7
@@ -1,4 +1,6 @@
1 +import 'package:cake_wallet/bitcoin/bitcoin.dart';
2 import 'package:cw_core/wallet_base.dart';
3 +import 'package:cw_core/wallet_type.dart';
4 import 'package:mobx/mobx.dart';
5
6 part 'rescan_view_model.g.dart';
@@ -8,11 +10,12 @@ class RescanViewModel = RescanViewModelBase with _$RescanViewModel;
10 enum RescanWalletState { rescaning, none }
11
12 abstract class RescanViewModelBase with Store {
11 - RescanViewModelBase(this._wallet)
12 - : state = RescanWalletState.none,
13 - isButtonEnabled = false;
13 + RescanViewModelBase(this.wallet)
14 + : state = RescanWalletState.none,
15 + isButtonEnabled = false,
16 + doSingleScan = false;
17
15 - final WalletBase _wallet;
18 + final WalletBase wallet;
19
20 @observable
21 RescanWalletState state;
@@ -20,11 +23,21 @@ abstract class RescanViewModelBase with Store {
23 @observable
24 bool isButtonEnabled;
25
26 + @observable
27 + bool doSingleScan;
28 +
29 + @computed
30 + bool get isSilentPaymentsScan => wallet.type == WalletType.bitcoin;
31 +
32 @action
33 Future<void> rescanCurrentWallet({required int restoreHeight}) async {
34 state = RescanWalletState.rescaning;
26 - await _wallet.rescan(height: restoreHeight);
27 - _wallet.transactionHistory.clear();
35 + if (wallet.type != WalletType.bitcoin) {
36 + wallet.rescan(height: restoreHeight);
37 + wallet.transactionHistory.clear();
38 + } else {
39 + bitcoin!.rescan(wallet, height: restoreHeight, doSingleScan: doSingleScan);
40 + }
41 state = RescanWalletState.none;
42 }
30 -}
\ No newline at end of file
43 +}
lib/view_model/send/send_view_model.dart
+14 -1
@@ -229,7 +229,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
229 isFiatDisabled ? '' : pendingTransactionFeeFiatAmount + ' ' + fiat.title;
230
231 @computed
232 - bool get isReadyForSend => wallet.syncStatus is SyncedSyncStatus;
232 + bool get isReadyForSend =>
233 + wallet.syncStatus is SyncedSyncStatus ||
234 + // If silent payments scanning, can still send payments
235 + (wallet.type == WalletType.bitcoin && wallet.syncStatus is SyncingSyncStatus);
236
237 @computed
238 List<Template> get templates => sendTemplateViewModel.templates
@@ -599,6 +602,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
602 walletType == WalletType.litecoin ||
603 walletType == WalletType.bitcoinCash) {
604 if (error is TransactionWrongBalanceException) {
605 + if (error.amount != null)
606 + return S.current
607 + .tx_wrong_balance_with_amount_exception(currency.toString(), error.amount.toString());
608 +
609 return S.current.tx_wrong_balance_exception(currency.toString());
610 }
611 if (error is TransactionNoInputsException) {
@@ -625,9 +632,15 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
632 if (error is TransactionCommitFailedVoutNegative) {
633 return S.current.tx_rejected_vout_negative;
634 }
635 + if (error is TransactionCommitFailedBIP68Final) {
636 + return S.current.tx_rejected_bip68_final;
637 + }
638 if (error is TransactionNoDustOnChangeException) {
639 return S.current.tx_commit_exception_no_dust_on_change(error.min, error.max);
640 }
641 + if (error is TransactionInputNotSupported) {
642 + return S.current.tx_invalid_input;
643 + }
644 }
645
646 return errorMessage;
lib/view_model/settings/silent_payments_settings_view_model.dart new
+33
@@ -0,0 +1,33 @@
1 +import 'package:cake_wallet/bitcoin/bitcoin.dart';
2 +import 'package:cake_wallet/store/settings_store.dart';
3 +import 'package:cw_core/wallet_base.dart';
4 +import 'package:mobx/mobx.dart';
5 +
6 +part 'silent_payments_settings_view_model.g.dart';
7 +
8 +class SilentPaymentsSettingsViewModel = SilentPaymentsSettingsViewModelBase
9 + with _$SilentPaymentsSettingsViewModel;
10 +
11 +abstract class SilentPaymentsSettingsViewModelBase with Store {
12 + SilentPaymentsSettingsViewModelBase(this._settingsStore, this._wallet);
13 +
14 + final SettingsStore _settingsStore;
15 + final WalletBase _wallet;
16 +
17 + @computed
18 + bool get silentPaymentsCardDisplay => _settingsStore.silentPaymentsCardDisplay;
19 +
20 + @computed
21 + bool get silentPaymentsAlwaysScan => _settingsStore.silentPaymentsAlwaysScan;
22 +
23 + @action
24 + void setSilentPaymentsCardDisplay(bool value) {
25 + _settingsStore.silentPaymentsCardDisplay = value;
26 + }
27 +
28 + @action
29 + void setSilentPaymentsAlwaysScan(bool value) {
30 + _settingsStore.silentPaymentsAlwaysScan = value;
31 + if (value) bitcoin!.setScanningActive(_wallet, true);
32 + }
33 +}
lib/view_model/unspent_coins/unspent_coins_item.dart
+5 -1
@@ -15,7 +15,8 @@ abstract class UnspentCoinsItemBase with Store {
15 required this.isChange,
16 required this.amountRaw,
17 required this.vout,
18 - required this.keyImage
18 + required this.keyImage,
19 + required this.isSilentPayment,
20 });
21
22 @observable
@@ -47,4 +48,7 @@ abstract class UnspentCoinsItemBase with Store {
48
49 @observable
50 String? keyImage;
51 +
52 + @observable
53 + bool isSilentPayment;
54 }
lib/view_model/unspent_coins/unspent_coins_list_view_model.dart
+30 -17
@@ -1,10 +1,12 @@
1 import 'package:cake_wallet/bitcoin/bitcoin.dart';
2 import 'package:cake_wallet/monero/monero.dart';
3 +import 'package:cake_wallet/utils/exception_handler.dart';
4 import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
5 import 'package:cw_core/unspent_coins_info.dart';
6 import 'package:cw_core/unspent_transaction_output.dart';
7 import 'package:cw_core/wallet_base.dart';
8 import 'package:cw_core/wallet_type.dart';
9 +import 'package:flutter/cupertino.dart';
10 import 'package:hive/hive.dart';
11 import 'package:mobx/mobx.dart';
12
@@ -86,22 +88,33 @@ abstract class UnspentCoinsListViewModelBase with Store {
88 @action
89 void _updateUnspentCoinsInfo() {
90 _items.clear();
89 - _items.addAll(_getUnspents().map((elem) {
90 - final info =
91 - getUnspentCoinInfo(elem.hash, elem.address, elem.value, elem.vout, elem.keyImage);
92 -
93 - return UnspentCoinsItem(
94 - address: elem.address,
95 - amount: '${formatAmountToString(elem.value)} ${wallet.currency.title}',
96 - hash: elem.hash,
97 - isFrozen: info.isFrozen,
98 - note: info.note,
99 - isSending: info.isSending,
100 - amountRaw: elem.value,
101 - vout: elem.vout,
102 - keyImage: elem.keyImage,
103 - isChange: elem.isChange,
104 - );
105 - }));
91 +
92 + List<UnspentCoinsItem> unspents = [];
93 + _getUnspents().forEach((elem) {
94 + try {
95 + final info =
96 + getUnspentCoinInfo(elem.hash, elem.address, elem.value, elem.vout, elem.keyImage);
97 +
98 + unspents.add(UnspentCoinsItem(
99 + address: elem.address,
100 + amount: '${formatAmountToString(elem.value)} ${wallet.currency.title}',
101 + hash: elem.hash,
102 + isFrozen: info.isFrozen,
103 + note: info.note,
104 + isSending: info.isSending,
105 + amountRaw: elem.value,
106 + vout: elem.vout,
107 + keyImage: elem.keyImage,
108 + isChange: elem.isChange,
109 + isSilentPayment: info.isSilentPayment ?? false,
110 + ));
111 + } catch (e, s) {
112 + print(s);
113 + print(e.toString());
114 + ExceptionHandler.onError(FlutterErrorDetails(exception: e, stack: s));
115 + }
116 + });
117 +
118 + _items.addAll(unspents);
119 }
120 }
lib/view_model/wallet_address_list/wallet_address_list_header.dart
+4 -1
@@ -1,3 +1,6 @@
1 import 'package:cake_wallet/utils/list_item.dart';
2
3 -class WalletAddressListHeader extends ListItem {}
\ No newline at end of file
3 +class WalletAddressListHeader extends ListItem {
4 + final String? title;
5 + WalletAddressListHeader({this.title});
6 +}
lib/view_model/wallet_address_list/wallet_address_list_item.dart
+6 -3
@@ -8,8 +8,10 @@ class WalletAddressListItem extends ListItem {
8 this.name,
9 this.txCount,
10 this.balance,
11 - this.isChange = false})
12 - : super();
11 + this.isChange = false,
12 + // Address that is only ever used once, shouldn't be used to receive funds, copy and paste, share etc
13 + this.isOneTimeReceiveAddress = false,
14 + }) : super();
15
16 final int? id;
17 final bool isPrimary;
@@ -18,7 +20,8 @@ class WalletAddressListItem extends ListItem {
20 final int? txCount;
21 final String? balance;
22 final bool isChange;
23 + final bool? isOneTimeReceiveAddress;
24
25 @override
26 String toString() => name ?? address;
24 -}
\ No newline at end of file
27 +}
lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
+59 -12
@@ -334,20 +334,55 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
334 }
335
336 if (isElectrumWallet) {
337 - final addressItems = bitcoin!.getSubAddresses(wallet).map((subaddress) {
338 - final isPrimary = subaddress.id == 0;
337 + if (bitcoin!.hasSelectedSilentPayments(wallet)) {
338 + final addressItems = bitcoin!.getSilentPaymentAddresses(wallet).map((address) {
339 + final isPrimary = address.id == 0;
340
340 - return WalletAddressListItem(
341 - id: subaddress.id,
341 + return WalletAddressListItem(
342 + id: address.id,
343 isPrimary: isPrimary,
343 - name: subaddress.name,
344 - address: subaddress.address,
345 - txCount: subaddress.txCount,
344 + name: address.name,
345 + address: address.address,
346 + txCount: address.txCount,
347 balance: AmountConverter.amountIntToString(
347 - walletTypeToCryptoCurrency(type), subaddress.balance),
348 - isChange: subaddress.isChange);
349 - });
350 - addressList.addAll(addressItems);
348 + walletTypeToCryptoCurrency(type), address.balance),
349 + isChange: address.isChange,
350 + );
351 + });
352 + addressList.addAll(addressItems);
353 + addressList.add(WalletAddressListHeader(title: S.current.received));
354 +
355 + final receivedAddressItems =
356 + bitcoin!.getSilentPaymentReceivedAddresses(wallet).map((address) {
357 + return WalletAddressListItem(
358 + id: address.id,
359 + isPrimary: false,
360 + name: address.name,
361 + address: address.address,
362 + txCount: address.txCount,
363 + balance: AmountConverter.amountIntToString(
364 + walletTypeToCryptoCurrency(type), address.balance),
365 + isChange: address.isChange,
366 + isOneTimeReceiveAddress: true,
367 + );
368 + });
369 + addressList.addAll(receivedAddressItems);
370 + } else {
371 + final addressItems = bitcoin!.getSubAddresses(wallet).map((subaddress) {
372 + final isPrimary = subaddress.id == 0;
373 +
374 + return WalletAddressListItem(
375 + id: subaddress.id,
376 + isPrimary: isPrimary,
377 + name: subaddress.name,
378 + address: subaddress.address,
379 + txCount: subaddress.txCount,
380 + balance: AmountConverter.amountIntToString(
381 + walletTypeToCryptoCurrency(type), subaddress.balance),
382 + isChange: subaddress.isChange);
383 + });
384 + addressList.addAll(addressItems);
385 + }
386 }
387
388 if (wallet.type == WalletType.ethereum) {
@@ -416,9 +451,14 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
451 wallet.type == WalletType.litecoin ||
452 wallet.type == WalletType.bitcoinCash;
453
454 + @computed
455 + bool get isSilentPayments =>
456 + wallet.type == WalletType.bitcoin && bitcoin!.hasSelectedSilentPayments(wallet);
457 +
458 @computed
459 bool get isAutoGenerateSubaddressEnabled =>
421 - _settingsStore.autoGenerateSubaddressStatus != AutoGenerateSubaddressStatus.disabled;
460 + _settingsStore.autoGenerateSubaddressStatus != AutoGenerateSubaddressStatus.disabled &&
461 + !isSilentPayments;
462
463 List<ListItem> _baseItems;
464
@@ -478,4 +518,11 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
518 amount = '';
519 }
520 }
521 +
522 + @action
523 + void deleteAddress(ListItem item) {
524 + if (wallet.type == WalletType.bitcoin && item is WalletAddressListItem) {
525 + bitcoin!.deleteSilentPaymentAddress(wallet, item.address);
526 + }
527 + }
528 }
lib/view_model/wallet_list/wallet_list_item.dart
+2
@@ -7,6 +7,7 @@ class WalletListItem {
7 required this.key,
8 this.isCurrent = false,
9 this.isEnabled = true,
10 + this.isTestnet = false,
11 });
12
13 final String name;
@@ -14,4 +15,5 @@ class WalletListItem {
15 final bool isCurrent;
16 final dynamic key;
17 final bool isEnabled;
18 + final bool isTestnet;
19 }
lib/view_model/wallet_list/wallet_list_view_model.dart
+1
@@ -61,6 +61,7 @@ abstract class WalletListViewModelBase with Store {
61 key: info.key,
62 isCurrent: info.name == _appStore.wallet?.name && info.type == _appStore.wallet?.type,
63 isEnabled: availableWalletTypes.contains(info.type),
64 + isTestnet: info.network?.toLowerCase().contains('testnet') ?? false,
65 ),
66 ),
67 );
macos/Podfile.lock
+7 -1
@@ -47,6 +47,8 @@ PODS:
47 - shared_preferences_foundation (0.0.1):
48 - Flutter
49 - FlutterMacOS
50 + - sp_scanner (0.0.1):
51 + - FlutterMacOS
52 - url_launcher_macos (0.0.1):
53 - FlutterMacOS
54 - wakelock_plus (0.0.1):
@@ -67,6 +69,7 @@ DEPENDENCIES:
69 - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
70 - share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`)
71 - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
72 + - sp_scanner (from `Flutter/ephemeral/.symlinks/plugins/sp_scanner/macos`)
73 - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
74 - wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`)
75
@@ -104,6 +107,8 @@ EXTERNAL SOURCES:
107 :path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos
108 shared_preferences_foundation:
109 :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
110 + sp_scanner:
111 + :path: Flutter/ephemeral/.symlinks/plugins/sp_scanner/macos
112 url_launcher_macos:
113 :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
114 wakelock_plus:
@@ -126,7 +131,8 @@ SPEC CHECKSUMS:
131 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
132 share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4
133 shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
129 - url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
134 + sp_scanner: 269d96e0ec3173e69156be7239b95182be3b8303
135 + url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399
136 wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269
137
138 PODFILE CHECKSUM: 65ec1541137fb5b35d00490dec1bb48d4d9586bb
model_generator.sh
+11 -11
@@ -1,12 +1,12 @@
1 -cd cw_core && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
2 -cd cw_evm && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
3 -cd cw_monero && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
4 -cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
5 -cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
6 -cd cw_nano && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
7 -cd cw_bitcoin_cash && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
8 -cd cw_solana && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
9 -cd cw_tron && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
10 -cd cw_ethereum && flutter pub get && cd ..
11 -cd cw_polygon && flutter pub get && cd ..
1 +cd cw_core; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd ..
2 +cd cw_evm; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd ..
3 +cd cw_monero; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd ..
4 +cd cw_bitcoin; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd ..
5 +cd cw_haven; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd ..
6 +cd cw_nano; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd ..
7 +cd cw_bitcoin_cash; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd ..
8 +cd cw_solana; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd ..
9 +cd cw_tron; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd ..
10 +cd cw_polygon; flutter pub get; cd ..
11 +cd cw_ethereum; flutter pub get; cd ..
12 flutter packages pub run build_runner build --delete-conflicting-outputs
pubspec_base.yaml
+3 -3
@@ -105,8 +105,8 @@ dependencies:
105 solana: ^0.30.1
106 bitcoin_base:
107 git:
108 - url: https://github.com/cake-tech/bitcoin_base.git
109 - ref: cake-update-v2
108 + url: https://github.com/cake-tech/bitcoin_base
109 + ref: cake-update-v3
110 ledger_flutter: ^1.0.1
111
112 dev_dependencies:
@@ -117,7 +117,7 @@ dev_dependencies:
117 mobx_codegen: ^2.1.1
118 build_resolvers: ^2.0.9
119 hive_generator: ^1.1.3
120 - flutter_launcher_icons: ^0.11.0
120 + # flutter_launcher_icons: ^0.11.0
121 # check flutter_launcher_icons for usage
122 pedantic: ^1.8.0
123 # replace https://github.com/dart-lang/lints#migrating-from-packagepedantic
res/values/strings_ar.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "نسخ الاحتياطي",
72 "backup_file": "ملف النسخ الاحتياطي",
73 "backup_password": "كلمة مرور النسخ الاحتياطي",
74 + "balance": "توازن",
75 "balance_page": "صفحة التوازن",
76 "bill_amount": "مبلغ الفاتورة",
77 "billing_address_info": "إذا طُلب منك عنوان إرسال فواتير ، فأدخل عنوان الشحن الخاص بك",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "موضوع البيتكوين الظلام",
80 "bitcoin_light_theme": "موضوع البيتكوين الخفيفة",
81 "bitcoin_payments_require_1_confirmation": "تتطلب مدفوعات Bitcoin تأكيدًا واحدًا ، والذي قد يستغرق 20 دقيقة أو أكثر. شكرا لصبرك! سيتم إرسال بريد إلكتروني إليك عند تأكيد الدفع.",
82 + "block_remaining": "1 كتلة متبقية",
83 "Blocks_remaining": "بلوك متبقي ${status}",
84 "bluetooth": "بلوتوث",
85 "bright_theme": "مشرق",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "تأكيد خصم الرسوم",
142 "confirm_fee_deduction_content": "هل توافق على خصم الرسوم من الإخراج؟",
143 "confirm_sending": "تأكيد الإرسال",
144 + "confirm_silent_payments_switch_node": "حاليا مطلوب لتبديل العقد لمسح المدفوعات الصامتة",
145 "confirmations": "التأكيدات",
146 "confirmed": "رصيد مؤكد",
147 "confirmed_tx": "مؤكد",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "نقوم بإنشاء عناوين جديدة في كل مرة تستخدم فيها عنوانًا ، لكن العناوين السابقة تستمر في العمل",
225 "email_address": "عنوان البريد الالكترونى",
226 "enable_replace_by_fee": "تمكين الاستبدال",
227 + "enable_silent_payments_scanning": "تمكين المسح الضوئي للمدفوعات الصامتة",
228 "enabled": "ممكنة",
229 "enter_amount": "أدخل المبلغ",
230 "enter_backup_password": "أدخل كلمة المرور الاحتياطية هنا",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "سوف ترسل الأموال إلى\n${recipient_name}",
283 "failed_authentication": "${state_error} فشل المصادقة.",
284 "faq": "الأسئلة الشائعة",
285 + "features": "سمات",
286 "fetching": "جار الجلب",
287 "fiat_api": "Fiat API",
288 "fiat_balance": "الرصيد فيات",
@@ -531,6 +536,7 @@
536 "save_backup_password_alert": "حفظ كلمة المرور الاحتياطية",
537 "save_to_downloads": "ﺕﻼﻳﺰﻨﺘﻟﺍ ﻲﻓ ﻆﻔﺣ",
538 "saved_the_trade_id": "لقد تم حفظ معرف العملية",
539 + "scan_one_block": "مسح كتلة واحدة",
540 "scan_qr_code": "امسح رمز QR ضوئيًا",
541 "scan_qr_code_to_get_address": "امسح ال QR للحصول على العنوان",
542 "scan_qr_on_device": " ﺮﺧﺁ ﺯﺎﻬﺟ ﻰﻠﻋ ﺎﻴًﺋﻮﺿ ﺍﺬﻫ ﺔﻌﻳﺮﺴﻟﺍ ﺔﺑﺎﺠﺘﺳﻻﺍ ﺰﻣﺭ ﺢﺴﻤﺑ ﻢﻗ",
@@ -641,11 +647,22 @@
647 "sign_up": "اشتراك",
648 "signTransaction": " ﺔﻠﻣﺎﻌﻤﻟﺍ ﻊﻴﻗﻮﺗ",
649 "signup_for_card_accept_terms": "قم بالتسجيل للحصول على البطاقة وقبول الشروط.",
650 + "silent_payments": "مدفوعات صامتة",
651 + "silent_payments_always_scan": "حدد المدفوعات الصامتة دائمًا المسح الضوئي",
652 + "silent_payments_disclaimer": "العناوين الجديدة ليست هويات جديدة. إنها إعادة استخدام هوية موجودة مع ملصق مختلف.",
653 + "silent_payments_display_card": "عرض بطاقة المدفوعات الصامتة",
654 + "silent_payments_scan_from_date": "فحص من التاريخ",
655 + "silent_payments_scan_from_date_or_blockheight": "يرجى إدخال ارتفاع الكتلة الذي تريد بدء المسح الضوئي للمدفوعات الصامتة الواردة ، أو استخدام التاريخ بدلاً من ذلك. يمكنك اختيار ما إذا كانت المحفظة تواصل مسح كل كتلة ، أو تتحقق فقط من الارتفاع المحدد.",
656 + "silent_payments_scan_from_height": "فحص من ارتفاع الكتلة",
657 + "silent_payments_scanned_tip": "ممسوح ليفحص! (${tip})",
658 + "silent_payments_scanning": "المدفوعات الصامتة المسح الضوئي",
659 + "silent_payments_settings": "إعدادات المدفوعات الصامتة",
660 "slidable": "قابل للانزلاق",
661 "sort_by": "ترتيب حسب",
662 "spend_key_private": "مفتاح الإنفاق (خاص)",
663 "spend_key_public": "مفتاح الإنفاق (عام)",
664 "status": "الحالة:",
665 + "string_default": "تقصير",
666 "subaddress_title": "قائمة العناوين الفرعية",
667 "subaddresses": "العناوين الفرعية",
668 "submit_request": "تقديم طلب",
@@ -670,10 +687,13 @@
687 "sync_status_starting_sync": "بدء المزامنة",
688 "sync_status_syncronized": "متزامن",
689 "sync_status_syncronizing": "يتم المزامنة",
690 + "sync_status_timed_out": "نفد وقته",
691 + "sync_status_unsupported": "عقدة غير مدعومة",
692 "syncing_wallet_alert_content": "قد لا يكتمل رصيدك وقائمة المعاملات الخاصة بك حتى تظهر عبارة “SYNCHRONIZED“ في الأعلى. انقر / اضغط لمعرفة المزيد.",
693 "syncing_wallet_alert_title": "محفظتك تتم مزامنتها",
694 "template": "قالب",
695 "template_name": "اسم القالب",
696 + "testnet_coins_no_value": "عملات TestNet ليس لها قيمة",
697 "third_intro_content": "يعيش Yats خارج Cake Wallet أيضًا. يمكن استبدال أي عنوان محفظة على وجه الأرض بـ Yat!",
698 "third_intro_title": "يتماشي Yat بلطف مع الآخرين",
699 "thorchain_contract_address_not_supported": "لا يدعم Thorchain الإرسال إلى عنوان العقد",
@@ -749,13 +769,16 @@
769 "trusted": "موثوق به",
770 "tx_commit_exception_no_dust_on_change": "يتم رفض المعاملة مع هذا المبلغ. باستخدام هذه العملات المعدنية ، يمكنك إرسال ${min} دون تغيير أو ${max} الذي يعيد التغيير.",
771 "tx_commit_failed": "فشل ارتكاب المعاملة. يرجى الاتصال بالدعم.",
772 + "tx_invalid_input": "أنت تستخدم نوع الإدخال الخاطئ لهذا النوع من الدفع",
773 "tx_no_dust_exception": "يتم رفض المعاملة عن طريق إرسال مبلغ صغير جدًا. يرجى محاولة زيادة المبلغ.",
774 "tx_not_enough_inputs_exception": "لا يكفي المدخلات المتاحة. الرجاء تحديد المزيد تحت التحكم في العملة",
775 + "tx_rejected_bip68_final": "تحتوي المعاملة على مدخلات غير مؤكدة وفشلت في استبدال الرسوم.",
776 "tx_rejected_dust_change": "المعاملة التي يتم رفضها بموجب قواعد الشبكة ، ومبلغ التغيير المنخفض (الغبار). حاول إرسال كل أو تقليل المبلغ.",
777 "tx_rejected_dust_output": "المعاملة التي يتم رفضها بموجب قواعد الشبكة ، وكمية الإخراج المنخفض (الغبار). يرجى زيادة المبلغ.",
778 "tx_rejected_dust_output_send_all": "المعاملة التي يتم رفضها بموجب قواعد الشبكة ، وكمية الإخراج المنخفض (الغبار). يرجى التحقق من رصيد العملات المعدنية المحددة تحت التحكم في العملة.",
779 "tx_rejected_vout_negative": "لا يوجد ما يكفي من الرصيد لدفع رسوم هذه الصفقة. يرجى التحقق من رصيد العملات المعدنية تحت السيطرة على العملة.",
780 "tx_wrong_balance_exception": "ليس لديك ما يكفي من ${currency} لإرسال هذا المبلغ.",
781 + "tx_wrong_balance_with_amount_exception": "ليس لديك ما يكفي ${currency} لإرسال المبلغ الإجمالي ${amount}",
782 "tx_zero_fee_exception": "لا يمكن إرسال معاملة مع 0 رسوم. حاول زيادة المعدل أو التحقق من اتصالك للحصول على أحدث التقديرات.",
783 "unavailable_balance": "ﺮﻓﻮﺘﻣ ﺮﻴﻏ ﺪﻴﺻﺭ",
784 "unavailable_balance_description": ".ﺎﻫﺪﻴﻤﺠﺗ ءﺎﻐﻟﺇ ﺭﺮﻘﺗ ﻰﺘﺣ ﺕﻼﻣﺎﻌﻤﻠﻟ ﻝﻮﺻﻮﻠﻟ ﺔﻠﺑﺎﻗ ﺮﻴﻏ ﺓﺪﻤﺠﻤﻟﺍ ﺓﺪﺻﺭﻷﺍ ﻞﻈﺗ ﺎﻤﻨﻴﺑ ،ﺎﻬﺑ ﺔﺻﺎﺨﻟﺍ ﺕﻼﻣﺎﻌﻤﻟﺍ ﻝﺎﻤﺘﻛﺍ ﺩﺮﺠﻤﺑ ﺔﺣﺎﺘﻣ ﺔﻠﻔﻘﻤﻟﺍ ﺓﺪﺻﺭﻷﺍ ﺢﺒﺼﺘﺳ .ﻚﺑ ﺔﺻﺎﺨﻟﺍ ﺕﻼﻤﻌﻟﺍ ﻲﻓ ﻢﻜﺤﺘﻟﺍ ﺕﺍﺩﺍﺪﻋﺇ ﻲﻓ ﻂﺸﻧ ﻞﻜﺸﺑ ﺎﻫﺪﻴﻤﺠﺘﺑ ﺖﻤﻗ",
@@ -809,6 +832,7 @@
832 "warning": "تحذير",
833 "welcome": "مرحبا بك في",
834 "welcome_to_cakepay": "مرحبا بكم في Cake Pay!",
835 + "what_is_silent_payments": "ما هي المدفوعات الصامتة؟",
836 "widgets_address": "عنوان",
837 "widgets_or": "أو",
838 "widgets_restore_from_blockheight": "استعادة من ارتفاع البلوك",
res/values/strings_bg.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "Резервно копие",
72 "backup_file": "Резервно копие",
73 "backup_password": "Парола за възстановяване",
74 + "balance": "Баланс",
75 "balance_page": "Страница за баланс",
76 "bill_amount": "Искана сума",
77 "billing_address_info": "Ако Ви попитат за билинг адрес, въведето своя адрес за доставка",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "Тъмна тема за биткойн",
80 "bitcoin_light_theme": "Лека биткойн тема",
81 "bitcoin_payments_require_1_confirmation": "Плащанията с Bitcoin изискват потвърждение, което може да отнеме 20 минути или повече. Благодарим за търпението! Ще получите имейл, когато плащането е потвърдено.",
82 + "block_remaining": "1 блок останал",
83 "Blocks_remaining": "${status} оставащи блока",
84 "bluetooth": "Bluetooth",
85 "bright_theme": "Ярко",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "Потвърдете приспадането на таксите",
142 "confirm_fee_deduction_content": "Съгласни ли сте да приспадате таксата от продукцията?",
143 "confirm_sending": "Потвърждаване на изпращането",
144 + "confirm_silent_payments_switch_node": "Понастоящем се изисква да превключвате възлите за сканиране на мълчаливи плащания",
145 "confirmations": "потвърждения",
146 "confirmed": "Потвърден баланс",
147 "confirmed_tx": "Потвърдено",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "Нови адреси се генерират всеки път, когато използвате този, но и предишните продължават да работят",
225 "email_address": "Имейл адрес",
226 "enable_replace_by_fee": "Активиране на замяна по забрана",
227 + "enable_silent_payments_scanning": "Активирайте безшумните плащания за сканиране",
228 "enabled": "Активирано",
229 "enter_amount": "Въведете сума",
230 "enter_backup_password": "Въведете парола за възстановяване",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "Ще изпратите средства на \n${recipient_name}",
283 "failed_authentication": "Неуспешно удостоверяване. ${state_error}",
284 "faq": "FAQ",
285 + "features": "Характеристика",
286 "fetching": "Обработване",
287 "fiat_api": "Fiat API",
288 "fiat_balance": "Фиат Баланс",
@@ -531,6 +536,7 @@
536 "save_backup_password_alert": "Запазване на паролата за възстановяване",
537 "save_to_downloads": "Запазване в Изтегляния",
538 "saved_the_trade_id": "Запазих trade ID-то",
539 + "scan_one_block": "Сканирайте един блок",
540 "scan_qr_code": "Сканирайте QR кода, за да получите адреса",
541 "scan_qr_code_to_get_address": "Сканирайте QR кода, за да получите адреса",
542 "scan_qr_on_device": "Сканирайте този QR код на друго устройство",
@@ -641,11 +647,22 @@
647 "sign_up": "Регистрация",
648 "signTransaction": "Подпишете транзакция",
649 "signup_for_card_accept_terms": "Регистрайте се за картата и приемете условията.",
650 + "silent_payments": "Мълчаливи плащания",
651 + "silent_payments_always_scan": "Задайте мълчаливи плащания винаги сканиране",
652 + "silent_payments_disclaimer": "Новите адреси не са нови идентичности. Това е повторна употреба на съществуваща идентичност с различен етикет.",
653 + "silent_payments_display_card": "Показване на безшумни плащания карта",
654 + "silent_payments_scan_from_date": "Сканиране от дата",
655 + "silent_payments_scan_from_date_or_blockheight": "Моля, въведете височината на блока, която искате да започнете да сканирате за входящи безшумни плащания, или вместо това използвайте датата. Можете да изберете дали портфейлът продължава да сканира всеки блок или проверява само определената височина.",
656 + "silent_payments_scan_from_height": "Сканиране от височината на блока",
657 + "silent_payments_scanned_tip": "Сканиран за съвет! (${tip})",
658 + "silent_payments_scanning": "Безшумни плащания за сканиране",
659 + "silent_payments_settings": "Настройки за безшумни плащания",
660 "slidable": "Плъзгащ се",
661 "sort_by": "Сортирай по",
662 "spend_key_private": "Spend key (таен)",
663 "spend_key_public": "Spend key (публичен)",
664 "status": "Статус: ",
665 + "string_default": "По подразбиране",
666 "subaddress_title": "Лист от подадреси",
667 "subaddresses": "Подадреси",
668 "submit_request": "изпращане на заявка",
@@ -670,10 +687,13 @@
687 "sync_status_starting_sync": "ЗАПОЧВАНЕ НА СИНХРОНИЗАЦИЯ",
688 "sync_status_syncronized": "СИНХРОНИЗИРАНО",
689 "sync_status_syncronizing": "СИНХРОНИЗИРАНЕ",
690 + "sync_status_timed_out": "ВРЕМЕТО ИЗТЕЧЕ",
691 + "sync_status_unsupported": "Неподдържан възел",
692 "syncing_wallet_alert_content": "Списъкът ви с баланс и транзакции може да не е пълен, докато в горната част не пише „СИНХРОНИЗИРАН“. Кликнете/докоснете, за да научите повече.",
693 "syncing_wallet_alert_title": "Вашият портфейл се синхронизира",
694 "template": "Шаблон",
695 "template_name": "Име на шаблон",
696 + "testnet_coins_no_value": "Тестовите монети нямат стойност",
697 "third_intro_content": "Yats също живее извън Cake Wallet. Всеки адрес на портфейл може да бъде заменен с Yat!",
698 "third_intro_title": "Yat добре се сработва с други",
699 "thorchain_contract_address_not_supported": "Thorchain не подкрепя изпращането до адрес на договор",
@@ -749,13 +769,16 @@
769 "trusted": "Надежден",
770 "tx_commit_exception_no_dust_on_change": "Сделката се отхвърля с тази сума. С тези монети можете да изпратите ${min} без промяна или ${max}, която връща промяна.",
771 "tx_commit_failed": "Компетацията на транзакцията не успя. Моля, свържете се с поддръжката.",
772 + "tx_invalid_input": "Използвате грешен тип вход за този тип плащане",
773 "tx_no_dust_exception": "Сделката се отхвърля чрез изпращане на сума твърде малка. Моля, опитайте да увеличите сумата.",
774 "tx_not_enough_inputs_exception": "Няма достатъчно налични входове. Моля, изберете повече под контрол на монети",
775 + "tx_rejected_bip68_final": "Сделката има непотвърдени входове и не успя да се замени с такса.",
776 "tx_rejected_dust_change": "Транзакция, отхвърлена от мрежови правила, ниска сума на промяна (прах). Опитайте да изпратите всички или да намалите сумата.",
777 "tx_rejected_dust_output": "Транзакция, отхвърлена от мрежови правила, ниска стойност на изхода (прах). Моля, увеличете сумата.",
778 "tx_rejected_dust_output_send_all": "Транзакция, отхвърлена от мрежови правила, ниска стойност на изхода (прах). Моля, проверете баланса на монетите, избрани под контрол на монети.",
779 "tx_rejected_vout_negative": "Няма достатъчно баланс, за да платите за таксите на тази транзакция. Моля, проверете баланса на монетите под контрол на монетите.",
780 "tx_wrong_balance_exception": "Нямате достатъчно ${currency}, за да изпратите тази сума.",
781 + "tx_wrong_balance_with_amount_exception": "Нямате достатъчно ${currency} За да изпратите общата сума на ${amount}",
782 "tx_zero_fee_exception": "Не може да изпраща транзакция с 0 такса. Опитайте да увеличите скоростта или да проверите връзката си за най -новите оценки.",
783 "unavailable_balance": "Неналично салдо",
784 "unavailable_balance_description": "Неналично салдо: Тази обща сума включва средства, които са заключени в чакащи транзакции и тези, които сте замразили активно в настройките за контрол на монетите. Заключените баланси ще станат достъпни, след като съответните им транзакции бъдат завършени, докато замразените баланси остават недостъпни за транзакции, докато не решите да ги размразите.",
@@ -809,6 +832,7 @@
832 "warning": "Внимание",
833 "welcome": "Добре дошли в",
834 "welcome_to_cakepay": "Добре дошли в Cake Pay!",
835 + "what_is_silent_payments": "Какво са мълчаливи плащания?",
836 "widgets_address": "Адрес",
837 "widgets_or": "или",
838 "widgets_restore_from_blockheight": "Възстановяване от blockheight",
res/values/strings_cs.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "Záloha",
72 "backup_file": "Soubor se zálohou",
73 "backup_password": "Heslo pro zálohy",
74 + "balance": "Zůstatek",
75 "balance_page": "Stránka zůstatku",
76 "bill_amount": "Účtovaná částka",
77 "billing_address_info": "Při dotazu na fakturační adresu, zadejte svou doručovací adresu",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "Tmavé téma bitcoinů",
80 "bitcoin_light_theme": "Světlé téma bitcoinů",
81 "bitcoin_payments_require_1_confirmation": "U plateb Bitcoinem je vyžadováno alespoň 1 potvrzení, což může trvat 20 minut i déle. Děkujeme za vaši trpělivost! Až bude platba potvrzena, budete informováni e-mailem.",
82 + "block_remaining": "1 blok zbývající",
83 "Blocks_remaining": "Zbývá ${status} bloků",
84 "bluetooth": "Bluetooth",
85 "bright_theme": "Jasný",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "Potvrďte odpočet poplatků",
142 "confirm_fee_deduction_content": "Souhlasíte s odečtením poplatku z výstupu?",
143 "confirm_sending": "Potvrdit odeslání",
144 + "confirm_silent_payments_switch_node": "V současné době je nutné přepínat uzly pro skenování tichých plateb",
145 "confirmations": "Potvrzení",
146 "confirmed": "Potvrzený zůstatek",
147 "confirmed_tx": "Potvrzeno",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "Po každém použití je generována nová adresa, ale předchozí adresy také stále fungují",
225 "email_address": "E-mailová adresa",
226 "enable_replace_by_fee": "Povolit výměnu podle poplatku",
227 + "enable_silent_payments_scanning": "Povolte skenování tichých plateb",
228 "enabled": "Povoleno",
229 "enter_amount": "Zadejte částku",
230 "enter_backup_password": "Zde zadejte své heslo pro zálohy",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "Prostředky budete posílat na\n${recipient_name}",
283 "failed_authentication": "Ověřování selhalo. ${state_error}",
284 "faq": "FAQ",
285 + "features": "Funkce",
286 "fetching": "Načítá se",
287 "fiat_api": "Fiat API",
288 "fiat_balance": "Fiat Balance",
@@ -531,6 +536,7 @@
536 "save_backup_password_alert": "Uložit heslo pro zálohy",
537 "save_to_downloads": "Uložit do Stažených souborů",
538 "saved_the_trade_id": "Uložil jsem si ID transakce (trade ID)",
539 + "scan_one_block": "Prohledejte jeden blok",
540 "scan_qr_code": "Naskenujte QR kód pro získání adresy",
541 "scan_qr_code_to_get_address": "Prohledejte QR kód a získejte adresu",
542 "scan_qr_on_device": "Naskenujte tento QR kód na jiném zařízení",
@@ -641,11 +647,22 @@
647 "sign_up": "Registrovat se",
648 "signTransaction": "Podepsat transakci",
649 "signup_for_card_accept_terms": "Zaregistrujte se pro kartu a souhlaste s podmínkami.",
650 + "silent_payments": "Tiché platby",
651 + "silent_payments_always_scan": "Nastavit tiché platby vždy skenování",
652 + "silent_payments_disclaimer": "Nové adresy nejsou nové identity. Je to opětovné použití existující identity s jiným štítkem.",
653 + "silent_payments_display_card": "Zobrazit kartu Silent Payments",
654 + "silent_payments_scan_from_date": "Skenovat od data",
655 + "silent_payments_scan_from_date_or_blockheight": "Zadejte výšku bloku, kterou chcete začít skenovat, zda jsou přicházející tiché platby, nebo místo toho použijte datum. Můžete si vybrat, zda peněženka pokračuje v skenování každého bloku nebo zkontroluje pouze zadanou výšku.",
656 + "silent_payments_scan_from_height": "Skenování z výšky bloku",
657 + "silent_payments_scanned_tip": "Naskenované na tip! (${tip})",
658 + "silent_payments_scanning": "Skenování tichých plateb",
659 + "silent_payments_settings": "Nastavení tichých plateb",
660 "slidable": "Posuvné",
661 "sort_by": "Seřazeno podle",
662 "spend_key_private": "Klíč pro platby (soukromý)",
663 "spend_key_public": "Klíč pro platby (veřejný)",
664 "status": "Status: ",
665 + "string_default": "Výchozí",
666 "subaddress_title": "Seznam subadres",
667 "subaddresses": "Subadresy",
668 "submit_request": "odeslat požadavek",
@@ -670,10 +687,13 @@
687 "sync_status_starting_sync": "SPOUŠTĚNÍ SYNCHRONIZACE",
688 "sync_status_syncronized": "SYNCHRONIZOVÁNO",
689 "sync_status_syncronizing": "SYNCHRONIZUJI",
690 + "sync_status_timed_out": "ČAS VYPRŠEL",
691 + "sync_status_unsupported": "Nepodporovaný uzel",
692 "syncing_wallet_alert_content": "Váš seznam zůstatků a transakcí nemusí být úplný, dokud nebude nahoře uvedeno „SYNCHRONIZOVANÉ“. Kliknutím/klepnutím se dozvíte více.",
693 "syncing_wallet_alert_title": "Vaše peněženka se synchronizuje",
694 "template": "Šablona",
695 "template_name": "Název šablony",
696 + "testnet_coins_no_value": "Mince TestNet nemají žádnou hodnotu",
697 "third_intro_content": "Yat existuje i mimo Cake Wallet. Jakákoliv adresa peněženky na světě může být nahrazena Yatem!",
698 "third_intro_title": "Yat dobře spolupracuje s ostatními",
699 "thorchain_contract_address_not_supported": "Thorchain nepodporuje odeslání na adresu smlouvy",
@@ -749,13 +769,16 @@
769 "trusted": "Důvěřovat",
770 "tx_commit_exception_no_dust_on_change": "Transakce je zamítnuta s touto částkou. S těmito mincemi můžete odeslat ${min} bez změny nebo ${max}, které se vrátí změna.",
771 "tx_commit_failed": "Transakce COMPORT selhala. Kontaktujte prosím podporu.",
772 + "tx_invalid_input": "Pro tento typ platby používáte nesprávný typ vstupu",
773 "tx_no_dust_exception": "Transakce je zamítnuta odesláním příliš malé. Zkuste prosím zvýšit částku.",
774 "tx_not_enough_inputs_exception": "Není k dispozici dostatek vstupů. Vyberte prosím více pod kontrolou mincí",
775 + "tx_rejected_bip68_final": "Transakce má nepotvrzené vstupy a nepodařilo se nahradit poplatkem.",
776 "tx_rejected_dust_change": "Transakce zamítnuta podle síťových pravidel, množství nízké změny (prach). Zkuste odeslat vše nebo snížit částku.",
777 "tx_rejected_dust_output": "Transakce zamítnuta síťovými pravidly, nízkým množstvím výstupu (prach). Zvyšte prosím částku.",
778 "tx_rejected_dust_output_send_all": "Transakce zamítnuta síťovými pravidly, nízkým množstvím výstupu (prach). Zkontrolujte prosím zůstatek mincí vybraných pod kontrolou mincí.",
779 "tx_rejected_vout_negative": "Nedostatek zůstatek na zaplacení poplatků za tuto transakci. Zkontrolujte prosím zůstatek mincí pod kontrolou mincí.",
780 "tx_wrong_balance_exception": "Nemáte dost ${currency} pro odeslání této částky.",
781 + "tx_wrong_balance_with_amount_exception": "Nemáte dost ${currency} na odeslání celkové částky ${amount}",
782 "tx_zero_fee_exception": "Nelze odeslat transakci s 0 poplatkem. Zkuste zvýšit sazbu nebo zkontrolovat připojení pro nejnovější odhady.",
783 "unavailable_balance": "Nedostupný zůstatek",
784 "unavailable_balance_description": "Nedostupný zůstatek: Tento součet zahrnuje prostředky, které jsou uzamčeny v nevyřízených transakcích a ty, které jste aktivně zmrazili v nastavení kontroly mincí. Uzamčené zůstatky budou k dispozici po dokončení příslušných transakcí, zatímco zmrazené zůstatky zůstanou pro transakce nepřístupné, dokud se nerozhodnete je uvolnit.",
@@ -809,6 +832,7 @@
832 "warning": "Varování",
833 "welcome": "Vítejte v",
834 "welcome_to_cakepay": "Vítejte v Cake Pay!",
835 + "what_is_silent_payments": "Co jsou tiché platby?",
836 "widgets_address": "Adresa",
837 "widgets_or": "nebo",
838 "widgets_restore_from_blockheight": "Obnovit z výšky bloku",
res/values/strings_de.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "Sicherung",
72 "backup_file": "Sicherungsdatei",
73 "backup_password": "Passwort sichern",
74 + "balance": "Gleichgewicht",
75 "balance_page": "Balance-Seite",
76 "bill_amount": "Rechnungsbetrag",
77 "billing_address_info": "Wenn Sie nach einer Rechnungsadresse gefragt werden, geben Sie bitte Ihre Lieferadresse an",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "Dunkles Bitcoin-Thema",
80 "bitcoin_light_theme": "Bitcoin Light-Thema",
81 "bitcoin_payments_require_1_confirmation": "Bitcoin-Zahlungen erfordern 1 Bestätigung, was 20 Minuten oder länger dauern kann. Danke für Ihre Geduld! Sie erhalten eine E-Mail, wenn die Zahlung bestätigt ist.",
82 + "block_remaining": "1 Block verbleibend",
83 "Blocks_remaining": "${status} verbleibende Blöcke",
84 "bluetooth": "Bluetooth",
85 "bright_theme": "Strahlend hell",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "Gebührenabzug bestätigen",
142 "confirm_fee_deduction_content": "Stimmen Sie zu, die Gebühr von der Ausgabe abzuziehen?",
143 "confirm_sending": "Senden bestätigen",
144 + "confirm_silent_payments_switch_node": "Derzeit ist es erforderlich, Knoten zu wechseln, um stille Zahlungen zu scannen",
145 "confirmations": "Bestätigungen",
146 "confirmed": "Bestätigter Saldo",
147 "confirmed_tx": "Bestätigt",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "Wir generieren jedes Mal neue Adressen, wenn Sie eine verwenden, aber vorherige Adressen funktionieren weiterhin",
225 "email_address": "E-Mail-Adresse",
226 "enable_replace_by_fee": "Aktivieren Sie Ersatz für Fee",
227 + "enable_silent_payments_scanning": "Aktivieren Sie stille Zahlungen Scannen",
228 "enabled": "Ermöglicht",
229 "enter_amount": "Betrag eingeben",
230 "enter_backup_password": "Sicherungskennwort hier eingeben",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "Sie senden Geld an\n${recipient_name}",
283 "failed_authentication": "Authentifizierung fehlgeschlagen. ${state_error}",
284 "faq": "Häufig gestellte Fragen",
285 + "features": "Merkmale",
286 "fetching": "Frage ab",
287 "fiat_api": "Fiat API",
288 "fiat_balance": "Fiat Balance",
@@ -532,6 +537,7 @@
537 "save_backup_password_alert": "Sicherungskennwort speichern",
538 "save_to_downloads": "Unter „Downloads“ speichern",
539 "saved_the_trade_id": "Ich habe die Handels-ID gespeichert",
540 + "scan_one_block": "Einen Block scannen",
541 "scan_qr_code": "QR-Code scannen",
542 "scan_qr_code_to_get_address": "Scannen Sie den QR-Code, um die Adresse zu erhalten",
543 "scan_qr_on_device": "Scannen Sie diesen QR-Code auf einem anderen Gerät",
@@ -642,11 +648,22 @@
648 "sign_up": "Anmelden",
649 "signTransaction": "Transaktion unterzeichnen",
650 "signup_for_card_accept_terms": "Melden Sie sich für die Karte an und akzeptieren Sie die Bedingungen.",
651 + "silent_payments": "Stille Zahlungen",
652 + "silent_payments_always_scan": "Setzen Sie stille Zahlungen immer scannen",
653 + "silent_payments_disclaimer": "Neue Adressen sind keine neuen Identitäten. Es ist eine Wiederverwendung einer bestehenden Identität mit einem anderen Etikett.",
654 + "silent_payments_display_card": "Zeigen Sie stille Zahlungskarte",
655 + "silent_payments_scan_from_date": "Scan ab Datum",
656 + "silent_payments_scan_from_date_or_blockheight": "Bitte geben Sie die Blockhöhe ein, die Sie für eingehende stille Zahlungen scannen möchten, oder verwenden Sie stattdessen das Datum. Sie können wählen, ob die Brieftasche jeden Block scannt oder nur die angegebene Höhe überprüft.",
657 + "silent_payments_scan_from_height": "Scan aus der Blockhöhe scannen",
658 + "silent_payments_scanned_tip": "Gescannt zum Trinkgeld! (${tip})",
659 + "silent_payments_scanning": "Stille Zahlungen scannen",
660 + "silent_payments_settings": "Einstellungen für stille Zahlungen",
661 "slidable": "Verschiebbar",
662 "sort_by": "Sortiere nach",
663 "spend_key_private": "Spend Key (geheim)",
664 "spend_key_public": "Spend Key (öffentlich)",
665 "status": "Status: ",
666 + "string_default": "Standard",
667 "subaddress_title": "Unteradressenliste",
668 "subaddresses": "Unteradressen",
669 "submit_request": "Eine Anfrage stellen",
@@ -671,10 +688,13 @@
688 "sync_status_starting_sync": "STARTE SYNCHRONISIERUNG",
689 "sync_status_syncronized": "SYNCHRONISIERT",
690 "sync_status_syncronizing": "SYNCHRONISIERE",
691 + "sync_status_timed_out": "Zeitlich abgestimmt",
692 + "sync_status_unsupported": "Nicht unterstützter Knoten",
693 "syncing_wallet_alert_content": "Ihr Kontostand und Ihre Transaktionsliste sind möglicherweise erst vollständig, wenn oben „SYNCHRONISIERT“ steht. Klicken/tippen Sie, um mehr zu erfahren.",
694 "syncing_wallet_alert_title": "Ihr Wallet wird synchronisiert",
695 "template": "Vorlage",
696 "template_name": "Vorlagenname",
697 + "testnet_coins_no_value": "Testnet -Münzen haben keinen Wert",
698 "third_intro_content": "Yats leben auch außerhalb von Cake Wallet. Jede Wallet-Adresse auf der Welt kann durch ein Yat ersetzt werden!",
699 "third_intro_title": "Yat spielt gut mit anderen",
700 "thorchain_contract_address_not_supported": "Thorchain unterstützt das Senden an eine Vertragsadresse nicht",
@@ -750,13 +770,16 @@
770 "trusted": "Vertrauenswürdige",
771 "tx_commit_exception_no_dust_on_change": "Die Transaktion wird diesen Betrag abgelehnt. Mit diesen Münzen können Sie ${min} ohne Veränderung oder ${max} senden, die Änderungen zurückgeben.",
772 "tx_commit_failed": "Transaktionsausschüsse ist fehlgeschlagen. Bitte wenden Sie sich an Support.",
773 + "tx_invalid_input": "Sie verwenden den falschen Eingangstyp für diese Art von Zahlung",
774 "tx_no_dust_exception": "Die Transaktion wird abgelehnt, indem eine Menge zu klein gesendet wird. Bitte versuchen Sie, die Menge zu erhöhen.",
775 "tx_not_enough_inputs_exception": "Nicht genügend Eingänge verfügbar. Bitte wählen Sie mehr unter Münzkontrolle aus",
776 + "tx_rejected_bip68_final": "Die Transaktion hat unbestätigte Inputs und konnte nicht durch Gebühr ersetzt werden.",
777 "tx_rejected_dust_change": "Transaktion abgelehnt durch Netzwerkregeln, niedriger Änderungsbetrag (Staub). Versuchen Sie, alle zu senden oder die Menge zu reduzieren.",
778 "tx_rejected_dust_output": "Transaktion durch Netzwerkregeln, niedriger Ausgangsmenge (Staub) abgelehnt. Bitte erhöhen Sie den Betrag.",
779 "tx_rejected_dust_output_send_all": "Transaktion durch Netzwerkregeln, niedriger Ausgangsmenge (Staub) abgelehnt. Bitte überprüfen Sie den Gleichgewicht der unter Münzkontrolle ausgewählten Münzen.",
780 "tx_rejected_vout_negative": "Nicht genug Guthaben, um die Gebühren dieser Transaktion zu bezahlen. Bitte überprüfen Sie den Restbetrag der Münzen unter Münzkontrolle.",
781 "tx_wrong_balance_exception": "Sie haben nicht genug ${currency}, um diesen Betrag zu senden.",
782 + "tx_wrong_balance_with_amount_exception": "Sie haben nicht genug ${currency}, um die Gesamtmenge von ${amount} zu senden",
783 "tx_zero_fee_exception": "Transaktion kann nicht mit 0 Gebühren gesendet werden. Versuchen Sie, die Rate zu erhöhen oder Ihre Verbindung auf die neuesten Schätzungen zu überprüfen.",
784 "unavailable_balance": "Nicht verfügbares Guthaben",
785 "unavailable_balance_description": "Nicht verfügbares Guthaben: Diese Summe umfasst Gelder, die in ausstehenden Transaktionen gesperrt sind, und solche, die Sie in Ihren Münzkontrolleinstellungen aktiv eingefroren haben. Gesperrte Guthaben werden verfügbar, sobald die entsprechenden Transaktionen abgeschlossen sind, während eingefrorene Guthaben für Transaktionen nicht zugänglich bleiben, bis Sie sich dazu entschließen, sie wieder freizugeben.",
@@ -812,6 +835,7 @@
835 "warning": "Warnung",
836 "welcome": "Willkommen bei",
837 "welcome_to_cakepay": "Willkommen bei Cake Pay!",
838 + "what_is_silent_payments": "Was sind stille Zahlungen?",
839 "widgets_address": "Adresse",
840 "widgets_or": "oder",
841 "widgets_restore_from_blockheight": "Ab Blockhöhe wiederherstellen",
res/values/strings_en.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "Backup",
72 "backup_file": "Backup file",
73 "backup_password": "Backup password",
74 + "balance": "Balance",
75 "balance_page": "Balance Page",
76 "bill_amount": "Bill Amount",
77 "billing_address_info": "If asked for a billing address, provide your shipping address",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "Bitcoin Dark Theme",
80 "bitcoin_light_theme": "Bitcoin Light Theme",
81 "bitcoin_payments_require_1_confirmation": "Bitcoin payments require 1 confirmation, which can take 20 minutes or longer. Thanks for your patience! You will be emailed when the payment is confirmed.",
82 + "block_remaining": "1 Block Remaining",
83 "Blocks_remaining": "${status} Blocks Remaining",
84 "bluetooth": "Bluetooth",
85 "bright_theme": "Bright",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "Confirm Fee Deduction",
142 "confirm_fee_deduction_content": "Do you agree to deduct the fee from the output?",
143 "confirm_sending": "Confirm sending",
144 + "confirm_silent_payments_switch_node": "Currently it is required to switch nodes to scan silent payments",
145 "confirmations": "Confirmations",
146 "confirmed": "Confirmed Balance",
147 "confirmed_tx": "Confirmed",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "We generate new addresses each time you use one, but previous addresses continue to work",
225 "email_address": "Email Address",
226 "enable_replace_by_fee": "Enable Replace-By-Fee",
227 + "enable_silent_payments_scanning": "Enable silent payments scanning",
228 "enabled": "Enabled",
229 "enter_amount": "Enter Amount",
230 "enter_backup_password": "Enter backup password here",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "You will be sending funds to\n${recipient_name}",
283 "failed_authentication": "Failed authentication. ${state_error}",
284 "faq": "FAQ",
285 + "features": "Features",
286 "fetching": "Fetching",
287 "fiat_api": "Fiat API",
288 "fiat_balance": "Fiat Balance",
@@ -531,6 +536,7 @@
536 "save_backup_password_alert": "Save backup password",
537 "save_to_downloads": "Save to Downloads",
538 "saved_the_trade_id": "I've saved the trade ID",
539 + "scan_one_block": "Scan one block",
540 "scan_qr_code": "Scan QR code",
541 "scan_qr_code_to_get_address": "Scan the QR code to get the address",
542 "scan_qr_on_device": "Scan this QR code on another device",
@@ -641,11 +647,22 @@
647 "sign_up": "Sign Up",
648 "signTransaction": "Sign Transaction",
649 "signup_for_card_accept_terms": "Sign up for the card and accept the terms.",
650 + "silent_payments": "Silent Payments",
651 + "silent_payments_always_scan": "Set Silent Payments always scanning",
652 + "silent_payments_disclaimer": "New addresses are not new identities. It is a re-use of an existing identity with a different label.",
653 + "silent_payments_display_card": "Show Silent Payments card",
654 + "silent_payments_scan_from_date": "Scan from date",
655 + "silent_payments_scan_from_date_or_blockheight": "Please enter the block height you want to start scanning for incoming silent payments, or, use the date instead. You can choose if the wallet continues scanning every block, or checks only the specified height.",
656 + "silent_payments_scan_from_height": "Scan from block height",
657 + "silent_payments_scanned_tip": "SCANNED TO TIP! (${tip})",
658 + "silent_payments_scanning": "Silent Payments Scanning",
659 + "silent_payments_settings": "Silent Payments settings",
660 "slidable": "Slidable",
661 "sort_by": "Sort by",
662 "spend_key_private": "Spend key (private)",
663 "spend_key_public": "Spend key (public)",
664 "status": "Status: ",
665 + "string_default": "Default",
666 "subaddress_title": "Subaddress list",
667 "subaddresses": "Subaddresses",
668 "submit_request": "submit a request",
@@ -670,10 +687,13 @@
687 "sync_status_starting_sync": "STARTING SYNC",
688 "sync_status_syncronized": "SYNCHRONIZED",
689 "sync_status_syncronizing": "SYNCHRONIZING",
690 + "sync_status_timed_out": "TIMED OUT",
691 + "sync_status_unsupported": "UNSUPPORTED NODE",
692 "syncing_wallet_alert_content": "Your balance and transaction list may not be complete until it says “SYNCHRONIZED” at the top. Click/tap to learn more.",
693 "syncing_wallet_alert_title": "Your wallet is syncing",
694 "template": "Template",
695 "template_name": "Template Name",
696 + "testnet_coins_no_value": "Testnet coins have no value",
697 "third_intro_content": "Yats live outside of Cake Wallet, too. Any wallet address on earth can be replaced with a Yat!",
698 "third_intro_title": "Yat plays nicely with others",
699 "thorchain_contract_address_not_supported": "THORChain does not support sending to a contract address",
@@ -749,13 +769,16 @@
769 "trusted": "Trusted",
770 "tx_commit_exception_no_dust_on_change": "The transaction is rejected with this amount. With these coins you can send ${min} without change or ${max} that returns change.",
771 "tx_commit_failed": "Transaction commit failed. Please contact support.",
772 + "tx_invalid_input": "You are using the wrong input type for this type of payment",
773 "tx_no_dust_exception": "The transaction is rejected by sending an amount too small. Please try increasing the amount.",
774 "tx_not_enough_inputs_exception": "Not enough inputs available. Please select more under Coin Control",
775 + "tx_rejected_bip68_final": "Transaction has unconfirmed inputs and failed to replace by fee.",
776 "tx_rejected_dust_change": "Transaction rejected by network rules, low change amount (dust). Try sending ALL or reducing the amount.",
777 "tx_rejected_dust_output": "Transaction rejected by network rules, low output amount (dust). Please increase the amount.",
778 "tx_rejected_dust_output_send_all": "Transaction rejected by network rules, low output amount (dust). Please check the balance of coins selected under Coin Control.",
779 "tx_rejected_vout_negative": "Not enough balance to pay for this transaction's fees. Please check the balance of coins under Coin Control.",
780 "tx_wrong_balance_exception": "You do not have enough ${currency} to send this amount.",
781 + "tx_wrong_balance_with_amount_exception": "You do not have enough ${currency} to send the total amount of ${amount}",
782 "tx_zero_fee_exception": "Cannot send transaction with 0 fee. Try increasing the rate or checking your connection for latest estimates.",
783 "unavailable_balance": "Unavailable balance",
784 "unavailable_balance_description": "Unavailable Balance: This total includes funds that are locked in pending transactions and those you have actively frozen in your coin control settings. Locked balances will become available once their respective transactions are completed, while frozen balances remain inaccessible for transactions until you decide to unfreeze them.",
@@ -809,6 +832,7 @@
832 "warning": "Warning",
833 "welcome": "Welcome to",
834 "welcome_to_cakepay": "Welcome to Cake Pay!",
835 + "what_is_silent_payments": "What is silent payments?",
836 "widgets_address": "Address",
837 "widgets_or": "or",
838 "widgets_restore_from_blockheight": "Restore from blockheight",
res/values/strings_es.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "Apoyo",
72 "backup_file": "Archivo de respaldo",
73 "backup_password": "Contraseña de respaldo",
74 + "balance": "Balance",
75 "balance_page": "Página de saldo",
76 "bill_amount": "Importe de la factura",
77 "billing_address_info": "Si se le solicita una dirección de facturación, proporcione su dirección de envío",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "Tema oscuro de Bitcoin",
80 "bitcoin_light_theme": "Tema de la luz de Bitcoin",
81 "bitcoin_payments_require_1_confirmation": "Los pagos de Bitcoin requieren 1 confirmación, que puede demorar 20 minutos o más. ¡Gracias por su paciencia! Se le enviará un correo electrónico cuando se confirme el pago.",
82 + "block_remaining": "1 bloqueo restante",
83 "Blocks_remaining": "${status} Bloques restantes",
84 "bluetooth": "Bluetooth",
85 "bright_theme": "Brillante",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "Confirmar la deducción de la tarifa",
142 "confirm_fee_deduction_content": "¿Acepta deducir la tarifa de la producción?",
143 "confirm_sending": "Confirmar envío",
144 + "confirm_silent_payments_switch_node": "Actualmente se requiere cambiar los nodos para escanear pagos silenciosos",
145 "confirmations": "Confirmaciones",
146 "confirmed": "Saldo confirmado",
147 "confirmed_tx": "Confirmado",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "Generamos nuevas direcciones cada vez que usa una, pero las direcciones anteriores siguen funcionando",
225 "email_address": "Dirección de correo electrónico",
226 "enable_replace_by_fee": "Habilitar reemplazar por tarea",
227 + "enable_silent_payments_scanning": "Habilitar escaneo de pagos silenciosos",
228 "enabled": "Activado",
229 "enter_amount": "Ingrese la cantidad",
230 "enter_backup_password": "Ingrese la contraseña de respaldo aquí",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "Enviará fondos a\n${recipient_name}",
283 "failed_authentication": "Autenticación fallida. ${state_error}",
284 "faq": "FAQ",
285 + "features": "Características",
286 "fetching": "Cargando",
287 "fiat_api": "Fiat API",
288 "fiat_balance": "Equilibrio Fiat",
@@ -532,6 +537,7 @@
537 "save_backup_password_alert": "Guardar contraseña de respaldo",
538 "save_to_downloads": "Guardar en Descargas",
539 "saved_the_trade_id": "He salvado comercial ID",
540 + "scan_one_block": "Escanear un bloque",
541 "scan_qr_code": "Escanear código QR",
542 "scan_qr_code_to_get_address": "Escanee el código QR para obtener la dirección",
543 "scan_qr_on_device": "Escanea este código QR en otro dispositivo",
@@ -642,11 +648,22 @@
648 "sign_up": "Registrarse",
649 "signTransaction": "Firmar transacción",
650 "signup_for_card_accept_terms": "Regístrese para obtener la tarjeta y acepte los términos.",
651 + "silent_payments": "Pagos silenciosos",
652 + "silent_payments_always_scan": "Establecer pagos silenciosos siempre escaneando",
653 + "silent_payments_disclaimer": "Las nuevas direcciones no son nuevas identidades. Es una reutilización de una identidad existente con una etiqueta diferente.",
654 + "silent_payments_display_card": "Mostrar tarjeta de pagos silenciosos",
655 + "silent_payments_scan_from_date": "Escanear desde la fecha",
656 + "silent_payments_scan_from_date_or_blockheight": "Ingrese la altura del bloque que desea comenzar a escanear para pagos silenciosos entrantes, o use la fecha en su lugar. Puede elegir si la billetera continúa escaneando cada bloque, o verifica solo la altura especificada.",
657 + "silent_payments_scan_from_height": "Escanear desde la altura del bloque",
658 + "silent_payments_scanned_tip": "Escaneado hasta la punta! (${tip})",
659 + "silent_payments_scanning": "Escaneo de pagos silenciosos",
660 + "silent_payments_settings": "Configuración de pagos silenciosos",
661 "slidable": "deslizable",
662 "sort_by": "Ordenar por",
663 "spend_key_private": "Spend clave (privado)",
664 "spend_key_public": "Spend clave (público)",
665 "status": "Estado: ",
666 + "string_default": "Por defecto",
667 "subaddress_title": "Lista de subdirecciones",
668 "subaddresses": "Subdirecciones",
669 "submit_request": "presentar una solicitud",
@@ -671,10 +688,13 @@
688 "sync_status_starting_sync": "EMPEZANDO A SINCRONIZAR",
689 "sync_status_syncronized": "SINCRONIZADO",
690 "sync_status_syncronizing": "SINCRONIZANDO",
691 + "sync_status_timed_out": "CADUCADO",
692 + "sync_status_unsupported": "Nodo no compatible",
693 "syncing_wallet_alert_content": "Es posible que su lista de saldo y transacciones no esté completa hasta que diga \"SINCRONIZADO\" en la parte superior. Haga clic/toque para obtener más información.",
694 "syncing_wallet_alert_title": "Tu billetera se está sincronizando",
695 "template": "Plantilla",
696 "template_name": "Nombre de la plantilla",
697 + "testnet_coins_no_value": "Las monedas de prueba no tienen valor",
698 "third_intro_content": "Los Yats también viven fuera de Cake Wallet. Cualquier dirección de billetera en la tierra se puede reemplazar con un Yat!",
699 "third_intro_title": "Yat juega muy bien con otras",
700 "thorchain_contract_address_not_supported": "Thorchain no admite enviar a una dirección de contrato",
@@ -750,13 +770,16 @@
770 "trusted": "de confianza",
771 "tx_commit_exception_no_dust_on_change": "La transacción se rechaza con esta cantidad. Con estas monedas puede enviar ${min} sin cambios o ${max} que devuelve el cambio.",
772 "tx_commit_failed": "La confirmación de transacción falló. Póngase en contacto con el soporte.",
773 + "tx_invalid_input": "Está utilizando el tipo de entrada incorrecto para este tipo de pago",
774 "tx_no_dust_exception": "La transacción se rechaza enviando una cantidad demasiado pequeña. Intente aumentar la cantidad.",
775 "tx_not_enough_inputs_exception": "No hay suficientes entradas disponibles. Seleccione más bajo control de monedas",
776 + "tx_rejected_bip68_final": "La transacción tiene entradas no confirmadas y no ha podido reemplazar por tarifa.",
777 "tx_rejected_dust_change": "Transacción rechazada por reglas de red, bajo cambio de cambio (polvo). Intente enviar todo o reducir la cantidad.",
778 "tx_rejected_dust_output": "Transacción rechazada por reglas de red, baja cantidad de salida (polvo). Aumente la cantidad.",
779 "tx_rejected_dust_output_send_all": "Transacción rechazada por reglas de red, baja cantidad de salida (polvo). Verifique el saldo de monedas seleccionadas bajo control de monedas.",
780 "tx_rejected_vout_negative": "No es suficiente saldo para pagar las tarifas de esta transacción. Verifique el saldo de monedas bajo control de monedas.",
781 "tx_wrong_balance_exception": "No tiene suficiente ${currency} para enviar esta cantidad.",
782 + "tx_wrong_balance_with_amount_exception": "No tiene suficiente ${currency} para enviar la cantidad total de ${amount}",
783 "tx_zero_fee_exception": "No se puede enviar transacciones con 0 tarifa. Intente aumentar la tasa o verificar su conexión para las últimas estimaciones.",
784 "unavailable_balance": "Saldo no disponible",
785 "unavailable_balance_description": "Saldo no disponible: este total incluye fondos que están bloqueados en transacciones pendientes y aquellos que usted ha congelado activamente en su configuración de control de monedas. Los saldos bloqueados estarán disponibles una vez que se completen sus respectivas transacciones, mientras que los saldos congelados permanecerán inaccesibles para las transacciones hasta que usted decida descongelarlos.",
@@ -810,6 +833,7 @@
833 "warning": "Advertencia",
834 "welcome": "Bienvenido",
835 "welcome_to_cakepay": "¡Bienvenido a Cake Pay!",
836 + "what_is_silent_payments": "¿Qué son los pagos silenciosos?",
837 "widgets_address": "Dirección",
838 "widgets_or": "o",
839 "widgets_restore_from_blockheight": "Restaurar desde blockheight",
res/values/strings_fr.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "Sauvegarde",
72 "backup_file": "Fichier de sauvegarde",
73 "backup_password": "Mot de passe de sauvegarde",
74 + "balance": "Équilibre",
75 "balance_page": "Page Solde",
76 "bill_amount": "Montant de la facture",
77 "billing_address_info": "Si une adresse de facturation vous est demandée, indiquez votre adresse de livraison",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "Thème sombre Bitcoin",
80 "bitcoin_light_theme": "Thème léger Bitcoin",
81 "bitcoin_payments_require_1_confirmation": "Les paiements Bitcoin nécessitent 1 confirmation, ce qui peut prendre 20 minutes ou plus. Merci pour votre patience ! Vous serez averti par e-mail lorsque le paiement sera confirmé.",
82 + "block_remaining": "1 bloc restant",
83 "Blocks_remaining": "Blocs Restants : ${status}",
84 "bluetooth": "Bluetooth",
85 "bright_theme": "Vif",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "Confirmer la déduction des frais",
142 "confirm_fee_deduction_content": "Acceptez-vous de déduire les frais de la production?",
143 "confirm_sending": "Confirmer l'envoi",
144 + "confirm_silent_payments_switch_node": "Actuellement, il est nécessaire de changer de nœuds pour scanner les paiements silencieux",
145 "confirmations": "Confirmations",
146 "confirmed": "Solde confirmé",
147 "confirmed_tx": "Confirmé",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "Nous générons de nouvelles adresses à chaque fois que vous en utilisez une, mais les adresses précédentes continuent à fonctionner",
225 "email_address": "Adresse e-mail",
226 "enable_replace_by_fee": "Activer Remplace-by-Fee",
227 + "enable_silent_payments_scanning": "Activer la numérisation des paiements silencieux",
228 "enabled": "Activé",
229 "enter_amount": "Entrez le montant",
230 "enter_backup_password": "Entrez le mot de passe de sauvegarde ici",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "Vous allez envoyer des fonds à\n${recipient_name}",
283 "failed_authentication": "Échec d'authentification. ${state_error}",
284 "faq": "FAQ",
285 + "features": "Caractéristiques",
286 "fetching": "Récupération",
287 "fiat_api": "Fiat API",
288 "fiat_balance": "Solde fiat",
@@ -531,6 +536,7 @@
536 "save_backup_password_alert": "Enregistrer le mot de passe de sauvegarde",
537 "save_to_downloads": "Enregistrer dans les téléchargements",
538 "saved_the_trade_id": "J'ai sauvegardé l'ID d'échange",
539 + "scan_one_block": "Scanner un bloc",
540 "scan_qr_code": "Scannez le QR code",
541 "scan_qr_code_to_get_address": "Scannez le QR code pour obtenir l'adresse",
542 "scan_qr_on_device": "Scannez ce code QR sur un autre appareil",
@@ -641,11 +647,22 @@
647 "sign_up": "S'inscrire",
648 "signTransaction": "Signer une transaction",
649 "signup_for_card_accept_terms": "Inscrivez-vous pour la carte et acceptez les conditions.",
650 + "silent_payments": "Paiements silencieux",
651 + "silent_payments_always_scan": "Définir les paiements silencieux toujours à la scanne",
652 + "silent_payments_disclaimer": "Les nouvelles adresses ne sont pas de nouvelles identités. Il s'agit d'une réutilisation d'une identité existante avec une étiquette différente.",
653 + "silent_payments_display_card": "Afficher la carte de paiement silencieuse",
654 + "silent_payments_scan_from_date": "Analyser à partir de la date",
655 + "silent_payments_scan_from_date_or_blockheight": "Veuillez saisir la hauteur du bloc que vous souhaitez commencer à scanner pour les paiements silencieux entrants, ou utilisez la date à la place. Vous pouvez choisir si le portefeuille continue de numériser chaque bloc ou ne vérifie que la hauteur spécifiée.",
656 + "silent_payments_scan_from_height": "Scan à partir de la hauteur du bloc",
657 + "silent_payments_scanned_tip": "Scanné à la pointe! (${tip})",
658 + "silent_payments_scanning": "Payments silencieux SCANNING",
659 + "silent_payments_settings": "Paramètres de paiement silencieux",
660 "slidable": "Glissable",
661 "sort_by": "Trier par",
662 "spend_key_private": "Clef de dépense (spend key) (privée)",
663 "spend_key_public": "Clef de dépense (spend key) (publique)",
664 "status": "Statut : ",
665 + "string_default": "Défaut",
666 "subaddress_title": "Liste des sous-adresses",
667 "subaddresses": "Sous-adresses",
668 "submit_request": "soumettre une requête",
@@ -670,10 +687,13 @@
687 "sync_status_starting_sync": "DÉBUT DE SYNCHRO",
688 "sync_status_syncronized": "SYNCHRONISÉ",
689 "sync_status_syncronizing": "SYNCHRONISATION EN COURS",
690 + "sync_status_timed_out": "FIN DU TEMPS",
691 + "sync_status_unsupported": "Nœud non pris en charge",
692 "syncing_wallet_alert_content": "Votre solde et votre liste de transactions peuvent ne pas être à jour tant que la mention « SYNCHRONISÉ » n'apparaît en haut de l'écran. Cliquez/appuyez pour en savoir plus.",
693 "syncing_wallet_alert_title": "Votre portefeuille (wallet) est en cours de synchronisation",
694 "template": "Modèle",
695 "template_name": "Nom du modèle",
696 + "testnet_coins_no_value": "Les pièces TestNet n'ont aucune valeur",
697 "third_intro_content": "Les Yats existent aussi en dehors de Cake Wallet. Toute adresse sur terre peut être remplacée par un Yat !",
698 "third_intro_title": "Yat est universel",
699 "thorchain_contract_address_not_supported": "Thorchain ne prend pas en charge l'envoi à une adresse de contrat",
@@ -749,13 +769,16 @@
769 "trusted": "de confiance",
770 "tx_commit_exception_no_dust_on_change": "La transaction est rejetée avec ce montant. Avec ces pièces, vous pouvez envoyer ${min} sans changement ou ${max} qui renvoie le changement.",
771 "tx_commit_failed": "La validation de la transaction a échoué. Veuillez contacter l'assistance.",
772 + "tx_invalid_input": "Vous utilisez le mauvais type d'entrée pour ce type de paiement",
773 "tx_no_dust_exception": "La transaction est rejetée en envoyant un montant trop faible. Veuillez essayer d'augmenter le montant.",
774 "tx_not_enough_inputs_exception": "Pas assez d'entrées disponibles. Veuillez sélectionner plus sous Control Control",
775 + "tx_rejected_bip68_final": "La transaction a des entrées non confirmées et n'a pas réussi à remplacer par les frais.",
776 "tx_rejected_dust_change": "Transaction rejetée par les règles du réseau, montant de faible variation (poussière). Essayez d'envoyer tout ou de réduire le montant.",
777 "tx_rejected_dust_output": "Transaction rejetée par les règles du réseau, faible quantité de sortie (poussière). Veuillez augmenter le montant.",
778 "tx_rejected_dust_output_send_all": "Transaction rejetée par les règles du réseau, faible quantité de sortie (poussière). Veuillez vérifier le solde des pièces sélectionnées sous le contrôle des pièces de monnaie.",
779 "tx_rejected_vout_negative": "Pas assez de solde pour payer les frais de cette transaction. Veuillez vérifier le solde des pièces sous le contrôle des pièces.",
780 "tx_wrong_balance_exception": "Vous n'avez pas assez ${currency} pour envoyer ce montant.",
781 + "tx_wrong_balance_with_amount_exception": "Vous n'avez pas assez ${currency} pour envoyer le montant total de ${amount}",
782 "tx_zero_fee_exception": "Impossible d'envoyer une transaction avec 0 frais. Essayez d'augmenter le taux ou de vérifier votre connexion pour les dernières estimations.",
783 "unavailable_balance": "Solde indisponible",
784 "unavailable_balance_description": "Solde indisponible : ce total comprend les fonds bloqués dans les transactions en attente et ceux que vous avez activement gelés dans vos paramètres de contrôle des pièces. Les soldes bloqués deviendront disponibles une fois leurs transactions respectives terminées, tandis que les soldes gelés resteront inaccessibles aux transactions jusqu'à ce que vous décidiez de les débloquer.",
@@ -809,6 +832,7 @@
832 "warning": "Avertissement",
833 "welcome": "Bienvenue sur",
834 "welcome_to_cakepay": "Bienvenue sur Cake Pay !",
835 + "what_is_silent_payments": "Qu'est-ce que les paiements silencieux?",
836 "widgets_address": "Adresse",
837 "widgets_or": "ou",
838 "widgets_restore_from_blockheight": "Restaurer depuis une hauteur de bloc",
res/values/strings_ha.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "Ajiyayyen",
72 "backup_file": "Ajiyayyen fayil",
73 "backup_password": "Ajiyayyen kalmar sirri",
74 + "balance": "Ma'auni",
75 "balance_page": "Ma'auni Page",
76 "bill_amount": "Adadin Bill",
77 "billing_address_info": "Idan an nemi adireshin biyan kuɗi, samar da adireshin jigilar kaya",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "Bitcoin Dark Jigo",
80 "bitcoin_light_theme": "Jigon Hasken Bitcoin",
81 "bitcoin_payments_require_1_confirmation": "Akwatin Bitcoin na buɗe 1 sambumbu, da yake za ta samu mintuna 20 ko yawa. Ina kira ga sabuwar lafiya! Zaka sanarwa ta email lokacin da aka samu akwatin samun lambar waya.",
82 + "block_remaining": "1 toshe ragowar",
83 "Blocks_remaining": "${status} Katanga ya rage",
84 "bluetooth": "Bluetooth",
85 "bright_theme": "Mai haske",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "Tabbatar da cire kudade",
142 "confirm_fee_deduction_content": "Shin kun yarda ku cire kuɗin daga fitarwa?",
143 "confirm_sending": "Tabbatar da aikawa",
144 + "confirm_silent_payments_switch_node": "A halin yanzu ana buƙatar sauya nodes don bincika biyan siliki",
145 "confirmations": "Tabbatar",
146 "confirmed": "An tabbatar",
147 "confirmed_tx": "Tabbatar",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "Muna samar da sababbin adireshi duk lokacin da kuka yi amfani da ɗaya, amma adiresoshin da suka gabata suna ci gaba da aiki",
225 "email_address": "Adireshin i-mel",
226 "enable_replace_by_fee": "Ba da damar maye gurbin-by-kudin",
227 + "enable_silent_payments_scanning": "Kunna biya biya",
228 "enabled": "An kunna",
229 "enter_amount": "Shigar da Adadi",
230 "enter_backup_password": "Shigar da kalmar wucewa ta madadin nan",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "Za ku aika da kudade zuwa\n${recipient_name}",
283 "failed_authentication": "Binne wajen shiga. ${state_error}",
284 "faq": "FAQ",
285 + "features": "Fasas",
286 "fetching": "Daukewa",
287 "fiat_api": "API ɗin Fiat",
288 "fiat_balance": "Fiat Balance",
@@ -533,6 +538,7 @@
538 "save_backup_password_alert": "Ajiye kalmar sirri ta ajiya",
539 "save_to_downloads": "Ajiye zuwa Zazzagewa",
540 "saved_the_trade_id": "Na ajiye ID na ciniki",
541 + "scan_one_block": "Duba toshe daya",
542 "scan_qr_code": "Gani QR kodin",
543 "scan_qr_code_to_get_address": "Duba lambar QR don samun adireshin",
544 "scan_qr_on_device": "Duba wannan lambar QR akan wata na'ura",
@@ -643,11 +649,22 @@
649 "sign_up": "Shiga",
650 "signTransaction": "Sa hannu Ma'amala",
651 "signup_for_card_accept_terms": "Yi rajista don katin kuma karɓi sharuɗɗan.",
652 + "silent_payments": "Biya silent",
653 + "silent_payments_always_scan": "Saita biya na shiru koyaushe",
654 + "silent_payments_disclaimer": "Sabbin adiresoshin ba sabon tsari bane. Wannan shine sake amfani da asalin asalin tare da wata alama daban.",
655 + "silent_payments_display_card": "Nuna katin silent",
656 + "silent_payments_scan_from_date": "Scan daga kwanan wata",
657 + "silent_payments_scan_from_date_or_blockheight": "Da fatan za a shigar da toshe wurin da kake son fara bincika don biyan silins mai shigowa, ko, yi amfani da kwanan wata. Zaka iya zabar idan walat ɗin ya ci gaba da bincika kowane toshe, ko duba tsinkaye da aka ƙayyade.",
658 + "silent_payments_scan_from_height": "Scan daga tsayin daka",
659 + "silent_payments_scanned_tip": "Bincika don tip! (${tip})",
660 + "silent_payments_scanning": "Silent biya scanning",
661 + "silent_payments_settings": "Saitunan Silent",
662 "slidable": "Mai iya zamewa",
663 "sort_by": "Kasa",
664 "spend_key_private": "makullin biya (maɓallin kalmar sirri)",
665 "spend_key_public": "makullin biya (maɓallin jama'a)",
666 "status": "Matsayi:",
667 + "string_default": "Ƙin cika alƙawari",
668 "subaddress_title": "Jagorar subaddress",
669 "subaddresses": "Subaddresses",
670 "submit_request": "gabatar da bukata",
@@ -672,10 +689,13 @@
689 "sync_status_starting_sync": "KWAFI",
690 "sync_status_syncronized": "KYAU",
691 "sync_status_syncronizing": "KWAFI",
692 + "sync_status_timed_out": "ATED Out",
693 + "sync_status_unsupported": "Ba a Taimako ba",
694 "syncing_wallet_alert_content": "Ma'aunin ku da lissafin ma'amala bazai cika ba har sai an ce \"SYNCHRONIZED\" a saman. Danna/matsa don ƙarin koyo.",
695 "syncing_wallet_alert_title": "Walat ɗin ku yana aiki tare",
696 "template": "Samfura",
697 "template_name": "Sunan Samfura",
698 + "testnet_coins_no_value": "TalkNet tsabar kudi ba su da darajar",
699 "third_intro_content": "Yats suna zaune a wajen Kek Wallet, kuma. Ana iya maye gurbin kowane adireshin walat a duniya da Yat!",
700 "third_intro_title": "Yat yana wasa da kyau tare da wasu",
701 "thorchain_contract_address_not_supported": "Thorchain baya goyon bayan aika zuwa adireshin kwangila",
@@ -751,13 +771,16 @@
771 "trusted": "Amintacce",
772 "tx_commit_exception_no_dust_on_change": "An ƙi ma'amala da wannan adadin. Tare da waɗannan tsabar kudi Zaka iya aika ${min}, ba tare da canji ba ko ${max} wanda ya dawo canzawa.",
773 "tx_commit_failed": "Ma'amala ya kasa. Da fatan za a tuntuɓi goyan baya.",
774 + "tx_invalid_input": "Kuna amfani da nau'in shigar da ba daidai ba don wannan nau'in biyan kuɗi",
775 "tx_no_dust_exception": "An ƙi ma'amala ta hanyar aika adadin ƙarami. Da fatan za a gwada ƙara adadin.",
776 "tx_not_enough_inputs_exception": "Bai isa ba hanyoyin da ake samu. Da fatan za selectiari a karkashin Kwarewar Coin",
777 + "tx_rejected_bip68_final": "Ma'amala tana da abubuwan da basu dace ba kuma sun kasa maye gurbin ta.",
778 "tx_rejected_dust_change": "Ma'amala ta ƙi ta dokokin cibiyar sadarwa, ƙarancin canji (ƙura). Gwada aikawa da duka ko rage adadin.",
779 "tx_rejected_dust_output": "Ma'adar da aka ƙi ta dokokin cibiyar sadarwa, ƙananan fitarwa (ƙura). Da fatan za a ƙara adadin.",
780 "tx_rejected_dust_output_send_all": "Ma'adar da aka ƙi ta dokokin cibiyar sadarwa, ƙananan fitarwa (ƙura). Da fatan za a duba daidaiton tsabar kudi a ƙarƙashin ikon tsabar kudin.",
781 "tx_rejected_vout_negative": "Bai isa daidai ba don biyan wannan kudin ma'amala. Da fatan za a duba daidaiton tsabar kudi a ƙarƙashin ikon tsabar kudin.",
782 "tx_wrong_balance_exception": "Ba ku da isasshen ${currency} don aika wannan adadin.",
783 + "tx_wrong_balance_with_amount_exception": "Ba ku da isasshen ${currency} don aika jimlar adadin ${amount}",
784 "tx_zero_fee_exception": "Ba zai iya aika ma'amala da kuɗi 0 ba. Gwada ƙara ƙimar ko bincika haɗin ku don mahimmin ƙididdiga.",
785 "unavailable_balance": "Ma'aunin da ba ya samuwa",
786 "unavailable_balance_description": "Ma'auni Babu: Wannan jimlar ya haɗa da kuɗi waɗanda ke kulle a cikin ma'amaloli da ke jiran aiki da waɗanda kuka daskare sosai a cikin saitunan sarrafa kuɗin ku. Ma'auni da aka kulle za su kasance da zarar an kammala ma'amalolinsu, yayin da daskararrun ma'auni ba za su iya samun damar yin ciniki ba har sai kun yanke shawarar cire su.",
@@ -811,6 +834,7 @@
834 "warning": "Gargadi",
835 "welcome": "Barka da zuwa",
836 "welcome_to_cakepay": "Barka da zuwa Cake Pay!",
837 + "what_is_silent_payments": "Menene biyan shiru?",
838 "widgets_address": "Adireshin",
839 "widgets_or": "ko",
840 "widgets_restore_from_blockheight": "Sake dawo da daga blockheight",
res/values/strings_hi.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "बैकअप",
72 "backup_file": "बैकअपफ़ाइल",
73 "backup_password": "बैकअप पासवर्ड",
74 + "balance": "संतुलन",
75 "balance_page": "बैलेंस पेज",
76 "bill_amount": "बिल राशि",
77 "billing_address_info": "यदि बिलिंग पता मांगा जाए, तो अपना शिपिंग पता प्रदान करें",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "बिटकॉइन डार्क थीम",
80 "bitcoin_light_theme": "बिटकॉइन लाइट थीम",
81 "bitcoin_payments_require_1_confirmation": "बिटकॉइन भुगतान के लिए 1 पुष्टिकरण की आवश्यकता होती है, जिसमें 20 मिनट या अधिक समय लग सकता है। आपके धैर्य के लिए धन्यवाद! भुगतान की पुष्टि होने पर आपको ईमेल किया जाएगा।",
82 + "block_remaining": "1 ब्लॉक शेष",
83 "Blocks_remaining": "${status} शेष रहते हैं",
84 "bluetooth": "ब्लूटूथ",
85 "bright_theme": "उज्ज्वल",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "शुल्क कटौती की पुष्टि करें",
142 "confirm_fee_deduction_content": "क्या आप आउटपुट से शुल्क में कटौती करने के लिए सहमत हैं?",
143 "confirm_sending": "भेजने की पुष्टि करें",
144 + "confirm_silent_payments_switch_node": "वर्तमान में मूक भुगतान को स्कैन करने के लिए नोड्स को स्विच करना आवश्यक है",
145 "confirmations": "पुष्टिकरण",
146 "confirmed": "पुष्टि की गई शेष राशिी",
147 "confirmed_tx": "की पुष्टि",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "हर बार जब आप एक का उपयोग करते हैं तो हम नए पते उत्पन्न करते हैं, लेकिन पिछले पते काम करना जारी रखते हैं",
225 "email_address": "ईमेल पता",
226 "enable_replace_by_fee": "प्रतिस्थापित-दर-शुल्क सक्षम करें",
227 + "enable_silent_payments_scanning": "मूक भुगतान स्कैनिंग सक्षम करें",
228 "enabled": "सक्रिय",
229 "enter_amount": "राशि दर्ज करें",
230 "enter_backup_password": "यहां बैकअप पासवर्ड डालें",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "आपको धनराशि भेजी जाएगी\n${recipient_name}",
283 "failed_authentication": "प्रमाणीकरण विफल. ${state_error}",
284 "faq": "FAQ",
285 + "features": "विशेषताएँ",
286 "fetching": "ला रहा है",
287 "fiat_api": "फिएट पैसे API",
288 "fiat_balance": "फिएट बैलेंस",
@@ -533,6 +538,7 @@
538 "save_backup_password_alert": "बैकअप पासवर्ड सेव करें",
539 "save_to_downloads": "डाउनलोड में सहेजें",
540 "saved_the_trade_id": "मैंने व्यापार बचा लिया है ID",
541 + "scan_one_block": "एक ब्लॉक को स्कैन करना",
542 "scan_qr_code": "स्कैन क्यू आर कोड",
543 "scan_qr_code_to_get_address": "पता प्राप्त करने के लिए QR कोड स्कैन करें",
544 "scan_qr_on_device": "इस QR कोड को किसी अन्य डिवाइस पर स्कैन करें",
@@ -643,11 +649,22 @@
649 "sign_up": "साइन अप करें",
650 "signTransaction": "लेन-देन पर हस्ताक्षर करें",
651 "signup_for_card_accept_terms": "कार्ड के लिए साइन अप करें और शर्तें स्वीकार करें।",
652 + "silent_payments": "मूक भुगतान",
653 + "silent_payments_always_scan": "मूक भुगतान हमेशा स्कैनिंग सेट करें",
654 + "silent_payments_disclaimer": "नए पते नई पहचान नहीं हैं। यह एक अलग लेबल के साथ एक मौजूदा पहचान का पुन: उपयोग है।",
655 + "silent_payments_display_card": "मूक भुगतान कार्ड दिखाएं",
656 + "silent_payments_scan_from_date": "तिथि से स्कैन करना",
657 + "silent_payments_scan_from_date_or_blockheight": "कृपया उस ब्लॉक ऊंचाई दर्ज करें जिसे आप आने वाले मूक भुगतान के लिए स्कैन करना शुरू करना चाहते हैं, या, इसके बजाय तारीख का उपयोग करें। आप चुन सकते हैं कि क्या वॉलेट हर ब्लॉक को स्कैन करना जारी रखता है, या केवल निर्दिष्ट ऊंचाई की जांच करता है।",
658 + "silent_payments_scan_from_height": "ब्लॉक ऊंचाई से स्कैन करें",
659 + "silent_payments_scanned_tip": "टिप करने के लिए स्कैन किया! (${tip})",
660 + "silent_payments_scanning": "मूक भुगतान स्कैनिंग",
661 + "silent_payments_settings": "मूक भुगतान सेटिंग्स",
662 "slidable": "फिसलने लायक",
663 "sort_by": "इसके अनुसार क्रमबद्ध करें",
664 "spend_key_private": "खर्च करना (निजी)",
665 "spend_key_public": "खर्च करना (जनता)",
666 "status": "स्थिति: ",
667 + "string_default": "गलती करना",
668 "subaddress_title": "उपखंड सूची",
669 "subaddresses": "उप पते",
670 "submit_request": "एक अनुरोध सबमिट करें",
@@ -672,10 +689,13 @@
689 "sync_status_starting_sync": "सिताज़ा करना",
690 "sync_status_syncronized": "सिंक्रनाइज़",
691 "sync_status_syncronizing": "सिंक्रनाइज़ करने",
692 + "sync_status_timed_out": "समय समााप्त",
693 + "sync_status_unsupported": "असमर्थित नोड",
694 "syncing_wallet_alert_content": "आपकी शेष राशि और लेनदेन सूची तब तक पूरी नहीं हो सकती जब तक कि शीर्ष पर \"सिंक्रनाइज़्ड\" न लिखा हो। अधिक जानने के लिए क्लिक/टैप करें।",
695 "syncing_wallet_alert_title": "आपका वॉलेट सिंक हो रहा है",
696 "template": "खाका",
697 "template_name": "टेम्पलेट नाम",
698 + "testnet_coins_no_value": "टेस्टनेट सिक्कों का कोई मूल्य नहीं है",
699 "third_intro_content": "Yats Cake Wallet के बाहर भी रहता है। धरती पर किसी भी वॉलेट पते को Yat से बदला जा सकता है!",
700 "third_intro_title": "Yat दूसरों के साथ अच्छा खेलता है",
701 "thorchain_contract_address_not_supported": "थोरचेन एक अनुबंध पते पर भेजने का समर्थन नहीं करता है",
@@ -751,13 +771,16 @@
771 "trusted": "भरोसा",
772 "tx_commit_exception_no_dust_on_change": "लेनदेन को इस राशि से खारिज कर दिया जाता है। इन सिक्कों के साथ आप चेंज या ${min} के बिना ${max} को भेज सकते हैं जो परिवर्तन लौटाता है।",
773 "tx_commit_failed": "लेन -देन प्रतिबद्ध विफल। कृपया संपर्क समर्थन करें।",
774 + "tx_invalid_input": "आप इस प्रकार के भुगतान के लिए गलत इनपुट प्रकार का उपयोग कर रहे हैं",
775 "tx_no_dust_exception": "लेनदेन को बहुत छोटी राशि भेजकर अस्वीकार कर दिया जाता है। कृपया राशि बढ़ाने का प्रयास करें।",
776 "tx_not_enough_inputs_exception": "पर्याप्त इनपुट उपलब्ध नहीं है। कृपया सिक्का नियंत्रण के तहत अधिक चुनें",
777 + "tx_rejected_bip68_final": "लेन -देन में अपुष्ट इनपुट हैं और शुल्क द्वारा प्रतिस्थापित करने में विफल रहे हैं।",
778 "tx_rejected_dust_change": "नेटवर्क नियमों, कम परिवर्तन राशि (धूल) द्वारा खारिज किए गए लेनदेन। सभी भेजने या राशि को कम करने का प्रयास करें।",
779 "tx_rejected_dust_output": "नेटवर्क नियमों, कम आउटपुट राशि (धूल) द्वारा खारिज किए गए लेनदेन। कृपया राशि बढ़ाएं।",
780 "tx_rejected_dust_output_send_all": "नेटवर्क नियमों, कम आउटपुट राशि (धूल) द्वारा खारिज किए गए लेनदेन। कृपया सिक्का नियंत्रण के तहत चुने गए सिक्कों के संतुलन की जाँच करें।",
781 "tx_rejected_vout_negative": "इस लेनदेन की फीस के लिए भुगतान करने के लिए पर्याप्त शेष राशि नहीं है। कृपया सिक्के नियंत्रण के तहत सिक्कों के संतुलन की जाँच करें।",
782 "tx_wrong_balance_exception": "इस राशि को भेजने के लिए आपके पास पर्याप्त ${currency} नहीं है।",
783 + "tx_wrong_balance_with_amount_exception": "आपके पास पर्याप्त नहीं है${currency} ${amount} की कुल राशि भेजने के लिए",
784 "tx_zero_fee_exception": "0 शुल्क के साथ लेनदेन नहीं भेज सकते। नवीनतम अनुमानों के लिए दर बढ़ाने या अपने कनेक्शन की जांच करने का प्रयास करें।",
785 "unavailable_balance": "अनुपलब्ध शेष",
786 "unavailable_balance_description": "अनुपलब्ध शेष राशि: इस कुल में वे धनराशि शामिल हैं जो लंबित लेनदेन में बंद हैं और जिन्हें आपने अपनी सिक्का नियंत्रण सेटिंग्स में सक्रिय रूप से जमा कर रखा है। लॉक किए गए शेष उनके संबंधित लेन-देन पूरे होने के बाद उपलब्ध हो जाएंगे, जबकि जमे हुए शेष लेन-देन के लिए अप्राप्य रहेंगे जब तक कि आप उन्हें अनफ्रीज करने का निर्णय नहीं लेते।",
@@ -811,6 +834,7 @@
834 "warning": "चेतावनी",
835 "welcome": "स्वागत हे सेवा मेरे",
836 "welcome_to_cakepay": "केकपे में आपका स्वागत है!",
837 + "what_is_silent_payments": "मूक भुगतान क्या है?",
838 "widgets_address": "पता",
839 "widgets_or": "या",
840 "widgets_restore_from_blockheight": "ब्लॉकचेन से पुनर्स्थापित करें",
res/values/strings_hr.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "Sigurnosna kopija",
72 "backup_file": "Sigurnosna kopija datoteke",
73 "backup_password": "Lozinka za sigurnosnu kopiju",
74 + "balance": "Uravnotežiti",
75 "balance_page": "Stranica sa stanjem",
76 "bill_amount": "Iznos računa",
77 "billing_address_info": "Ako se od vas zatraži adresa za naplatu, navedite svoju adresu za dostavu",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "Bitcoin Tamna tema",
80 "bitcoin_light_theme": "Bitcoin Light Theme",
81 "bitcoin_payments_require_1_confirmation": "Bitcoin plaćanja zahtijevaju 1 potvrdu, što može potrajati 20 minuta ili dulje. Hvala na Vašem strpljenju! Dobit ćete e-poruku kada plaćanje bude potvrđeno.",
82 + "block_remaining": "Preostalo 1 blok",
83 "Blocks_remaining": "${status} preostalih blokova",
84 "bluetooth": "Bluetooth",
85 "bright_theme": "Jarka",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "Potvrdite odbitak naknade",
142 "confirm_fee_deduction_content": "Slažete li se da ćete odbiti naknadu od izlaza?",
143 "confirm_sending": "Potvrdi slanje",
144 + "confirm_silent_payments_switch_node": "Trenutno je potrebno prebaciti čvorove na skeniranje tihih plaćanja",
145 "confirmations": "Potvrde",
146 "confirmed": "Potvrđeno stanje",
147 "confirmed_tx": "Potvrđen",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "Minden egyes alkalommal új címeket generálunk, de a korábbi címek továbbra is működnek",
225 "email_address": "Adresa e-pošte",
226 "enable_replace_by_fee": "Omogući zamjenu",
227 + "enable_silent_payments_scanning": "Omogući skeniranje tihih plaćanja",
228 "enabled": "Omogućeno",
229 "enter_amount": "Unesite iznos",
230 "enter_backup_password": "Unesite svoju lozinku za sigurnosnu kopiju ovdje",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "Poslat ćete sredstva primatelju\n${recipient_name}",
283 "failed_authentication": "Autentifikacija neuspješna. ${state_error}",
284 "faq": "FAQ",
285 + "features": "Značajke",
286 "fetching": "Dohvaćanje",
287 "fiat_api": "Fiat API",
288 "fiat_balance": "Fiat Bilans",
@@ -531,6 +536,7 @@
536 "save_backup_password_alert": "Spremi lozinku za sigurnosnu kopiju",
537 "save_to_downloads": "Spremi u Preuzimanja",
538 "saved_the_trade_id": "Spremio/la sam transakcijski ID",
539 + "scan_one_block": "Skenirajte jedan blok",
540 "scan_qr_code": "Skenirajte QR kod",
541 "scan_qr_code_to_get_address": "Skeniraj QR kod za dobivanje adrese",
542 "scan_qr_on_device": "Skenirajte ovaj QR kod na drugom uređaju",
@@ -641,11 +647,22 @@
647 "sign_up": "Prijavite se",
648 "signTransaction": "Potpišite transakciju",
649 "signup_for_card_accept_terms": "Prijavite se za karticu i prihvatite uvjete.",
650 + "silent_payments": "Tiha plaćanja",
651 + "silent_payments_always_scan": "Postavite tiho plaćanje uvijek skeniranje",
652 + "silent_payments_disclaimer": "Nove adrese nisu novi identiteti. To je ponovna upotreba postojećeg identiteta s drugom oznakom.",
653 + "silent_payments_display_card": "Prikaži karticu tihih plaćanja",
654 + "silent_payments_scan_from_date": "Skeniranje iz datuma",
655 + "silent_payments_scan_from_date_or_blockheight": "Unesite visinu bloka koju želite započeti skeniranje za dolazna tiha plaćanja ili umjesto toga upotrijebite datum. Možete odabrati da li novčanik nastavlja skenirati svaki blok ili provjerava samo navedenu visinu.",
656 + "silent_payments_scan_from_height": "Skeniranje s visine bloka",
657 + "silent_payments_scanned_tip": "Skenirano na savjet! (${tip})",
658 + "silent_payments_scanning": "Skeniranje tihih plaćanja",
659 + "silent_payments_settings": "Postavke tihih plaćanja",
660 "slidable": "Klizna",
661 "sort_by": "Poredaj po",
662 "spend_key_private": "Spend key (privatni)",
663 "spend_key_public": "Spend key (javni)",
664 "status": "Status: ",
665 + "string_default": "Zadano",
666 "subaddress_title": "Lista podadresa",
667 "subaddresses": "Podadrese",
668 "submit_request": "podnesi zahtjev",
@@ -670,10 +687,13 @@
687 "sync_status_starting_sync": "ZAPOČINJEMO SINKRONIZIRANJE",
688 "sync_status_syncronized": "SINKRONIZIRANO",
689 "sync_status_syncronizing": "SINKRONIZIRANJE",
690 + "sync_status_timed_out": "ISTEKLO",
691 + "sync_status_unsupported": "Nepodržani čvor",
692 "syncing_wallet_alert_content": "Vaš saldo i popis transakcija možda neće biti potpuni sve dok na vrhu ne piše \"SINKRONIZIRANO\". Kliknite/dodirnite da biste saznali više.",
693 "syncing_wallet_alert_title": "Vaš novčanik se sinkronizira",
694 "template": "Predložak",
695 "template_name": "Naziv predloška",
696 + "testnet_coins_no_value": "TestNet kovanice nemaju vrijednost",
697 "third_intro_content": "Yats žive i izvan Cake Wallet -a. Bilo koja adresa novčanika na svijetu može se zamijeniti Yat!",
698 "third_intro_title": "Yat se lijepo igra s drugima",
699 "thorchain_contract_address_not_supported": "Thorchain ne podržava slanje na adresu ugovora",
@@ -749,13 +769,16 @@
769 "trusted": "vjerovao",
770 "tx_commit_exception_no_dust_on_change": "Transakcija se odbija s tim iznosom. Pomoću ovih kovanica možete poslati ${min} bez promjene ili ${max} koja vraća promjenu.",
771 "tx_commit_failed": "Obveza transakcije nije uspjela. Molimo kontaktirajte podršku.",
772 + "tx_invalid_input": "Koristite pogrešnu vrstu ulaza za ovu vrstu plaćanja",
773 "tx_no_dust_exception": "Transakcija se odbija slanjem iznosa premalo. Pokušajte povećati iznos.",
774 "tx_not_enough_inputs_exception": "Nema dovoljno unosa. Molimo odaberite više pod kontrolom novčića",
775 + "tx_rejected_bip68_final": "Transakcija ima nepotvrđene unose i nije zamijenila naknadom.",
776 "tx_rejected_dust_change": "Transakcija odbijena mrežnim pravilima, niska količina promjene (prašina). Pokušajte poslati sve ili smanjiti iznos.",
777 "tx_rejected_dust_output": "Transakcija odbijena mrežnim pravilima, niska količina izlaza (prašina). Molimo povećajte iznos.",
778 "tx_rejected_dust_output_send_all": "Transakcija odbijena mrežnim pravilima, niska količina izlaza (prašina). Molimo provjerite ravnotežu kovanica odabranih pod kontrolom novčića.",
779 "tx_rejected_vout_negative": "Nema dovoljno salda za plaćanje naknada ove transakcije. Molimo provjerite ravnotežu kovanica pod kontrolom novčića.",
780 "tx_wrong_balance_exception": "Nemate dovoljno ${currency} da biste poslali ovaj iznos.",
781 + "tx_wrong_balance_with_amount_exception": "Nemate dovoljno ${currency} za slanje ukupne količine ${amount}",
782 "tx_zero_fee_exception": "Ne mogu poslati transakciju s 0 naknade. Pokušajte povećati stopu ili provjeriti vezu za najnovije procjene.",
783 "unavailable_balance": "Nedostupno stanje",
784 "unavailable_balance_description": "Nedostupno stanje: Ovaj ukupni iznos uključuje sredstva koja su zaključana u transakcijama na čekanju i ona koja ste aktivno zamrznuli u postavkama kontrole novčića. Zaključani saldi postat će dostupni kada se dovrše njihove transakcije, dok zamrznuti saldi ostaju nedostupni za transakcije sve dok ih ne odlučite odmrznuti.",
@@ -809,6 +832,7 @@
832 "warning": "Upozorenje",
833 "welcome": "Dobrodošli na",
834 "welcome_to_cakepay": "Dobro došli u Cake Pay!",
835 + "what_is_silent_payments": "Što je tiha plaćanja?",
836 "widgets_address": "Adresa",
837 "widgets_or": "ili",
838 "widgets_restore_from_blockheight": "Oporavi pomoću visine bloka",
res/values/strings_id.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "Cadangan",
72 "backup_file": "File cadangan",
73 "backup_password": "Kata sandi cadangan",
74 + "balance": "Keseimbangan",
75 "balance_page": "Halaman Saldo",
76 "bill_amount": "Jumlah Tagihan",
77 "billing_address_info": "Jika diminta alamat billing, berikan alamat pengiriman Anda",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "Tema Gelap Bitcoin",
80 "bitcoin_light_theme": "Tema Cahaya Bitcoin",
81 "bitcoin_payments_require_1_confirmation": "Pembayaran Bitcoin memerlukan 1 konfirmasi, yang bisa memakan waktu 20 menit atau lebih. Terima kasih atas kesabaran Anda! Anda akan diemail saat pembayaran dikonfirmasi.",
82 + "block_remaining": "1 blok tersisa",
83 "Blocks_remaining": "${status} Blok Tersisa",
84 "bluetooth": "Bluetooth",
85 "bright_theme": "Cerah",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "Konfirmasi pengurangan biaya",
142 "confirm_fee_deduction_content": "Apakah Anda setuju untuk mengurangi biaya dari output?",
143 "confirm_sending": "Konfirmasi pengiriman",
144 + "confirm_silent_payments_switch_node": "Saat ini diminta untuk mengganti node untuk memindai pembayaran diam",
145 "confirmations": "Konfirmasi",
146 "confirmed": "Saldo Terkonfirmasi",
147 "confirmed_tx": "Dikonfirmasi",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "Kami menghasilkan alamat baru setiap kali Anda menggunakan satu, tetapi alamat sebelumnya tetap berfungsi",
225 "email_address": "Alamat Email",
226 "enable_replace_by_fee": "Aktifkan ganti-by-fee",
227 + "enable_silent_payments_scanning": "Aktifkan pemindaian pembayaran diam",
228 "enabled": "Diaktifkan",
229 "enter_amount": "Masukkan Jumlah",
230 "enter_backup_password": "Masukkan kata sandi cadangan di sini",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "Anda akan mengirim dana ke\n${recipient_name}",
283 "failed_authentication": "Otentikasi gagal. ${state_error}",
284 "faq": "Pertanyaan yang Sering Diajukan",
285 + "features": "Fitur",
286 "fetching": "Mengambil",
287 "fiat_api": "API fiat",
288 "fiat_balance": "Saldo Fiat",
@@ -534,6 +539,7 @@
539 "save_backup_password_alert": "Simpan kata sandi cadangan",
540 "save_to_downloads": "Simpan ke Unduhan",
541 "saved_the_trade_id": "Saya telah menyimpan ID perdagangan",
542 + "scan_one_block": "Pindai satu blok",
543 "scan_qr_code": "Scan kode QR untuk mendapatkan alamat",
544 "scan_qr_code_to_get_address": "Pindai kode QR untuk mendapatkan alamat",
545 "scan_qr_on_device": "Pindai kode QR ini di perangkat lain",
@@ -644,11 +650,22 @@
650 "sign_up": "Daftar",
651 "signTransaction": "Tandatangani Transaksi",
652 "signup_for_card_accept_terms": "Daftar untuk kartu dan terima syarat dan ketentuan.",
653 + "silent_payments": "Pembayaran diam",
654 + "silent_payments_always_scan": "Tetapkan pembayaran diam selalu pemindaian",
655 + "silent_payments_disclaimer": "Alamat baru bukanlah identitas baru. Ini adalah penggunaan kembali identitas yang ada dengan label yang berbeda.",
656 + "silent_payments_display_card": "Tunjukkan kartu pembayaran diam",
657 + "silent_payments_scan_from_date": "Pindai dari tanggal",
658 + "silent_payments_scan_from_date_or_blockheight": "Harap masukkan ketinggian blok yang ingin Anda mulai pemindaian untuk pembayaran diam yang masuk, atau, gunakan tanggal sebagai gantinya. Anda dapat memilih jika dompet terus memindai setiap blok, atau memeriksa hanya ketinggian yang ditentukan.",
659 + "silent_payments_scan_from_height": "Pindai dari Tinggi Blok",
660 + "silent_payments_scanned_tip": "Pindai untuk memberi tip! (${tip})",
661 + "silent_payments_scanning": "Pemindaian pembayaran diam",
662 + "silent_payments_settings": "Pengaturan pembayaran diam",
663 "slidable": "Dapat digeser",
664 "sort_by": "Sortir dengan",
665 "spend_key_private": "Kunci pengeluaran (privat)",
666 "spend_key_public": "Kunci pengeluaran (publik)",
667 "status": "Status: ",
668 + "string_default": "Bawaan",
669 "subaddress_title": "Daftar sub-alamat",
670 "subaddresses": "Sub-alamat",
671 "submit_request": "kirim permintaan",
@@ -673,10 +690,13 @@
690 "sync_status_starting_sync": "MULAI SINKRONISASI",
691 "sync_status_syncronized": "SUDAH TERSINKRONISASI",
692 "sync_status_syncronizing": "SEDANG SINKRONISASI",
693 + "sync_status_timed_out": "WAKTU HABIS",
694 + "sync_status_unsupported": "Node yang tidak didukung",
695 "syncing_wallet_alert_content": "Saldo dan daftar transaksi Anda mungkin belum lengkap sampai tertulis “SYNCHRONIZED” di bagian atas. Klik/ketuk untuk mempelajari lebih lanjut.",
696 "syncing_wallet_alert_title": "Dompet Anda sedang disinkronkan",
697 "template": "Template",
698 "template_name": "Nama Templat",
699 + "testnet_coins_no_value": "Koin TestNet tidak memiliki nilai",
700 "third_intro_content": "Yats hidup di luar Cake Wallet juga. Setiap alamat dompet di dunia dapat diganti dengan Yat!",
701 "third_intro_title": "Yat bermain baik dengan yang lain",
702 "thorchain_contract_address_not_supported": "Thorchain tidak mendukung pengiriman ke alamat kontrak",
@@ -752,13 +772,16 @@
772 "trusted": "Dipercayai",
773 "tx_commit_exception_no_dust_on_change": "Transaksi ditolak dengan jumlah ini. Dengan koin ini Anda dapat mengirim ${min} tanpa perubahan atau ${max} yang mengembalikan perubahan.",
774 "tx_commit_failed": "Transaksi Gagal. Silakan hubungi Dukungan.",
775 + "tx_invalid_input": "Anda menggunakan jenis input yang salah untuk jenis pembayaran ini",
776 "tx_no_dust_exception": "Transaksi ditolak dengan mengirimkan jumlah yang terlalu kecil. Silakan coba tingkatkan jumlahnya.",
777 "tx_not_enough_inputs_exception": "Tidak cukup input yang tersedia. Pilih lebih banyak lagi di bawah Kontrol Koin",
778 + "tx_rejected_bip68_final": "Transaksi memiliki input yang belum dikonfirmasi dan gagal mengganti dengan biaya.",
779 "tx_rejected_dust_change": "Transaksi ditolak oleh aturan jaringan, jumlah perubahan rendah (debu). Coba kirim semua atau mengurangi jumlahnya.",
780 "tx_rejected_dust_output": "Transaksi ditolak oleh aturan jaringan, jumlah output rendah (debu). Harap tingkatkan jumlahnya.",
781 "tx_rejected_dust_output_send_all": "Transaksi ditolak oleh aturan jaringan, jumlah output rendah (debu). Silakan periksa saldo koin yang dipilih di bawah kontrol koin.",
782 "tx_rejected_vout_negative": "Tidak cukup saldo untuk membayar biaya transaksi ini. Silakan periksa saldo koin di bawah kendali koin.",
783 "tx_wrong_balance_exception": "Anda tidak memiliki cukup ${currency} untuk mengirim jumlah ini.",
784 + "tx_wrong_balance_with_amount_exception": "Anda tidak memiliki cukup ${currency} untuk mengirim jumlah total ${amount}",
785 "tx_zero_fee_exception": "Tidak dapat mengirim transaksi dengan biaya 0. Coba tingkatkan tarif atau periksa koneksi Anda untuk perkiraan terbaru.",
786 "unavailable_balance": "Saldo tidak tersedia",
787 "unavailable_balance_description": "Saldo Tidak Tersedia: Total ini termasuk dana yang terkunci dalam transaksi yang tertunda dan dana yang telah Anda bekukan secara aktif di pengaturan kontrol koin Anda. Saldo yang terkunci akan tersedia setelah transaksi masing-masing selesai, sedangkan saldo yang dibekukan tetap tidak dapat diakses untuk transaksi sampai Anda memutuskan untuk mencairkannya.",
@@ -812,6 +835,7 @@
835 "warning": "Peringatan",
836 "welcome": "Selamat datang di",
837 "welcome_to_cakepay": "Selamat Datang di Cake Pay!",
838 + "what_is_silent_payments": "Apa itu pembayaran diam?",
839 "widgets_address": "Alamat",
840 "widgets_or": "atau",
841 "widgets_restore_from_blockheight": "Pulihkan dari tinggi blok",
res/values/strings_it.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "Backup",
72 "backup_file": "Backup file",
73 "backup_password": "Backup password",
74 + "balance": "Bilancia",
75 "balance_page": "Pagina di equilibrio",
76 "bill_amount": "Importo della fattura",
77 "billing_address_info": "Se ti viene richiesto un indirizzo di fatturazione, fornisci il tuo indirizzo di spedizione",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "Tema oscuro di Bitcoin",
80 "bitcoin_light_theme": "Tema luce Bitcoin",
81 "bitcoin_payments_require_1_confirmation": "I pagamenti in bitcoin richiedono 1 conferma, che può richiedere 20 minuti o più. Grazie per la vostra pazienza! Riceverai un'e-mail quando il pagamento sarà confermato.",
82 + "block_remaining": "1 blocco rimanente",
83 "Blocks_remaining": "${status} Blocchi Rimanenti",
84 "bluetooth": "Bluetooth",
85 "bright_theme": "Colorato",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "Conferma la detrazione delle commissioni",
142 "confirm_fee_deduction_content": "Accetti di detrarre la commissione dall'output?",
143 "confirm_sending": "Conferma l'invio",
144 + "confirm_silent_payments_switch_node": "Attualmente è necessario cambiare nodi per scansionare i pagamenti silenziosi",
145 "confirmations": "Conferme",
146 "confirmed": "Saldo confermato",
147 "confirmed_tx": "Confermato",
@@ -222,6 +225,7 @@
225 "electrum_address_disclaimer": "Generiamo nuovi indirizzi ogni volta che ne utilizzi uno, ma gli indirizzi precedenti continuano a funzionare",
226 "email_address": "Indirizzo e-mail",
227 "enable_replace_by_fee": "Abilita sostituzione per fee",
228 + "enable_silent_payments_scanning": "Abilita la scansione dei pagamenti silenziosi",
229 "enabled": "Abilitato",
230 "enter_amount": "Inserisci importo",
231 "enter_backup_password": "Inserisci la password di backup qui",
@@ -279,6 +283,7 @@
283 "extracted_address_content": "Invierai i tuoi fondi a\n${recipient_name}",
284 "failed_authentication": "Autenticazione fallita. ${state_error}",
285 "faq": "Domande Frequenti",
286 + "features": "Caratteristiche",
287 "fetching": "Recupero",
288 "fiat_api": "Fiat API",
289 "fiat_balance": "Equilibrio fiat",
@@ -533,6 +538,7 @@
538 "save_backup_password_alert": "Salva password Backup",
539 "save_to_downloads": "Salva in Download",
540 "saved_the_trade_id": "Ho salvato l'ID dello scambio",
541 + "scan_one_block": "Scansionare un blocco",
542 "scan_qr_code": "Scansiona il codice QR",
543 "scan_qr_code_to_get_address": "Scansiona il codice QR per ottenere l'indirizzo",
544 "scan_qr_on_device": "Scansiona questo codice QR su un altro dispositivo",
@@ -643,11 +649,22 @@
649 "sign_up": "Registrati",
650 "signTransaction": "Firma la transazione",
651 "signup_for_card_accept_terms": "Registrati per la carta e accetta i termini.",
652 + "silent_payments": "Pagamenti silenziosi",
653 + "silent_payments_always_scan": "Impostare i pagamenti silenziosi che scansionano sempre",
654 + "silent_payments_disclaimer": "I nuovi indirizzi non sono nuove identità. È un riutilizzo di un'identità esistente con un'etichetta diversa.",
655 + "silent_payments_display_card": "Mostra la carta di pagamenti silenziosi",
656 + "silent_payments_scan_from_date": "Scansionare dalla data",
657 + "silent_payments_scan_from_date_or_blockheight": "Inserisci l'altezza del blocco che si desidera iniziare la scansione per i pagamenti silenziosi in arrivo o, utilizza invece la data. Puoi scegliere se il portafoglio continua a scansionare ogni blocco o controlla solo l'altezza specificata.",
658 + "silent_payments_scan_from_height": "Scansione dall'altezza del blocco",
659 + "silent_payments_scanned_tip": "Scansionato per dare la mancia! (${tip})",
660 + "silent_payments_scanning": "Scansione di pagamenti silenziosi",
661 + "silent_payments_settings": "Impostazioni di pagamenti silenziosi",
662 "slidable": "Scorrevole",
663 "sort_by": "Ordina per",
664 "spend_key_private": "Chiave di spesa (privata)",
665 "spend_key_public": "Chiave di spesa (pubblica)",
666 "status": "Stato: ",
667 + "string_default": "Predefinito",
668 "subaddress_title": "Lista sottoindirizzi",
669 "subaddresses": "Sottoindirizzi",
670 "submit_request": "invia una richiesta",
@@ -672,10 +689,13 @@
689 "sync_status_starting_sync": "INIZIO SINC",
690 "sync_status_syncronized": "SINCRONIZZATO",
691 "sync_status_syncronizing": "SINCRONIZZAZIONE",
692 + "sync_status_timed_out": "FUORI TEMPO",
693 + "sync_status_unsupported": "Nodo non supportato",
694 "syncing_wallet_alert_content": "Il saldo e l'elenco delle transazioni potrebbero non essere completi fino a quando non viene visualizzato \"SYNCHRONIZED\" in alto. Clicca/tocca per saperne di più.",
695 "syncing_wallet_alert_title": "Il tuo portafoglio si sta sincronizzando",
696 "template": "Modello",
697 "template_name": "Nome modello",
698 + "testnet_coins_no_value": "Le monete TestNet non hanno valore",
699 "third_intro_content": "Yat può funzionare anche fuori da Cake Wallet. Qualsiasi indirizzo di portafoglio sulla terra può essere sostituito con uno Yat!",
700 "third_intro_title": "Yat gioca bene con gli altri",
701 "thorchain_contract_address_not_supported": "Thorchain non supporta l'invio a un indirizzo contrattuale",
@@ -751,13 +771,16 @@
771 "trusted": "di fiducia",
772 "tx_commit_exception_no_dust_on_change": "La transazione viene respinta con questo importo. Con queste monete è possibile inviare ${min} senza modifiche o ${max} che restituisce il cambiamento.",
773 "tx_commit_failed": "Commit di transazione non riuscita. Si prega di contattare il supporto.",
774 + "tx_invalid_input": "Stai usando il tipo di input sbagliato per questo tipo di pagamento",
775 "tx_no_dust_exception": "La transazione viene respinta inviando un importo troppo piccolo. Per favore, prova ad aumentare l'importo.",
776 "tx_not_enough_inputs_exception": "Input non sufficienti disponibili. Seleziona di più sotto il controllo delle monete",
777 + "tx_rejected_bip68_final": "La transazione ha input non confermati e non è stata sostituita per tassa.",
778 "tx_rejected_dust_change": "Transazione respinta dalle regole di rete, quantità bassa variazione (polvere). Prova a inviare tutto o ridurre l'importo.",
779 "tx_rejected_dust_output": "Transazione respinta dalle regole di rete, bassa quantità di output (polvere). Si prega di aumentare l'importo.",
780 "tx_rejected_dust_output_send_all": "Transazione respinta dalle regole di rete, bassa quantità di output (polvere). Si prega di controllare il saldo delle monete selezionate sotto controllo delle monete.",
781 "tx_rejected_vout_negative": "Non abbastanza saldo per pagare le commissioni di questa transazione. Si prega di controllare il saldo delle monete sotto controllo delle monete.",
782 "tx_wrong_balance_exception": "Non hai abbastanza ${currency} per inviare questo importo.",
783 + "tx_wrong_balance_with_amount_exception": "Non hai abbastanza ${currency} per inviare la quantità totale di ${amount}",
784 "tx_zero_fee_exception": "Impossibile inviare transazioni con 0 tassa. Prova ad aumentare la tariffa o controlla la connessione per le ultime stime.",
785 "unavailable_balance": "Saldo non disponibile",
786 "unavailable_balance_description": "Saldo non disponibile: questo totale include i fondi bloccati nelle transazioni in sospeso e quelli che hai congelato attivamente nelle impostazioni di controllo delle monete. I saldi bloccati diventeranno disponibili una volta completate le rispettive transazioni, mentre i saldi congelati rimarranno inaccessibili per le transazioni finché non deciderai di sbloccarli.",
@@ -812,6 +835,7 @@
835 "warning": "Avvertimento",
836 "welcome": "Benvenuto",
837 "welcome_to_cakepay": "Benvenuto in Cake Pay!",
838 + "what_is_silent_payments": "Che cos'è i pagamenti silenziosi?",
839 "widgets_address": "Indirizzo",
840 "widgets_or": "o",
841 "widgets_restore_from_blockheight": "Recupera da altezza blocco",
res/values/strings_ja.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "バックアップ",
72 "backup_file": "バックアップファイル",
73 "backup_password": "バックアップパスワード",
74 + "balance": "バランス",
75 "balance_page": "残高ページ",
76 "bill_amount": "請求額",
77 "billing_address_info": "請求先住所を尋ねられた場合は、配送先住所を入力してください",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "ビットコインダークテーマ",
80 "bitcoin_light_theme": "ビットコインライトテーマ",
81 "bitcoin_payments_require_1_confirmation": "ビットコインの支払いには 1 回の確認が必要で、これには 20 分以上かかる場合があります。お待ち頂きまして、ありがとうございます!支払いが確認されると、メールが送信されます。",
82 + "block_remaining": "残り1ブロック",
83 "Blocks_remaining": "${status} 残りのブロック",
84 "bluetooth": "ブルートゥース",
85 "bright_theme": "明るい",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "料金控除を確認します",
142 "confirm_fee_deduction_content": "出力から料金を差し引くことに同意しますか?",
143 "confirm_sending": "送信を確認",
144 + "confirm_silent_payments_switch_node": "現在、ノードを切り替えてサイレント決済をスキャンする必要があります",
145 "confirmations": "確認",
146 "confirmed": "確認済み残高",
147 "confirmed_tx": "確認済み",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "使用するたびに新しいアドレスが生成されますが、以前のアドレスは引き続き機能します",
225 "email_address": "メールアドレス",
226 "enable_replace_by_fee": "交換ごとに有効にします",
227 + "enable_silent_payments_scanning": "サイレントペイメントスキャンを有効にします",
228 "enabled": "有効",
229 "enter_amount": "金額を入力",
230 "enter_backup_password": "ここにバックアップパスワードを入力してください",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "に送金します\n${recipient_name}",
283 "failed_authentication": "認証失敗. ${state_error}",
284 "faq": "FAQ",
285 + "features": "特徴",
286 "fetching": "フェッチング",
287 "fiat_api": "不換紙幣 API",
288 "fiat_balance": "フィアットバランス",
@@ -532,6 +537,7 @@
537 "save_backup_password_alert": "バックアップパスワードを保存する",
538 "save_to_downloads": "ダウンロードに保存",
539 "saved_the_trade_id": "取引IDを保存しました",
540 + "scan_one_block": "1つのブロックをスキャンします",
541 "scan_qr_code": "QRコードをスキャン",
542 "scan_qr_code_to_get_address": "QRコードをスキャンして住所を取得します",
543 "scan_qr_on_device": "別のデバイスでこの QR コードをスキャンします",
@@ -642,11 +648,22 @@
648 "sign_up": "サインアップ",
649 "signTransaction": "トランザクションに署名する",
650 "signup_for_card_accept_terms": "カードにサインアップして、利用規約に同意してください。",
651 + "silent_payments": "サイレント支払い",
652 + "silent_payments_always_scan": "サイレント決済を常にスキャンします",
653 + "silent_payments_disclaimer": "新しいアドレスは新しいアイデンティティではありません。これは、異なるラベルを持つ既存のアイデンティティの再利用です。",
654 + "silent_payments_display_card": "サイレントペイメントカードを表示します",
655 + "silent_payments_scan_from_date": "日付からスキャンします",
656 + "silent_payments_scan_from_date_or_blockheight": "着信のサイレント決済のためにスキャンを開始するブロックの高さを入力するか、代わりに日付を使用してください。ウォレットがすべてのブロックをスキャンし続けるか、指定された高さのみをチェックするかどうかを選択できます。",
657 + "silent_payments_scan_from_height": "ブロックの高さからスキャンします",
658 + "silent_payments_scanned_tip": "チップをスキャンしました! (${tip})",
659 + "silent_payments_scanning": "サイレントペイメントスキャン",
660 + "silent_payments_settings": "サイレントペイメント設定",
661 "slidable": "スライド可能",
662 "sort_by": "並び替え",
663 "spend_key_private": "キーを使う (プライベート)",
664 "spend_key_public": "キーを使う (パブリック)",
665 "status": "状態: ",
666 + "string_default": "デフォルト",
667 "subaddress_title": "サブアドレス一覧",
668 "subaddresses": "サブアドレス",
669 "submit_request": "リクエストを送信する",
@@ -671,10 +688,13 @@
688 "sync_status_starting_sync": "同期の開始",
689 "sync_status_syncronized": "同期された",
690 "sync_status_syncronizing": "同期",
691 + "sync_status_timed_out": "タイムアウトしました",
692 + "sync_status_unsupported": "サポートされていないノード",
693 "syncing_wallet_alert_content": "上部に「同期済み」と表示されるまで、残高と取引リストが完了していない可能性があります。詳細については、クリック/タップしてください。",
694 "syncing_wallet_alert_title": "ウォレットは同期中です",
695 "template": "テンプレート",
696 "template_name": "テンプレート名",
697 + "testnet_coins_no_value": "テストネットコインには価値がありません",
698 "third_intro_content": "YatsはCakeWalletの外にも住んでいます。 地球上のどのウォレットアドレスもYatに置き換えることができます!",
699 "third_intro_title": "Yatは他の人とうまく遊ぶ",
700 "thorchain_contract_address_not_supported": "Thorchainは、契約アドレスへの送信をサポートしていません",
@@ -750,13 +770,16 @@
770 "trusted": "信頼できる",
771 "tx_commit_exception_no_dust_on_change": "この金額ではトランザクションは拒否されます。 これらのコインを使用すると、おつりなしの ${min} またはおつりを返す ${max} を送信できます。",
772 "tx_commit_failed": "トランザクションコミットは失敗しました。サポートに連絡してください。",
773 + "tx_invalid_input": "このタイプの支払いに間違った入力タイプを使用しています",
774 "tx_no_dust_exception": "トランザクションは、小さすぎる金額を送信することにより拒否されます。量を増やしてみてください。",
775 "tx_not_enough_inputs_exception": "利用可能な入力が十分ではありません。コイン制御下でもっと選択してください",
776 + "tx_rejected_bip68_final": "トランザクションには未確認の入力があり、料金で交換できませんでした。",
777 "tx_rejected_dust_change": "ネットワークルール、低い変更量(ほこり)によって拒否されたトランザクション。すべてを送信するか、金額を減らしてみてください。",
778 "tx_rejected_dust_output": "ネットワークルール、低出力量(ダスト)によって拒否されたトランザクション。金額を増やしてください。",
779 "tx_rejected_dust_output_send_all": "ネットワークルール、低出力量(ダスト)によって拒否されたトランザクション。コイン管理下で選択されたコインのバランスを確認してください。",
780 "tx_rejected_vout_negative": "この取引の料金に支払うのに十分な残高はありません。コイン制御下のコインのバランスを確認してください。",
781 "tx_wrong_balance_exception": "この金額を送信するのに十分な${currency}はありません。",
782 + "tx_wrong_balance_with_amount_exception": "あなたは十分なものを持っていませ${currency} ${amount}",
783 "tx_zero_fee_exception": "0料金でトランザクションを送信できません。レートを上げて、最新の見積もりについて接続を確認してみてください。",
784 "unavailable_balance": "利用できない残高",
785 "unavailable_balance_description": "利用不可能な残高: この合計には、保留中のトランザクションにロックされている資金と、コイン管理設定でアクティブに凍結した資金が含まれます。ロックされた残高は、それぞれの取引が完了すると利用可能になりますが、凍結された残高は、凍結を解除するまで取引にアクセスできません。",
@@ -810,6 +833,7 @@
833 "warning": "警告",
834 "welcome": "ようこそ に",
835 "welcome_to_cakepay": "Cake Payへようこそ!",
836 + "what_is_silent_payments": "サイレント支払いとは何ですか?",
837 "widgets_address": "住所",
838 "widgets_or": "または",
839 "widgets_restore_from_blockheight": "ブロックの高さから復元",
res/values/strings_ko.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "지원",
72 "backup_file": "백업 파일",
73 "backup_password": "백업 비밀번호",
74 + "balance": "균형",
75 "balance_page": "잔액 페이지",
76 "bill_amount": "청구 금액",
77 "billing_address_info": "청구서 수신 주소를 묻는 메시지가 표시되면 배송 주소를 입력하세요.",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "비트코인 다크 테마",
80 "bitcoin_light_theme": "비트코인 라이트 테마",
81 "bitcoin_payments_require_1_confirmation": "비트코인 결제는 1번의 확인이 필요하며 20분 이상이 소요될 수 있습니다. 기다려 주셔서 감사합니다! 결제가 확인되면 이메일이 전송됩니다.",
82 + "block_remaining": "남은 블록 1 개",
83 "Blocks_remaining": "${status} 남은 블록",
84 "bluetooth": "블루투스",
85 "bright_theme": "선명한",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "수수료 공제를 확인하십시오",
142 "confirm_fee_deduction_content": "출력에서 수수료를 공제하는 데 동의하십니까?",
143 "confirm_sending": "전송 확인",
144 + "confirm_silent_payments_switch_node": "현재 사일런트 결제를 스캔하려면 노드를 전환해야합니다.",
145 "confirmations": "확인",
146 "confirmed": "확인된 잔액",
147 "confirmed_tx": "확인",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "사용할 때마다 새 주소가 생성되지만 이전 주소는 계속 작동합니다.",
225 "email_address": "이메일 주소",
226 "enable_replace_by_fee": "대체별로 활성화하십시오",
227 + "enable_silent_payments_scanning": "무음 지불 스캔을 활성화합니다",
228 "enabled": "사용",
229 "enter_amount": "금액 입력",
230 "enter_backup_password": "여기에 백업 비밀번호를 입력하세요.",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "당신은에 자금을 보낼 것입니다\n${recipient_name}",
283 "failed_authentication": "인증 실패. ${state_error}",
284 "faq": "FAQ",
285 + "features": "특징",
286 "fetching": "가져 오는 중",
287 "fiat_api": "명목 화폐 API",
288 "fiat_balance": "피아트 잔액",
@@ -532,6 +537,7 @@
537 "save_backup_password_alert": "백업 비밀번호 저장",
538 "save_to_downloads": "다운로드에 저장",
539 "saved_the_trade_id": "거래 ID를 저장했습니다",
540 + "scan_one_block": "하나의 블록을 스캔하십시오",
541 "scan_qr_code": "QR 코드 스캔",
542 "scan_qr_code_to_get_address": "QR 코드를 스캔하여 주소를 얻습니다.",
543 "scan_qr_on_device": "다른 기기에서 이 QR 코드를 스캔하세요.",
@@ -642,11 +648,22 @@
648 "sign_up": "가입",
649 "signTransaction": "거래 서명",
650 "signup_for_card_accept_terms": "카드에 가입하고 약관에 동의합니다.",
651 + "silent_payments": "조용한 지불",
652 + "silent_payments_always_scan": "무음금을 항상 스캔합니다",
653 + "silent_payments_disclaimer": "새로운 주소는 새로운 정체성이 아닙니다. 다른 레이블로 기존 신원을 재사용하는 것입니다.",
654 + "silent_payments_display_card": "사일런트 지불 카드 표시",
655 + "silent_payments_scan_from_date": "날짜부터 스캔하십시오",
656 + "silent_payments_scan_from_date_or_blockheight": "들어오는 사일런트 결제를 위해 스캔을 시작하려는 블록 높이를 입력하거나 대신 날짜를 사용하십시오. 지갑이 모든 블록을 계속 스캔하는지 여부를 선택하거나 지정된 높이 만 확인할 수 있습니다.",
657 + "silent_payments_scan_from_height": "블록 높이에서 스캔하십시오",
658 + "silent_payments_scanned_tip": "팁을 스캔했습니다! (${tip})",
659 + "silent_payments_scanning": "조용한 지불 스캔",
660 + "silent_payments_settings": "조용한 지불 설정",
661 "slidable": "슬라이딩 가능",
662 "sort_by": "정렬 기준",
663 "spend_key_private": "지출 키 (은밀한)",
664 "spend_key_public": "지출 키 (공공의)",
665 "status": "지위: ",
666 + "string_default": "기본",
667 "subaddress_title": "하위 주소 목록",
668 "subaddresses": "하위 주소",
669 "submit_request": "요청을 제출",
@@ -671,10 +688,13 @@
688 "sync_status_starting_sync": "동기화 시작",
689 "sync_status_syncronized": "동기화",
690 "sync_status_syncronizing": "동기화",
691 + "sync_status_timed_out": "시간 초과",
692 + "sync_status_unsupported": "지원되지 않은 노드",
693 "syncing_wallet_alert_content": "상단에 \"동기화됨\"이라고 표시될 때까지 잔액 및 거래 목록이 완전하지 않을 수 있습니다. 자세히 알아보려면 클릭/탭하세요.",
694 "syncing_wallet_alert_title": "지갑 동기화 중",
695 "template": "주형",
696 "template_name": "템플릿 이름",
697 + "testnet_coins_no_value": "Testnet 코인은 가치가 없습니다",
698 "third_intro_content": "Yats는 Cake Wallet 밖에서도 살고 있습니다. 지구상의 모든 지갑 주소는 Yat!",
699 "third_intro_title": "Yat는 다른 사람들과 잘 놉니다.",
700 "thorchain_contract_address_not_supported": "Thorchain은 계약 주소로 보내는 것을 지원하지 않습니다",
@@ -750,13 +770,16 @@
770 "trusted": "신뢰할 수 있는",
771 "tx_commit_exception_no_dust_on_change": "이 금액으로 거래가 거부되었습니다. 이 코인을 사용하면 거스름돈 없이 ${min}를 보내거나 거스름돈을 반환하는 ${max}를 보낼 수 있습니다.",
772 "tx_commit_failed": "거래 커밋이 실패했습니다. 지원에 연락하십시오.",
773 + "tx_invalid_input": "이 유형의 지불에 잘못 입력 유형을 사용하고 있습니다.",
774 "tx_no_dust_exception": "너무 작은 금액을 보내면 거래가 거부됩니다. 금액을 늘리십시오.",
775 "tx_not_enough_inputs_exception": "사용 가능한 입력이 충분하지 않습니다. 코인 컨트롤에서 더 많은 것을 선택하십시오",
776 + "tx_rejected_bip68_final": "거래는 확인되지 않은 입력을 받았으며 수수료로 교체하지 못했습니다.",
777 "tx_rejected_dust_change": "네트워크 규칙, 낮은 변경 금액 (먼지)에 의해 거부 된 거래. 전부를 보내거나 금액을 줄이십시오.",
778 "tx_rejected_dust_output": "네트워크 규칙, 낮은 출력 금액 (먼지)에 의해 거부 된 거래. 금액을 늘리십시오.",
779 "tx_rejected_dust_output_send_all": "네트워크 규칙, 낮은 출력 금액 (먼지)에 의해 거부 된 거래. 동전 제어에서 선택한 동전의 균형을 확인하십시오.",
780 "tx_rejected_vout_negative": "이 거래 수수료를 지불하기에 잔액이 충분하지 않습니다. 동전 통제하에 동전의 균형을 확인하십시오.",
781 "tx_wrong_balance_exception": "이 금액을 보내기에 충분한 ${currency}가 충분하지 않습니다.",
782 + "tx_wrong_balance_with_amount_exception": "충분하지 않습니다 ${currency} 총 ${amount} 총 금액을 보내십시오.",
783 "tx_zero_fee_exception": "0 수수료로 거래를 보낼 수 없습니다. 최신 견적에 대해서는 속도를 높이거나 연결을 확인하십시오.",
784 "unavailable_balance": "사용할 수 없는 잔액",
785 "unavailable_balance_description": "사용할 수 없는 잔액: 이 총계에는 보류 중인 거래에 잠겨 있는 자금과 코인 관리 설정에서 적극적으로 동결된 자금이 포함됩니다. 잠긴 잔액은 해당 거래가 완료되면 사용할 수 있게 되며, 동결된 잔액은 동결을 해제하기 전까지 거래에 액세스할 수 없습니다.",
@@ -810,6 +833,7 @@
833 "warning": "경고",
834 "welcome": "환영 에",
835 "welcome_to_cakepay": "Cake Pay에 오신 것을 환영합니다!",
836 + "what_is_silent_payments": "조용한 지불이란 무엇입니까?",
837 "widgets_address": "주소",
838 "widgets_or": "또는",
839 "widgets_restore_from_blockheight": "블록 높이에서 복원",
res/values/strings_my.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "မိတ္တူ",
72 "backup_file": "အရန်ဖိုင်",
73 "backup_password": "စကားဝှက်ကို အရန်သိမ်းဆည်းပါ။",
74 + "balance": "လက်ကျန်ငေှ",
75 "balance_page": "လက်ကျန်စာမျက်နှာ",
76 "bill_amount": "ဘီလ်ပမာဏ",
77 "billing_address_info": "ငွေပေးချေရမည့်လိပ်စာကို တောင်းဆိုပါက သင့်ပို့ဆောင်ရေးလိပ်စာကို ပေးပါ။",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "Bitcoin Dark Theme",
80 "bitcoin_light_theme": "Bitcoin Light အပြင်အဆင်",
81 "bitcoin_payments_require_1_confirmation": "Bitcoin ငွေပေးချေမှုများသည် မိနစ် 20 သို့မဟုတ် ထို့ထက်ပိုကြာနိုင်သည် 1 အတည်ပြုချက် လိုအပ်သည်။ မင်းရဲ့စိတ်ရှည်မှုအတွက် ကျေးဇူးတင်ပါတယ်။ ငွေပေးချေမှုကို အတည်ပြုပြီးသောအခါ သင့်ထံ အီးမေးလ်ပို့ပါမည်။",
82 + "block_remaining": "ကျန်ရှိနေသေးသော block",
83 "Blocks_remaining": "${status} ဘလောက်များ ကျန်နေပါသည်။",
84 "bluetooth": "ဘလူးတုသ်",
85 "bright_theme": "တောက်ပ",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "အခကြေးငွေကိုနှုတ်ယူခြင်း",
142 "confirm_fee_deduction_content": "output မှအခကြေးငွေကိုယူရန်သဘောတူပါသလား။",
143 "confirm_sending": "ပေးပို့အတည်ပြုပါ။",
144 + "confirm_silent_payments_switch_node": "လောလောဆယ်အသံတိတ်ငွေပေးချေမှုကိုစကင်ဖတ်စစ်ဆေးရန် node များကိုပြောင်းရန်လိုအပ်သည်",
145 "confirmations": "အတည်ပြုချက်များ",
146 "confirmed": "အတည်ပြုထားသော လက်ကျန်ငွေ",
147 "confirmed_tx": "အတည်ပြုသည်",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "သင်အသုံးပြုသည့်အချိန်တိုင်းတွင် ကျွန်ုပ်တို့သည် လိပ်စာအသစ်များကို ထုတ်ပေးသော်လည်း ယခင်လိပ်စာများသည် ဆက်လက်အလုပ်လုပ်နေပါသည်။",
225 "email_address": "အီးမေးလ်လိပ်စာ",
226 "enable_replace_by_fee": "အစားထိုး - by- အခကြေးငွေ enable",
227 + "enable_silent_payments_scanning": "အသံတိတ်ငွေပေးချေမှုကို scanable လုပ်ပါ",
228 "enabled": "ဖွင့်ထားသည်။",
229 "enter_amount": "ပမာဏကို ထည့်ပါ။",
230 "enter_backup_password": "အရန်စကားဝှက်ကို ဤနေရာတွင် ထည့်ပါ။",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "သင်သည် \n${recipient_name} သို့ ရန်ပုံငွေများ ပေးပို့ပါမည်",
283 "failed_authentication": "အထောက်အထားစိစစ်ခြင်း မအောင်မြင်ပါ။. ${state_error}",
284 "faq": "အမြဲမေးလေ့ရှိသောမေးခွန်းများ",
285 + "features": "အင်္ဂါရပ်များ",
286 "fetching": "ခေါ်ယူခြင်း။",
287 "fiat_api": "Fiat API",
288 "fiat_balance": "Fiat Balance",
@@ -531,6 +536,7 @@
536 "save_backup_password_alert": "အရန်စကားဝှက်ကို သိမ်းဆည်းပါ။",
537 "save_to_downloads": "ဒေါင်းလုဒ်များထံ သိမ်းဆည်းပါ။",
538 "saved_the_trade_id": "ကုန်သွယ်မှု ID ကို သိမ်းဆည်းပြီးပါပြီ။",
539 + "scan_one_block": "တစ်ကွက်ကိုစကင်ဖတ်စစ်ဆေးပါ",
540 "scan_qr_code": "QR ကုဒ်ကို စကင်န်ဖတ်ပါ။",
541 "scan_qr_code_to_get_address": "လိပ်စာရယူရန် QR ကုဒ်ကို စကင်န်ဖတ်ပါ။",
542 "scan_qr_on_device": "အခြားစက်တွင် ဤ QR ကုဒ်ကို စကင်ဖတ်ပါ။",
@@ -641,11 +647,22 @@
647 "sign_up": "ဆိုင်းအပ်",
648 "signTransaction": "ငွေလွှဲဝင်ပါ။",
649 "signup_for_card_accept_terms": "ကတ်အတွက် စာရင်းသွင်းပြီး စည်းကမ်းချက်များကို လက်ခံပါ။",
650 + "silent_payments": "အသံတိတ်ငွေပေးချေမှု",
651 + "silent_payments_always_scan": "အမြဲတမ်း scanning အမြဲ scanning",
652 + "silent_payments_disclaimer": "လိပ်စာအသစ်များသည်အထောက်အထားအသစ်များမဟုတ်ပါ။ ၎င်းသည်ကွဲပြားခြားနားသောတံဆိပ်ဖြင့်ရှိပြီးသားဝိသေသလက်ခဏာကိုပြန်လည်အသုံးပြုခြင်းဖြစ်သည်။",
653 + "silent_payments_display_card": "အသံတိတ်ငွေပေးချေမှုကဒ်ကိုပြပါ",
654 + "silent_payments_scan_from_date": "ရက်စွဲမှစကင်ဖတ်ပါ",
655 + "silent_payments_scan_from_date_or_blockheight": "ကျေးဇူးပြု. သင်ဝင်လာသောအသံတိတ်ငွေပေးချေမှုအတွက်သင်စကင်ဖတ်စစ်ဆေးလိုသည့်အမြင့်ကိုဖြည့်ပါ။ သို့မဟုတ်နေ့စွဲကိုသုံးပါ။ Wallet သည်လုပ်ကွက်တိုင်းကိုဆက်လက်စကင်ဖတ်စစ်ဆေးပါကသို့မဟုတ်သတ်မှတ်ထားသောအမြင့်ကိုသာစစ်ဆေးပါကသင်ရွေးချယ်နိုင်သည်။",
656 + "silent_payments_scan_from_height": "ပိတ်ပင်တားဆီးမှုအမြင့်ကနေ scan",
657 + "silent_payments_scanned_tip": "အစွန်အဖျားမှ scan ဖတ်! (${tip})",
658 + "silent_payments_scanning": "အသံတိတ်ငွေပေးချေမှု scanning",
659 + "silent_payments_settings": "အသံတိတ်ငွေပေးချေမှုဆက်တင်များ",
660 "slidable": "လျှောချနိုင်သည်။",
661 "sort_by": "အလိုက်စဥ်သည်",
662 "spend_key_private": "သော့သုံးရန် (သီးသန့်)",
663 "spend_key_public": "သုံးစွဲရန်သော့ (အများပြည်သူ)",
664 "status": "အခြေအနေ:",
665 + "string_default": "ပျက်ကွက်ခြင်း",
666 "subaddress_title": "လိပ်စာစာရင်း",
667 "subaddresses": "လိပ်စာများ",
668 "submit_request": "တောင်းဆိုချက်တစ်ခုတင်ပြပါ။",
@@ -670,10 +687,13 @@
687 "sync_status_starting_sync": "စင့်ခ်လုပ်ခြင်း။",
688 "sync_status_syncronized": "ထပ်တူပြုထားသည်။",
689 "sync_status_syncronizing": "ထပ်တူပြုခြင်း။",
690 + "sync_status_timed_out": "ထွက်အချိန်ကုန်",
691 + "sync_status_unsupported": "node မထောက်ပံ့ node ကို",
692 "syncing_wallet_alert_content": "သင်၏လက်ကျန်နှင့် ငွေပေးငွေယူစာရင်းသည် ထိပ်တွင် \"Synchronizeed\" ဟုပြောသည်အထိ မပြီးမြောက်နိုင်ပါ။ ပိုမိုလေ့လာရန် နှိပ်/နှိပ်ပါ။",
693 "syncing_wallet_alert_title": "သင့်ပိုက်ဆံအိတ်ကို စင့်ခ်လုပ်နေပါသည်။",
694 "template": "ပုံစံခွက်",
695 "template_name": "နမူနာပုံစံ",
696 + "testnet_coins_no_value": "Testnet ဒင်္ဂါးပြားတန်ဖိုးမရှိပါ",
697 "third_intro_content": "Yats သည် Cake Wallet အပြင်ဘက်တွင် နေထိုင်ပါသည်။ ကမ္ဘာပေါ်ရှိ မည်သည့်ပိုက်ဆံအိတ်လိပ်စာကို Yat ဖြင့် အစားထိုးနိုင်ပါသည်။",
698 "third_intro_title": "Yat သည် အခြားသူများနှင့် ကောင်းစွာကစားသည်။",
699 "thorchain_contract_address_not_supported": "Thorchain သည်စာချုပ်လိပ်စာသို့ပို့ခြင်းမပြုပါ",
@@ -749,13 +769,16 @@
769 "trusted": "ယုံတယ်။",
770 "tx_commit_exception_no_dust_on_change": "အဆိုပါငွေပေးငွေယူကဒီပမာဏနှင့်အတူပယ်ချခံရသည်။ ဤဒင်္ဂါးပြားများနှင့်အတူပြောင်းလဲမှုကိုပြန်လည်ပြောင်းလဲခြင်းသို့မဟုတ် ${min} မပါဘဲ ${max} ပေးပို့နိုင်သည်။",
771 "tx_commit_failed": "ငွေပေးငွေယူကျူးလွန်မှုပျက်ကွက်။ ကျေးဇူးပြုပြီးပံ့ပိုးမှုဆက်သွယ်ပါ။",
772 + "tx_invalid_input": "သင်သည်ဤငွေပေးချေမှုအမျိုးအစားအတွက်မှားယွင်းသော input type ကိုအသုံးပြုနေသည်",
773 "tx_no_dust_exception": "ငွေပမာဏကိုသေးငယ်လွန်းသောငွေပမာဏကိုပေးပို့ခြင်းဖြင့်ပယ်ဖျက်ခြင်းကိုငြင်းပယ်သည်။ ကျေးဇူးပြုပြီးငွေပမာဏကိုတိုးမြှင့်ကြိုးစားပါ။",
774 "tx_not_enough_inputs_exception": "အလုံအလောက်သွင်းအားစုများမလုံလောက်။ ကျေးဇူးပြုပြီးဒင်္ဂါးပြားထိန်းချုပ်မှုအောက်တွင်ပိုမိုရွေးချယ်ပါ",
775 + "tx_rejected_bip68_final": "ငွေပေးငွေယူသည်အတည်မပြုရသေးသောသွင်းအားစုများရှိပြီးအခကြေးငွေဖြင့်အစားထိုးရန်ပျက်ကွက်ခဲ့သည်။",
776 "tx_rejected_dust_change": "Network စည်းမျဉ်းစည်းကမ်းများဖြင့်ပယ်ဖျက်ခြင်းသည် Network စည်းမျဉ်းစည်းကမ်းများဖြင့်ငြင်းပယ်ခြင်း, အားလုံးပေးပို့ခြင်းသို့မဟုတ်ငွေပမာဏကိုလျှော့ချကြိုးစားပါ။",
777 "tx_rejected_dust_output": "Network စည်းမျဉ်းစည်းကမ်းများဖြင့် ပယ်ချ. ငွေပေးချေမှုသည် output output (ဖုန်မှုန့်) ဖြင့်ပယ်ချခဲ့သည်။ ကျေးဇူးပြုပြီးငွေပမာဏကိုတိုးမြှင့်ပေးပါ။",
778 "tx_rejected_dust_output_send_all": "Network စည်းမျဉ်းစည်းကမ်းများဖြင့် ပယ်ချ. ငွေပေးချေမှုသည် output output (ဖုန်မှုန့်) ဖြင့်ပယ်ချခဲ့သည်။ ဒင်္ဂါးပြားထိန်းချုပ်မှုအောက်တွင်ရွေးချယ်ထားသောဒင်္ဂါးများ၏လက်ကျန်ငွေကိုစစ်ဆေးပါ။",
779 "tx_rejected_vout_negative": "ဒီငွေပေးငွေယူရဲ့အခကြေးငွေအတွက်ပေးဆောင်ဖို့လုံလောက်တဲ့ဟန်ချက်မလုံလောက်။ ဒင်္ဂါးပြား၏လက်ကျန်ငွေလက်ကျန်ငွေကိုစစ်ဆေးပါ။",
780 "tx_wrong_balance_exception": "ဤငွေပမာဏကိုပေးပို့ရန်သင့်တွင် ${currency} မရှိပါ။",
781 + "tx_wrong_balance_with_amount_exception": "သင့်တွင်လုံလောက်မှုမရှိပါ${currency} ${amount}",
782 "tx_zero_fee_exception": "0 ကြေးနှင့်အတူငွေပေးငွေယူပေးပို့လို့မရပါဘူး။ နှုန်းကိုတိုးမြှင့်ခြင်းသို့မဟုတ်နောက်ဆုံးခန့်မှန်းချက်များအတွက်သင်၏ connection ကိုစစ်ဆေးပါ။",
783 "unavailable_balance": "လက်ကျန်ငွေ မရရှိနိုင်ပါ။",
784 "unavailable_balance_description": "မရရှိနိုင်သော လက်ကျန်ငွေ- ဤစုစုပေါင်းတွင် ဆိုင်းငံ့ထားသော ငွေပေးငွေယူများတွင် သော့ခတ်ထားသော ငွေကြေးများနှင့် သင်၏ coin ထိန်းချုပ်မှုဆက်တင်များတွင် သင် တက်ကြွစွာ အေးခဲထားသော ငွေများ ပါဝင်သည်။ သော့ခတ်ထားသော လက်ကျန်ငွေများကို ၎င်းတို့၏ သက်ဆိုင်ရာ ငွေပေးငွေယူများ ပြီးမြောက်သည်နှင့် တပြိုင်နက် ရရှိနိုင်မည်ဖြစ်ပြီး၊ အေးခဲထားသော လက်ကျန်များကို ၎င်းတို့အား ပြန်ဖြုတ်ရန် သင်ဆုံးဖြတ်သည်အထိ ငွေပေးငွေယူများအတွက် ဆက်လက်၍မရနိုင်ပါ။",
@@ -809,6 +832,7 @@
832 "warning": "သတိပေးချက်",
833 "welcome": "မှကြိုဆိုပါတယ်။",
834 "welcome_to_cakepay": "Cake Pay မှကြိုဆိုပါသည်။",
835 + "what_is_silent_payments": "အသံတိတ်ငွေပေးချေမှုဆိုတာဘာလဲ",
836 "widgets_address": "လိပ်စာ",
837 "widgets_or": "သို့မဟုတ်",
838 "widgets_restore_from_blockheight": "အမြင့်မှ ပြန်လည်ရယူပါ။",
res/values/strings_nl.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "Back-up",
72 "backup_file": "Backup bestand",
73 "backup_password": "Reservewachtwoord",
74 + "balance": "Evenwicht",
75 "balance_page": "Saldo pagina",
76 "bill_amount": "Bill bedrag",
77 "billing_address_info": "Als u om een ​​factuuradres wordt gevraagd, geef dan uw verzendadres op",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "Bitcoin donker thema",
80 "bitcoin_light_theme": "Bitcoin Light-thema",
81 "bitcoin_payments_require_1_confirmation": "Bitcoin-betalingen vereisen 1 bevestiging, wat 20 minuten of langer kan duren. Dank voor uw geduld! U ontvangt een e-mail wanneer de betaling is bevestigd.",
82 + "block_remaining": "1 blok resterend",
83 "Blocks_remaining": "${status} Resterende blokken",
84 "bluetooth": "Bluetooth",
85 "bright_theme": "Helder",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "Bevestig de aftrek van de kosten",
142 "confirm_fee_deduction_content": "Stemt u ermee in om de vergoeding af te trekken van de output?",
143 "confirm_sending": "Bevestig verzending",
144 + "confirm_silent_payments_switch_node": "Momenteel is het vereist om knooppunten te schakelen om stille betalingen te scannen",
145 "confirmations": "Bevestigingen",
146 "confirmed": "Bevestigd saldo",
147 "confirmed_tx": "Bevestigd",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "We genereren nieuwe adressen elke keer dat u er een gebruikt, maar eerdere adressen blijven werken",
225 "email_address": "E-mailadres",
226 "enable_replace_by_fee": "Schakel vervangen door een fee",
227 + "enable_silent_payments_scanning": "Schakel stille betalingen in scannen in",
228 "enabled": "Ingeschakeld",
229 "enter_amount": "Voer Bedrag in",
230 "enter_backup_password": "Voer hier een back-upwachtwoord in",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "U stuurt geld naar\n${recipient_name}",
283 "failed_authentication": "Mislukte authenticatie. ${state_error}",
284 "faq": "FAQ",
285 + "features": "Functies",
286 "fetching": "Ophalen",
287 "fiat_api": "Fiat API",
288 "fiat_balance": "Fiat Balans",
@@ -531,6 +536,7 @@
536 "save_backup_password_alert": "Bewaar back-upwachtwoord",
537 "save_to_downloads": "Opslaan in downloads",
538 "saved_the_trade_id": "Ik heb de ruil-ID opgeslagen",
539 + "scan_one_block": "Scan een blok",
540 "scan_qr_code": "Scan QR-code",
541 "scan_qr_code_to_get_address": "Scan de QR-code om het adres te krijgen",
542 "scan_qr_on_device": "Scan deze QR-code op een ander apparaat",
@@ -641,11 +647,22 @@
647 "sign_up": "Aanmelden",
648 "signTransaction": "Transactie ondertekenen",
649 "signup_for_card_accept_terms": "Meld je aan voor de kaart en accepteer de voorwaarden.",
650 + "silent_payments": "Stille betalingen",
651 + "silent_payments_always_scan": "Stel stille betalingen in het scannen",
652 + "silent_payments_disclaimer": "Nieuwe adressen zijn geen nieuwe identiteiten. Het is een hergebruik van een bestaande identiteit met een ander label.",
653 + "silent_payments_display_card": "Toon stille betalingskaart",
654 + "silent_payments_scan_from_date": "Scan vanaf datum",
655 + "silent_payments_scan_from_date_or_blockheight": "Voer de blokhoogte in die u wilt beginnen met scannen op inkomende stille betalingen, of gebruik in plaats daarvan de datum. U kunt kiezen of de portemonnee elk blok blijft scannen of alleen de opgegeven hoogte controleert.",
656 + "silent_payments_scan_from_height": "Scan van blokhoogte",
657 + "silent_payments_scanned_tip": "Gescand om te fooien! (${tip})",
658 + "silent_payments_scanning": "Stille betalingen scannen",
659 + "silent_payments_settings": "Stille betalingsinstellingen",
660 "slidable": "Verschuifbaar",
661 "sort_by": "Sorteer op",
662 "spend_key_private": "Sleutel uitgeven (privaat)",
663 "spend_key_public": "Sleutel uitgeven (openbaar)",
664 "status": "Staat: ",
665 + "string_default": "Standaard",
666 "subaddress_title": "Subadreslijst",
667 "subaddresses": "Subadressen",
668 "submit_request": "een verzoek indienen",
@@ -670,10 +687,13 @@
687 "sync_status_starting_sync": "BEGINNEN MET SYNCHRONISEREN",
688 "sync_status_syncronized": "SYNCHRONIZED",
689 "sync_status_syncronizing": "SYNCHRONISEREN",
690 + "sync_status_timed_out": "Uitgeput",
691 + "sync_status_unsupported": "Niet ondersteund knooppunt",
692 "syncing_wallet_alert_content": "Uw saldo- en transactielijst is mogelijk pas compleet als er bovenaan 'GESYNCHRONISEERD' staat. Klik/tik voor meer informatie.",
693 "syncing_wallet_alert_title": "Uw portemonnee wordt gesynchroniseerd",
694 "template": "Sjabloon",
695 "template_name": "Sjabloonnaam",
696 + "testnet_coins_no_value": "Testnet -munten hebben geen waarde",
697 "third_intro_content": "Yats wonen ook buiten Cake Wallet. Elk portemonnee-adres op aarde kan worden vervangen door een Yat!",
698 "third_intro_title": "Yat speelt leuk met anderen",
699 "thorchain_contract_address_not_supported": "Thorchain ondersteunt het verzenden niet naar een contractadres",
@@ -749,13 +769,16 @@
769 "trusted": "vertrouwd",
770 "tx_commit_exception_no_dust_on_change": "De transactie wordt afgewezen met dit bedrag. Met deze munten kunt u ${min} verzenden zonder verandering of ${max} die wijziging retourneert.",
771 "tx_commit_failed": "Transactiebewissing is mislukt. Neem contact op met de ondersteuning.",
772 + "tx_invalid_input": "U gebruikt het verkeerde invoertype voor dit type betaling",
773 "tx_no_dust_exception": "De transactie wordt afgewezen door een te klein bedrag te verzenden. Probeer het bedrag te verhogen.",
774 "tx_not_enough_inputs_exception": "Niet genoeg ingangen beschikbaar. Selecteer meer onder muntenbesturing",
775 + "tx_rejected_bip68_final": "Transactie heeft onbevestigde ingangen en niet vervangen door vergoeding.",
776 "tx_rejected_dust_change": "Transactie afgewezen door netwerkregels, laag wijzigingsbedrag (stof). Probeer alles te verzenden of het bedrag te verminderen.",
777 "tx_rejected_dust_output": "Transactie afgewezen door netwerkregels, laag outputbedrag (stof). Verhoog het bedrag.",
778 "tx_rejected_dust_output_send_all": "Transactie afgewezen door netwerkregels, laag outputbedrag (stof). Controleer het saldo van munten die zijn geselecteerd onder muntcontrole.",
779 "tx_rejected_vout_negative": "Niet genoeg saldo om te betalen voor de kosten van deze transactie. Controleer het saldo van munten onder muntcontrole.",
780 "tx_wrong_balance_exception": "Je hebt niet genoeg ${currency} om dit bedrag te verzenden.",
781 + "tx_wrong_balance_with_amount_exception": "Je hebt niet genoeg ${currency} om de totale hoeveelheid ${amount} te verzenden",
782 "tx_zero_fee_exception": "Kan geen transactie verzenden met 0 kosten. Probeer het tarief te verhogen of uw verbinding te controleren op de laatste schattingen.",
783 "unavailable_balance": "Onbeschikbaar saldo",
784 "unavailable_balance_description": "Niet-beschikbaar saldo: Dit totaal omvat het geld dat is vergrendeld in lopende transacties en het geld dat u actief hebt bevroren in uw muntcontrole-instellingen. Vergrendelde saldi komen beschikbaar zodra de betreffende transacties zijn voltooid, terwijl bevroren saldi ontoegankelijk blijven voor transacties totdat u besluit ze weer vrij te geven.",
@@ -810,6 +833,7 @@
833 "warning": "Waarschuwing",
834 "welcome": "Welkom bij",
835 "welcome_to_cakepay": "Welkom bij Cake Pay!",
836 + "what_is_silent_payments": "Wat zijn stille betalingen?",
837 "widgets_address": "Adres",
838 "widgets_or": "of",
839 "widgets_restore_from_blockheight": "Herstel vanaf blockheight",
res/values/strings_pl.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "Kopia zapasowa",
72 "backup_file": "Plik kopii zapasowej",
73 "backup_password": "Hasło kpoii zapasowej",
74 + "balance": "Balansować",
75 "balance_page": "Strona salda",
76 "bill_amount": "Kwota rachunku",
77 "billing_address_info": "Jeśli zostaniesz poproszony o podanie adresu rozliczeniowego, podaj swój adres wysyłki",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "Ciemny motyw Bitcoina",
80 "bitcoin_light_theme": "Lekki motyw Bitcoin",
81 "bitcoin_payments_require_1_confirmation": "Płatności Bitcoin wymagają 1 potwierdzenia, co może zająć 20 minut lub dłużej. Dziękuję za cierpliwość! Otrzymasz wiadomość e-mail, gdy płatność zostanie potwierdzona.",
82 + "block_remaining": "1 blok pozostałym",
83 "Blocks_remaining": "Pozostało ${status} bloków",
84 "bluetooth": "Bluetooth",
85 "bright_theme": "Biały",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "Potwierdź odliczenie opłaty",
142 "confirm_fee_deduction_content": "Czy zgadzasz się odliczyć opłatę od wyników?",
143 "confirm_sending": "Potwierdź wysłanie",
144 + "confirm_silent_payments_switch_node": "Obecnie wymagane jest zmiana węzłów w celu skanowania cichych płatności",
145 "confirmations": "Potwierdzenia",
146 "confirmed": "Potwierdzone saldo",
147 "confirmed_tx": "Potwierdzony",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "Za każdym razem, gdy wykorzystasz adres, dla wiekszej prywatności generujemy nowy, ale poprzednie adresy nadal działają, i moga odbierać środki",
225 "email_address": "Adres e-mail",
226 "enable_replace_by_fee": "Włącz wymianę po lewej",
227 + "enable_silent_payments_scanning": "Włącz skanowanie cichych płatności",
228 "enabled": "Włączone",
229 "enter_amount": "Wprowadź kwotę",
230 "enter_backup_password": "Wprowadź tutaj hasło kopii zapasowej",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "Wysyłasz środki na\n${recipient_name}",
283 "failed_authentication": "Nieudane uwierzytelnienie. ${state_error}",
284 "faq": "FAQ",
285 + "features": "Cechy",
286 "fetching": "Pobieranie",
287 "fiat_api": "API Walut FIAT",
288 "fiat_balance": "Bilans Fiata",
@@ -531,6 +536,7 @@
536 "save_backup_password_alert": "Zapisz hasło kopii zapasowej",
537 "save_to_downloads": "Zapisz w Pobranych",
538 "saved_the_trade_id": "Zapisałem ID",
539 + "scan_one_block": "Zeskanuj jeden blok",
540 "scan_qr_code": "Skanowania QR code",
541 "scan_qr_code_to_get_address": "Zeskanuj kod QR, aby uzyskać adres",
542 "scan_qr_on_device": "Zeskanuj ten kod QR na innym urządzeniu",
@@ -641,11 +647,22 @@
647 "sign_up": "Zarejestruj się",
648 "signTransaction": "Podpisz transakcję",
649 "signup_for_card_accept_terms": "Zarejestruj się, aby otrzymać kartę i zaakceptuj warunki.",
650 + "silent_payments": "Ciche płatności",
651 + "silent_payments_always_scan": "Ustaw ciche płatności zawsze skanowanie",
652 + "silent_payments_disclaimer": "Nowe adresy nie są nową tożsamością. Jest to ponowne wykorzystanie istniejącej tożsamości z inną etykietą.",
653 + "silent_payments_display_card": "Pokaż kartę Silent Payments",
654 + "silent_payments_scan_from_date": "Skanuj z daty",
655 + "silent_payments_scan_from_date_or_blockheight": "Wprowadź wysokość bloku, którą chcesz rozpocząć skanowanie w poszukiwaniu cichej płatności lub zamiast tego skorzystaj z daty. Możesz wybrać, czy portfel kontynuuje skanowanie każdego bloku, lub sprawdza tylko określoną wysokość.",
656 + "silent_payments_scan_from_height": "Skanuj z wysokości bloku",
657 + "silent_payments_scanned_tip": "Zeskanowany do napiwku! (${tip})",
658 + "silent_payments_scanning": "Skanowanie cichych płatności",
659 + "silent_payments_settings": "Ustawienia o cichej płatności",
660 "slidable": "Przesuwne",
661 "sort_by": "Sortuj według",
662 "spend_key_private": "Klucz prywatny",
663 "spend_key_public": "Klucz publiczny",
664 "status": "Status: ",
665 + "string_default": "Domyślny",
666 "subaddress_title": "Lista podadresów",
667 "subaddresses": "Podadresy",
668 "submit_request": "Złóż wniosek",
@@ -670,10 +687,13 @@
687 "sync_status_starting_sync": "ROZPOCZĘCIE SYNCHRONIZACJI",
688 "sync_status_syncronized": "ZSYNCHRONIZOWANO",
689 "sync_status_syncronizing": "SYNCHRONIZACJA",
690 + "sync_status_timed_out": "PRZEKROCZONO LIMIT CZASU",
691 + "sync_status_unsupported": "Nieobsługiwany węzeł",
692 "syncing_wallet_alert_content": "Twoje saldo i lista transakcji mogą nie być kompletne, dopóki u góry nie pojawi się napis „SYNCHRONIZOWANY”. Kliknij/stuknij, aby dowiedzieć się więcej.",
693 "syncing_wallet_alert_title": "Twój portfel się synchronizuje",
694 "template": "Szablon",
695 "template_name": "Nazwa szablonu",
696 + "testnet_coins_no_value": "Monety testowe nie mają wartości",
697 "third_intro_content": "Yats mieszkają również poza Cake Wallet. Każdy adres portfela na ziemi można zastąpić Yat!",
698 "third_intro_title": "Yat ładnie bawi się z innymi",
699 "thorchain_contract_address_not_supported": "Thorchain nie wspiera wysyłania na adres umowy",
@@ -749,13 +769,16 @@
769 "trusted": "Zaufany",
770 "tx_commit_exception_no_dust_on_change": "Transakcja jest odrzucana z tą kwotą. Za pomocą tych monet możesz wysłać ${min} bez zmiany lub ${max}, które zwraca zmianę.",
771 "tx_commit_failed": "Zatwierdzenie transakcji nie powiodło się. Skontaktuj się z obsługą.",
772 + "tx_invalid_input": "Używasz niewłaściwego typu wejściowego dla tego rodzaju płatności",
773 "tx_no_dust_exception": "Transakcja jest odrzucana przez wysyłanie zbyt małej ilości. Spróbuj zwiększyć kwotę.",
774 "tx_not_enough_inputs_exception": "Za mało dostępnych danych wejściowych. Wybierz więcej pod kontrolą monet",
775 + "tx_rejected_bip68_final": "Transakcja niepotwierdza wejściów i nie zastąpiła opłaty.",
776 "tx_rejected_dust_change": "Transakcja odrzucona według reguł sieciowych, niska ilość zmiany (kurz). Spróbuj wysłać całość lub zmniejszyć kwotę.",
777 "tx_rejected_dust_output": "Transakcja odrzucona według reguł sieciowych, niskiej ilości wyjściowej (pyłu). Zwiększ kwotę.",
778 "tx_rejected_dust_output_send_all": "Transakcja odrzucona według reguł sieciowych, niskiej ilości wyjściowej (pyłu). Sprawdź saldo monet wybranych pod kontrolą monet.",
779 "tx_rejected_vout_negative": "Za mało salda, aby zapłacić za opłaty tej transakcji. Sprawdź saldo monet pod kontrolą monet.",
780 "tx_wrong_balance_exception": "Nie masz wystarczającej ilości ${currency}, aby wysłać tę kwotę.",
781 + "tx_wrong_balance_with_amount_exception": "Nie masz wystarczająco dużo ${currency} aby wysłać całkowitą ilość ${amount}",
782 "tx_zero_fee_exception": "Nie można wysłać transakcji z 0 opłatą. Spróbuj zwiększyć stawkę lub sprawdzić połączenie w poszukiwaniu najnowszych szacunków.",
783 "unavailable_balance": "Niedostępne saldo",
784 "unavailable_balance_description": "Niedostępne saldo: Suma ta obejmuje środki zablokowane w transakcjach oczekujących oraz te, które aktywnie zamroziłeś w ustawieniach kontroli monet. Zablokowane salda staną się dostępne po zakończeniu odpowiednich transakcji, natomiast zamrożone salda pozostaną niedostępne dla transakcji, dopóki nie zdecydujesz się ich odblokować.",
@@ -809,6 +832,7 @@
832 "warning": "Ostrzeżenie",
833 "welcome": "Witamy w",
834 "welcome_to_cakepay": "Witamy w Cake Pay!",
835 + "what_is_silent_payments": "Co to są ciche płatności?",
836 "widgets_address": "Adres",
837 "widgets_or": "lub",
838 "widgets_restore_from_blockheight": "Przywróć z wysokości bloku",
res/values/strings_pt.arb
+26 -2
@@ -71,6 +71,7 @@
71 "backup": "Cópia de segurança",
72 "backup_file": "Arquivo de backup",
73 "backup_password": "Senha de backup",
74 + "balance": "Equilíbrio",
75 "balance_page": "Página de saldo",
76 "bill_amount": "Valor da conta",
77 "billing_address_info": "Se for solicitado um endereço de cobrança, forneça seu endereço de entrega",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "Tema escuro Bitcoin",
80 "bitcoin_light_theme": "Tema claro de bitcoin",
81 "bitcoin_payments_require_1_confirmation": "Os pagamentos em Bitcoin exigem 1 confirmação, o que pode levar 20 minutos ou mais. Obrigado pela sua paciência! Você receberá um e-mail quando o pagamento for confirmado.",
82 + "block_remaining": "1 bloco restante",
83 "Blocks_remaining": "${status} blocos restantes",
84 "bluetooth": "Bluetooth",
85 "bright_theme": "Brilhante",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "Confirme dedução da taxa",
142 "confirm_fee_deduction_content": "Você concorda em deduzir a taxa da saída?",
143 "confirm_sending": "Confirmar o envio",
144 + "confirm_silent_payments_switch_node": "Atualmente, é necessário trocar de nós para digitalizar pagamentos silenciosos",
145 "confirmations": "Confirmações",
146 "confirmed": "Saldo Confirmado",
147 "confirmed_tx": "Confirmado",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "Geramos novos endereços cada vez que você usa um, mas os endereços anteriores continuam funcionando",
225 "email_address": "Endereço de e-mail",
226 "enable_replace_by_fee": "Habilite substituir por taxa",
227 + "enable_silent_payments_scanning": "Ativar escaneamento de pagamentos silenciosos",
228 "enabled": "Habilitado",
229 "enter_amount": "Digite o valor",
230 "enter_backup_password": "Digite a senha de backup aqui",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "Você enviará fundos para\n${recipient_name}",
283 "failed_authentication": "Falha na autenticação. ${state_error}",
284 "faq": "FAQ",
285 + "features": "Funcionalidades",
286 "fetching": "Buscando",
287 "fiat_api": "API da Fiat",
288 "fiat_balance": "Equilíbrio Fiat",
@@ -533,6 +538,7 @@
538 "save_backup_password_alert": "Salvar senha de backup",
539 "save_to_downloads": "Salvar em Downloads",
540 "saved_the_trade_id": "ID da troca salvo",
541 + "scan_one_block": "Escanear um bloco",
542 "scan_qr_code": "Escanear código QR",
543 "scan_qr_code_to_get_address": "Digitalize o código QR para obter o endereço",
544 "scan_qr_on_device": "Digitalize este código QR em outro dispositivo",
@@ -643,11 +649,22 @@
649 "sign_up": "Inscrever-se",
650 "signTransaction": "Assinar transação",
651 "signup_for_card_accept_terms": "Cadastre-se no cartão e aceite os termos.",
652 + "silent_payments": "Pagamentos silenciosos",
653 + "silent_payments_always_scan": "Defina pagamentos silenciosos sempre digitalizando",
654 + "silent_payments_disclaimer": "Novos endereços não são novas identidades. É uma reutilização de uma identidade existente com um rótulo diferente.",
655 + "silent_payments_display_card": "Mostrar cartão de pagamento silencioso",
656 + "silent_payments_scan_from_date": "Escanear a partir da data",
657 + "silent_payments_scan_from_date_or_blockheight": "Por favor, insira a altura do bloco que deseja iniciar o escaneamento para obter pagamentos silenciosos ou use a data. Você pode escolher se a carteira continua escaneando cada bloco ou verifica apenas a altura especificada.",
658 + "silent_payments_scan_from_height": "Escanear a partir da altura do bloco",
659 + "silent_payments_scanned_tip": "Escaneado até o fim! (${tip})",
660 + "silent_payments_scanning": "Escanear Pagamentos Silenciosos",
661 + "silent_payments_settings": "Configurações de pagamentos silenciosos",
662 "slidable": "Deslizável",
663 "sort_by": "Ordenar por",
664 "spend_key_private": "Chave de gastos (privada)",
665 "spend_key_public": "Chave de gastos (pública)",
666 "status": "Status: ",
667 + "string_default": "Padrão",
668 "subaddress_title": "Sub-endereços",
669 "subaddresses": "Sub-endereços",
670 "submit_request": "enviar um pedido",
@@ -672,10 +689,13 @@
689 "sync_status_starting_sync": "INICIANDO SINCRONIZAÇÃO",
690 "sync_status_syncronized": "SINCRONIZADO",
691 "sync_status_syncronizing": "SINCRONIZANDO",
692 + "sync_status_timed_out": "TEMPO ESGOTADO",
693 + "sync_status_unsupported": "Nó não suportado",
694 "syncing_wallet_alert_content": "Seu saldo e lista de transações podem não estar completos até que diga “SYNCHRONIZED” no topo. Clique/toque para saber mais.",
695 "syncing_wallet_alert_title": "Sua carteira está sincronizando",
696 "template": "Modelo",
697 "template_name": "Nome do modelo",
698 + "testnet_coins_no_value": "As moedas de teste não têm valor",
699 "third_intro_content": "Yats também mora fora da Cake Wallet. Qualquer endereço de carteira na Terra pode ser substituído por um Yat!",
700 "third_intro_title": "Yat joga bem com os outros",
701 "thorchain_contract_address_not_supported": "Thorchain não suporta o envio para um endereço de contrato",
@@ -751,20 +771,23 @@
771 "trusted": "confiável",
772 "tx_commit_exception_no_dust_on_change": "A transação é rejeitada com esse valor. Com essas moedas, você pode enviar ${min} sem alteração ou ${max} que retorna alterações.",
773 "tx_commit_failed": "A confirmação da transação falhou. Entre em contato com o suporte.",
774 + "tx_invalid_input": "Você está usando o tipo de entrada errado para este tipo de pagamento",
775 "tx_no_dust_exception": "A transação é rejeitada enviando uma quantia pequena demais. Por favor, tente aumentar o valor.",
776 "tx_not_enough_inputs_exception": "Não há entradas disponíveis. Selecione mais sob controle de moedas",
777 + "tx_rejected_bip68_final": "A transação tem entradas não confirmadas e não substituiu por taxa.",
778 "tx_rejected_dust_change": "Transação rejeitada pelas regras de rede, baixa quantidade de troco (poeira). Tente enviar tudo ou reduzir o valor.",
779 "tx_rejected_dust_output": "Transação rejeitada por regras de rede, baixa quantidade de saída (poeira). Por favor, aumente o valor.",
780 "tx_rejected_dust_output_send_all": "Transação rejeitada por regras de rede, baixa quantidade de saída (poeira). Por favor, verifique o saldo de moedas selecionadas sob controle de moedas.",
781 "tx_rejected_vout_negative": "Não há saldo suficiente para pagar as taxas desta transação. Por favor, verifique o saldo de moedas sob controle de moedas.",
782 "tx_wrong_balance_exception": "Você não tem o suficiente ${currency} para enviar esse valor.",
783 + "tx_wrong_balance_with_amount_exception": "Você não tem o suficiente ${currency} para enviar o valor total de ${amount}",
784 "tx_zero_fee_exception": "Não pode enviar transação com taxa 0. Tente aumentar a taxa ou verificar sua conexão para obter as estimativas mais recentes.",
785 "unavailable_balance": "Saldo indisponível",
786 "unavailable_balance_description": "Saldo Indisponível: Este total inclui fundos bloqueados em transações pendentes e aqueles que você congelou ativamente nas configurações de controle de moedas. Os saldos bloqueados ficarão disponíveis assim que suas respectivas transações forem concluídas, enquanto os saldos congelados permanecerão inacessíveis para transações até que você decida descongelá-los.",
787 "unconfirmed": "Saldo não confirmado",
788 "understand": "Entendo",
789 "unmatched_currencies": "A moeda da sua carteira atual não corresponde à do QR digitalizado",
767 - "unspent_change": "Mudar",
790 + "unspent_change": "Troco",
791 "unspent_coins_details_title": "Detalhes de moedas não gastas",
792 "unspent_coins_title": "Moedas não gastas",
793 "unsupported_asset": "Não oferecemos suporte a esta ação para este recurso. Crie ou mude para uma carteira de um tipo de ativo compatível.",
@@ -812,6 +835,7 @@
835 "warning": "Aviso",
836 "welcome": "Bem-vindo ao",
837 "welcome_to_cakepay": "Bem-vindo ao Cake Pay!",
838 + "what_is_silent_payments": "O que são pagamentos silenciosos?",
839 "widgets_address": "Endereço",
840 "widgets_or": "ou",
841 "widgets_restore_from_blockheight": "Restaurar a partir de altura do bloco",
@@ -840,4 +864,4 @@
864 "you_will_get": "Converter para",
865 "you_will_send": "Converter de",
866 "yy": "aa"
843 -}
\ No newline at end of file
867 +}
res/values/strings_ru.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "Резервная копия",
72 "backup_file": "Файл резервной копии",
73 "backup_password": "Пароль резервной копии",
74 + "balance": "Баланс",
75 "balance_page": "Страница баланса",
76 "bill_amount": "Сумма счета",
77 "billing_address_info": "Если вас попросят указать платежный адрес, укажите адрес доставки",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "Биткойн Темная тема",
80 "bitcoin_light_theme": "Светлая биткойн-тема",
81 "bitcoin_payments_require_1_confirmation": "Биткойн-платежи требуют 1 подтверждения, что может занять 20 минут или дольше. Спасибо тебе за твое терпение! Вы получите электронное письмо, когда платеж будет подтвержден.",
82 + "block_remaining": "1 Блок остался",
83 "Blocks_remaining": "${status} Осталось блоков",
84 "bluetooth": "Bluetooth",
85 "bright_theme": "Яркая",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "Подтвердите вычет платы",
142 "confirm_fee_deduction_content": "Согласны ли вы вычесть плату из вывода?",
143 "confirm_sending": "Подтвердить отправку",
144 + "confirm_silent_payments_switch_node": "В настоящее время требуется переключение узлов для сканирования молчаливых платежей",
145 "confirmations": "Подтверждения",
146 "confirmed": "Подтвержденный баланс",
147 "confirmed_tx": "Подтвержденный",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "Мы генерируем новые адреса каждый раз, когда вы их используете, но предыдущие адреса продолжают работать.",
225 "email_address": "Адрес электронной почты",
226 "enable_replace_by_fee": "Включить замену за пикой",
227 + "enable_silent_payments_scanning": "Включить сканирование безмолвных платежей",
228 "enabled": "Включено",
229 "enter_amount": "Введите сумму",
230 "enter_backup_password": "Введите пароль резервной копии",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "Вы будете отправлять средства\n${recipient_name}",
283 "failed_authentication": "Ошибка аутентификации. ${state_error}",
284 "faq": "FAQ",
285 + "features": "Функции",
286 "fetching": "Загрузка",
287 "fiat_api": "Фиат API",
288 "fiat_balance": "Фиатный баланс",
@@ -532,6 +537,7 @@
537 "save_backup_password_alert": "Сохранить пароль резервной копии",
538 "save_to_downloads": "Сохранить в загрузках",
539 "saved_the_trade_id": "Я сохранил ID сделки",
540 + "scan_one_block": "Сканируйте один блок",
541 "scan_qr_code": "Сканировать QR-код",
542 "scan_qr_code_to_get_address": "Отсканируйте QR-код для получения адреса",
543 "scan_qr_on_device": "Отсканируйте этот QR-код на другом устройстве",
@@ -642,11 +648,22 @@
648 "sign_up": "Зарегистрироваться",
649 "signTransaction": "Подписать транзакцию",
650 "signup_for_card_accept_terms": "Подпишитесь на карту и примите условия.",
651 + "silent_payments": "Молчаливые платежи",
652 + "silent_payments_always_scan": "Установить молчаливые платежи всегда сканирование",
653 + "silent_payments_disclaimer": "Новые адреса не являются новыми личностями. Это повторное использование существующей идентичности с другой этикеткой.",
654 + "silent_payments_display_card": "Показать бесшумную платежную карту",
655 + "silent_payments_scan_from_date": "Сканирование с даты",
656 + "silent_payments_scan_from_date_or_blockheight": "Пожалуйста, введите высоту блока, которую вы хотите начать сканирование для входящих молчаливых платежей, или вместо этого используйте дату. Вы можете выбрать, продолжает ли кошелек сканировать каждый блок или проверять только указанную высоту.",
657 + "silent_payments_scan_from_height": "Сканирование с высоты блока",
658 + "silent_payments_scanned_tip": "Оканируется, чтобы чаевые! (${tip})",
659 + "silent_payments_scanning": "Сканирование безмолвных платежей",
660 + "silent_payments_settings": "Silent Payments Settings",
661 "slidable": "Скользящий",
662 "sort_by": "Сортировать по",
663 "spend_key_private": "Приватный ключ траты",
664 "spend_key_public": "Публичный ключ траты",
665 "status": "Статус: ",
666 + "string_default": "По умолчанию",
667 "subaddress_title": "Список субадресов",
668 "subaddresses": "Субадреса",
669 "submit_request": "отправить запрос",
@@ -671,10 +688,13 @@
688 "sync_status_starting_sync": "НАЧАЛО СИНХРОНИЗАЦИИ",
689 "sync_status_syncronized": "СИНХРОНИЗИРОВАН",
690 "sync_status_syncronizing": "СИНХРОНИЗАЦИЯ",
691 + "sync_status_timed_out": "ВРЕМЯ ВЫШЛО",
692 + "sync_status_unsupported": "Неподдерживаемый узел",
693 "syncing_wallet_alert_content": "Ваш баланс и список транзакций могут быть неполными, пока вверху не будет написано «СИНХРОНИЗИРОВАНО». Щелкните/коснитесь, чтобы узнать больше.",
694 "syncing_wallet_alert_title": "Ваш кошелек синхронизируется",
695 "template": "Шаблон",
696 "template_name": "Имя Шаблона",
697 + "testnet_coins_no_value": "Монеты теста не имеют значения",
698 "third_intro_content": "Yat находятся за пределами Cake Wallet. Любой адрес кошелька на земле можно заменить на Yat!",
699 "third_intro_title": "Yat хорошо взаимодействует с другими",
700 "thorchain_contract_address_not_supported": "Thorchain не поддерживает отправку на адрес контракта",
@@ -750,13 +770,16 @@
770 "trusted": "доверенный",
771 "tx_commit_exception_no_dust_on_change": "Транзакция отклоняется с этой суммой. С этими монетами вы можете отправлять ${min} без изменения или ${max}, которые возвращают изменение.",
772 "tx_commit_failed": "Комплект транзакции не удался. Пожалуйста, свяжитесь с поддержкой.",
773 + "tx_invalid_input": "Вы используете неправильный тип ввода для этого типа оплаты",
774 "tx_no_dust_exception": "Транзакция отклоняется путем отправки слишком маленькой суммы. Пожалуйста, попробуйте увеличить сумму.",
775 "tx_not_enough_inputs_exception": "Недостаточно входов доступны. Пожалуйста, выберите больше под контролем монет",
776 + "tx_rejected_bip68_final": "Транзакция имеет неподтвержденные входные данные и не смогли заменить на плату.",
777 "tx_rejected_dust_change": "Транзакция отклоняется в соответствии с правилами сети, низкой суммой изменений (пыль). Попробуйте отправить все или уменьшить сумму.",
778 "tx_rejected_dust_output": "Транзакция отклоняется в соответствии с правилами сети, низкой выходной суммой (пыль). Пожалуйста, увеличьте сумму.",
779 "tx_rejected_dust_output_send_all": "Транзакция отклоняется в соответствии с правилами сети, низкой выходной суммой (пыль). Пожалуйста, проверьте баланс монет, выбранных под контролем монет.",
780 "tx_rejected_vout_negative": "Недостаточно баланс, чтобы оплатить плату этой транзакции. Пожалуйста, проверьте баланс монет под контролем монет.",
781 "tx_wrong_balance_exception": "У вас не хватает ${currency}, чтобы отправить эту сумму.",
782 + "tx_wrong_balance_with_amount_exception": "У вас недостаточно ${currency}, чтобы отправить общее количество ${amount}",
783 "tx_zero_fee_exception": "Не может отправить транзакцию с платой 0. Попробуйте увеличить ставку или проверить соединение на наличие последних оценок.",
784 "unavailable_balance": "Недоступный баланс",
785 "unavailable_balance_description": "Недоступный баланс: в эту сумму входят средства, заблокированные в ожидающих транзакциях, и средства, которые вы активно заморозили в настройках управления монетами. Заблокированные балансы станут доступны после завершения соответствующих транзакций, а замороженные балансы останутся недоступными для транзакций, пока вы не решите их разморозить.",
@@ -810,6 +833,7 @@
833 "warning": "Предупреждение",
834 "welcome": "Приветствуем в",
835 "welcome_to_cakepay": "Добро пожаловать в Cake Pay!",
836 + "what_is_silent_payments": "Что такое молчаливые платежи?",
837 "widgets_address": "Адрес",
838 "widgets_or": "или",
839 "widgets_restore_from_blockheight": "Восстановить на высоте блока",
res/values/strings_th.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "สำรองข้อมูล",
72 "backup_file": "ไฟล์สำรองข้อมูล",
73 "backup_password": "รหัสผ่านสำรองข้อมูล",
74 + "balance": "สมดุล",
75 "balance_page": "หน้ายอดคงเหลือ",
76 "bill_amount": "จำนวนบิล",
77 "billing_address_info": "ถ้าถูกร้องขอที่อยู่สำหรับการวางบิล ให้ใช้ที่อยู่จัดส่งของคุณ",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "ธีมมืด Bitcoin",
80 "bitcoin_light_theme": "ธีมแสง Bitcoin",
81 "bitcoin_payments_require_1_confirmation": "การชำระเงินด้วย Bitcoin ต้องการการยืนยัน 1 ครั้ง ซึ่งอาจใช้เวลา 20 นาทีหรือนานกว่านั้น ขอบคุณสำหรับความอดทนของคุณ! คุณจะได้รับอีเมลเมื่อการชำระเงินได้รับการยืนยัน",
82 + "block_remaining": "เหลือ 1 บล็อก",
83 "Blocks_remaining": "${status} บล็อกที่เหลืออยู่",
84 "bluetooth": "บลูทู ธ",
85 "bright_theme": "สดใส",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "ยืนยันการหักค่าธรรมเนียม",
142 "confirm_fee_deduction_content": "คุณตกลงที่จะหักค่าธรรมเนียมจากผลลัพธ์หรือไม่?",
143 "confirm_sending": "ยืนยันการส่ง",
144 + "confirm_silent_payments_switch_node": "ขณะนี้จำเป็นต้องเปลี่ยนโหนดเพื่อสแกนการชำระเงินแบบเงียบ",
145 "confirmations": "การยืนยัน",
146 "confirmed": "ยอดคงเหลือที่ยืนยันแล้ว",
147 "confirmed_tx": "ซึ่งยืนยันแล้ว",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "เราสร้างที่อยู่ใหม่ทุกครั้งที่คุณใช้หนึ่งอย่าง แต่ที่อยู่เก่ายังสามารถใช้ได้ต่อไป",
225 "email_address": "ที่อยู่อีเมล",
226 "enable_replace_by_fee": "เปิดใช้งานการเปลี่ยนโดยค่าธรรมเนียม",
227 + "enable_silent_payments_scanning": "เปิดใช้งานการสแกนการชำระเงินแบบเงียบ",
228 "enabled": "เปิดใช้งาน",
229 "enter_amount": "กรอกจำนวน",
230 "enter_backup_password": "ป้อนรหัสผ่านสำรองที่นี่",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "คุณกำลังจะส่งเงินไปยัง\n${recipient_name}",
283 "failed_authentication": "การยืนยันสิทธิ์ล้มเหลว ${state_error}",
284 "faq": "คำถามที่พบบ่อย",
285 + "features": "คุณสมบัติ",
286 "fetching": "กำลังโหลด",
287 "fiat_api": "API สกุลเงินตรา",
288 "fiat_balance": "เฟียต บาลานซ์",
@@ -531,6 +536,7 @@
536 "save_backup_password_alert": "บันทึกรหัสผ่านสำรอง",
537 "save_to_downloads": "บันทึกลงดาวน์โหลด",
538 "saved_the_trade_id": "ฉันได้บันทึก ID ของการซื้อขายแล้ว",
539 + "scan_one_block": "สแกนหนึ่งบล็อก",
540 "scan_qr_code": "สแกนรหัส QR",
541 "scan_qr_code_to_get_address": "สแกน QR code เพื่อรับที่อยู่",
542 "scan_qr_on_device": "สแกนโค้ด QR นี้บนอุปกรณ์อื่น",
@@ -641,11 +647,22 @@
647 "sign_up": "สมัครสมาชิก",
648 "signTransaction": "ลงนามในการทำธุรกรรม",
649 "signup_for_card_accept_terms": "ลงทะเบียนสำหรับบัตรและยอมรับเงื่อนไข",
650 + "silent_payments": "การชำระเงินเงียบ",
651 + "silent_payments_always_scan": "ตั้งค่าการชำระเงินแบบเงียบเสมอ",
652 + "silent_payments_disclaimer": "ที่อยู่ใหม่ไม่ใช่ตัวตนใหม่ มันเป็นการใช้ซ้ำของตัวตนที่มีอยู่ด้วยฉลากที่แตกต่างกัน",
653 + "silent_payments_display_card": "แสดงบัตร Silent Payments",
654 + "silent_payments_scan_from_date": "สแกนตั้งแต่วันที่",
655 + "silent_payments_scan_from_date_or_blockheight": "โปรดป้อนความสูงของบล็อกที่คุณต้องการเริ่มการสแกนสำหรับการชำระเงินแบบเงียบ ๆ หรือใช้วันที่แทน คุณสามารถเลือกได้ว่ากระเป๋าเงินยังคงสแกนทุกบล็อกหรือตรวจสอบความสูงที่ระบุเท่านั้น",
656 + "silent_payments_scan_from_height": "สแกนจากความสูงของบล็อก",
657 + "silent_payments_scanned_tip": "สแกนไปที่ปลาย! (${tip})",
658 + "silent_payments_scanning": "การสแกนการชำระเงินแบบเงียบ",
659 + "silent_payments_settings": "การตั้งค่าการชำระเงินแบบเงียบ",
660 "slidable": "เลื่อนได้",
661 "sort_by": "เรียงตาม",
662 "spend_key_private": "คีย์จ่าย (ส่วนตัว)",
663 "spend_key_public": "คีย์จ่าย (สาธารณะ)",
664 "status": "สถานะ: ",
665 + "string_default": "ค่าเริ่มต้น",
666 "subaddress_title": "รายการที่อยู่ย่อย",
667 "subaddresses": "ที่อยู่ย่อย",
668 "submit_request": "ส่งคำขอ",
@@ -670,10 +687,13 @@
687 "sync_status_starting_sync": "กำลังเริ่มซิงโครไนซ์",
688 "sync_status_syncronized": "ซิงโครไนซ์แล้ว",
689 "sync_status_syncronizing": "กำลังซิงโครไนซ์",
690 + "sync_status_timed_out": "หมดเวลา",
691 + "sync_status_unsupported": "โหนดที่ไม่ได้รับการสนับสนุน",
692 "syncing_wallet_alert_content": "รายการยอดเงินและธุรกรรมของคุณอาจไม่สมบูรณ์จนกว่าจะมีข้อความว่า “ซิงโครไนซ์” ที่ด้านบน คลิก/แตะเพื่อเรียนรู้เพิ่มเติม่",
693 "syncing_wallet_alert_title": "กระเป๋าสตางค์ของคุณกำลังซิงค์",
694 "template": "แบบฟอร์ม",
695 "template_name": "ชื่อแม่แบบ",
696 + "testnet_coins_no_value": "Testnet Coins ไม่มีค่า",
697 "third_intro_content": "Yat อาศัยอยู่นอก Cake Wallet ด้วย ที่อยู่กระเป๋าใดๆ ทั่วโลกสามารถแทนด้วย Yat ได้อีกด้วย!",
698 "third_intro_title": "Yat ปฏิบัติตนอย่างดีกับผู้อื่น",
699 "thorchain_contract_address_not_supported": "Thorchain ไม่สนับสนุนการส่งไปยังที่อยู่สัญญา",
@@ -749,13 +769,16 @@
769 "trusted": "มั่นคง",
770 "tx_commit_exception_no_dust_on_change": "ธุรกรรมถูกปฏิเสธด้วยจำนวนเงินนี้ ด้วยเหรียญเหล่านี้คุณสามารถส่ง ${min} โดยไม่ต้องเปลี่ยนแปลงหรือ ${max} ที่ส่งคืนการเปลี่ยนแปลง",
771 "tx_commit_failed": "การทำธุรกรรมล้มเหลว กรุณาติดต่อฝ่ายสนับสนุน",
772 + "tx_invalid_input": "คุณกำลังใช้ประเภทอินพุตที่ไม่ถูกต้องสำหรับการชำระเงินประเภทนี้",
773 "tx_no_dust_exception": "การทำธุรกรรมถูกปฏิเสธโดยการส่งจำนวนน้อยเกินไป โปรดลองเพิ่มจำนวนเงิน",
774 "tx_not_enough_inputs_exception": "มีอินพุตไม่เพียงพอ โปรดเลือกเพิ่มเติมภายใต้การควบคุมเหรียญ",
775 + "tx_rejected_bip68_final": "การทำธุรกรรมมีอินพุตที่ไม่ได้รับการยืนยันและไม่สามารถแทนที่ด้วยค่าธรรมเนียม",
776 "tx_rejected_dust_change": "ธุรกรรมถูกปฏิเสธโดยกฎเครือข่ายจำนวนการเปลี่ยนแปลงต่ำ (ฝุ่น) ลองส่งทั้งหมดหรือลดจำนวนเงิน",
777 "tx_rejected_dust_output": "การทำธุรกรรมถูกปฏิเสธโดยกฎเครือข่ายจำนวนเอาต์พุตต่ำ (ฝุ่น) โปรดเพิ่มจำนวนเงิน",
778 "tx_rejected_dust_output_send_all": "การทำธุรกรรมถูกปฏิเสธโดยกฎเครือข่ายจำนวนเอาต์พุตต่ำ (ฝุ่น) โปรดตรวจสอบยอดคงเหลือของเหรียญที่เลือกภายใต้การควบคุมเหรียญ",
779 "tx_rejected_vout_negative": "ยอดคงเหลือไม่เพียงพอที่จะจ่ายสำหรับค่าธรรมเนียมการทำธุรกรรมนี้ โปรดตรวจสอบยอดคงเหลือของเหรียญภายใต้การควบคุมเหรียญ",
780 "tx_wrong_balance_exception": "คุณมีไม่เพียงพอ ${currency} ในการส่งจำนวนนี้",
781 + "tx_wrong_balance_with_amount_exception": "คุณมีไม่เพียงพอ ${currency} เพื่อส่งจำนวนทั้งหมดของ ${amount}",
782 "tx_zero_fee_exception": "ไม่สามารถส่งธุรกรรมด้วยค่าธรรมเนียม 0 ลองเพิ่มอัตราหรือตรวจสอบการเชื่อมต่อของคุณสำหรับการประมาณการล่าสุด",
783 "unavailable_balance": "ยอดคงเหลือไม่พร้อมใช้งาน",
784 "unavailable_balance_description": "ยอดคงเหลือที่ไม่พร้อมใช้งาน: ยอดรวมนี้รวมถึงเงินทุนที่ถูกล็อคในการทำธุรกรรมที่รอดำเนินการและที่คุณได้แช่แข็งไว้ในการตั้งค่าการควบคุมเหรียญของคุณ ยอดคงเหลือที่ถูกล็อคจะพร้อมใช้งานเมื่อธุรกรรมที่เกี่ยวข้องเสร็จสมบูรณ์ ในขณะที่ยอดคงเหลือที่แช่แข็งจะไม่สามารถเข้าถึงได้สำหรับธุรกรรมจนกว่าคุณจะตัดสินใจยกเลิกการแช่แข็ง",
@@ -809,6 +832,7 @@
832 "warning": "คำเตือน",
833 "welcome": "ยินดีต้อนรับสู่",
834 "welcome_to_cakepay": "ยินดีต้อนรับสู่ Cake Pay!",
835 + "what_is_silent_payments": "การชำระเงินเงียบคืออะไร?",
836 "widgets_address": "ที่อยู่",
837 "widgets_or": "หรือ",
838 "widgets_restore_from_blockheight": "กู้คืนจากระดับบล็อก",
res/values/strings_tl.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "Backup",
72 "backup_file": "Backup file",
73 "backup_password": "Backup password",
74 + "balance": "Balansehin",
75 "balance_page": "Pahina ng Balanse",
76 "bill_amount": "Halaga ng Bill",
77 "billing_address_info": "Kung tatanungin ang isang address ng pagsingil, ibigay ang iyong address sa pagpapadala",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "Bitcoin Madilim na Tema",
80 "bitcoin_light_theme": "Tema ng ilaw ng bitcoin",
81 "bitcoin_payments_require_1_confirmation": "Ang mga pagbabayad sa Bitcoin ay nangangailangan ng 1 kumpirmasyon, na maaaring tumagal ng 20 minuto o mas mahaba. Salamat sa iyong pasensya! Mag -email ka kapag nakumpirma ang pagbabayad.",
82 + "block_remaining": "1 bloke ang natitira",
83 "Blocks_remaining": "Ang natitirang ${status} ay natitira",
84 "bluetooth": "Bluetooth",
85 "bright_theme": "Maliwanag",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "Kumpirmahin ang pagbabawas ng bayad",
142 "confirm_fee_deduction_content": "Sumasang -ayon ka bang bawasan ang bayad mula sa output?",
143 "confirm_sending": "Kumpirmahin ang pagpapadala",
144 + "confirm_silent_payments_switch_node": "Sa kasalukuyan kinakailangan itong lumipat ng mga node upang i -scan ang mga tahimik na pagbabayad",
145 "confirmations": "Mga kumpirmasyon",
146 "confirmed": "Nakumpirma na balanse",
147 "confirmed_tx": "Nakumpirma",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "Bumubuo kami ng mga bagong address sa tuwing gumagamit ka ng isa, ngunit ang mga nakaraang address ay patuloy na gumagana",
225 "email_address": "Email address",
226 "enable_replace_by_fee": "Paganahin ang palitan-by-fee",
227 + "enable_silent_payments_scanning": "Paganahin ang pag -scan ng tahimik na pagbabayad",
228 "enabled": "Pinagana",
229 "enter_amount": "Ipasok ang halaga",
230 "enter_backup_password": "Ipasok ang backup password dito",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "Magpapadala ka ng pondo sa\n${recipient_name}",
283 "failed_authentication": "Nabigong pagpapatunay. ${state_error}",
284 "faq": "FAQ",
285 + "features": "Mga tampok",
286 "fetching": "Pagkuha",
287 "fiat_api": "Fiat API",
288 "fiat_balance": "Balanse ng fiat",
@@ -531,6 +536,7 @@
536 "save_backup_password_alert": "I -save ang backup password",
537 "save_to_downloads": "I -save sa mga pag -download",
538 "saved_the_trade_id": "Nai -save ko ang trade ID",
539 + "scan_one_block": "I -scan ang isang bloke",
540 "scan_qr_code": "I -scan ang QR Code",
541 "scan_qr_code_to_get_address": "I -scan ang QR code upang makuha ang address",
542 "scan_qr_on_device": "I-scan ang QR code na ito sa ibang device",
@@ -641,11 +647,22 @@
647 "sign_up": "Mag -sign up",
648 "signTransaction": "Mag-sign Transaksyon",
649 "signup_for_card_accept_terms": "Mag -sign up para sa card at tanggapin ang mga termino.",
650 + "silent_payments": "Tahimik na pagbabayad",
651 + "silent_payments_always_scan": "Itakda ang mga tahimik na pagbabayad na laging nag -scan",
652 + "silent_payments_disclaimer": "Ang mga bagong address ay hindi mga bagong pagkakakilanlan. Ito ay isang muling paggamit ng isang umiiral na pagkakakilanlan na may ibang label.",
653 + "silent_payments_display_card": "Ipakita ang Silent Payment Card",
654 + "silent_payments_scan_from_date": "I -scan mula sa petsa",
655 + "silent_payments_scan_from_date_or_blockheight": "Mangyaring ipasok ang taas ng block na nais mong simulan ang pag -scan para sa papasok na tahimik na pagbabayad, o, gamitin ang petsa sa halip. Maaari kang pumili kung ang pitaka ay patuloy na pag -scan sa bawat bloke, o suriin lamang ang tinukoy na taas.",
656 + "silent_payments_scan_from_height": "I -scan mula sa taas ng block",
657 + "silent_payments_scanned_tip": "Na -scan sa tip! (${tip})",
658 + "silent_payments_scanning": "Tahimik na pag -scan ng mga pagbabayad",
659 + "silent_payments_settings": "Mga setting ng tahimik na pagbabayad",
660 "slidable": "Slidable",
661 "sort_by": "Pag -uri -uriin sa pamamagitan ng",
662 "spend_key_private": "Gumastos ng susi (pribado)",
663 "spend_key_public": "Gumastos ng susi (publiko)",
664 "status": "Katayuan:",
665 + "string_default": "Default",
666 "subaddress_title": "Listahan ng Subaddress",
667 "subaddresses": "Mga Subaddresses",
668 "submit_request": "magsumite ng isang kahilingan",
@@ -670,10 +687,13 @@
687 "sync_status_starting_sync": "Simula sa pag -sync",
688 "sync_status_syncronized": "Naka -synchronize",
689 "sync_status_syncronizing": "Pag -synchronize",
690 + "sync_status_timed_out": "Nag -time out",
691 + "sync_status_unsupported": "Hindi suportadong node",
692 "syncing_wallet_alert_content": "Ang iyong balanse at listahan ng transaksyon ay maaaring hindi kumpleto hanggang sa sabihin nito na \"naka -synchronize\" sa tuktok. Mag -click/tap upang malaman ang higit pa.",
693 "syncing_wallet_alert_title": "Ang iyong pitaka ay nag -sync",
694 "template": "Template",
695 "template_name": "Pangalan ng Template",
696 + "testnet_coins_no_value": "Ang mga barya ng testnet ay walang halaga",
697 "third_intro_content": "Ang mga yats ay nakatira sa labas ng cake wallet, din. Ang anumang address ng pitaka sa mundo ay maaaring mapalitan ng isang yat!",
698 "third_intro_title": "Si Yat ay mahusay na gumaganap sa iba",
699 "thorchain_contract_address_not_supported": "Hindi sinusuportahan ng Thorchain ang pagpapadala sa isang address ng kontrata",
@@ -749,13 +769,16 @@
769 "trusted": "Pinagkakatiwalaan",
770 "tx_commit_exception_no_dust_on_change": "Ang transaksyon ay tinanggihan sa halagang ito. Sa mga barya na ito maaari kang magpadala ng ${min} nang walang pagbabago o ${max} na nagbabalik ng pagbabago.",
771 "tx_commit_failed": "Nabigo ang transaksyon sa transaksyon. Mangyaring makipag -ugnay sa suporta.",
772 + "tx_invalid_input": "Gumagamit ka ng maling uri ng pag -input para sa ganitong uri ng pagbabayad",
773 "tx_no_dust_exception": "Ang transaksyon ay tinanggihan sa pamamagitan ng pagpapadala ng isang maliit na maliit. Mangyaring subukang dagdagan ang halaga.",
774 "tx_not_enough_inputs_exception": "Hindi sapat na magagamit ang mga input. Mangyaring pumili ng higit pa sa ilalim ng control ng barya",
775 + "tx_rejected_bip68_final": "Ang transaksyon ay hindi nakumpirma na mga input at nabigo na palitan ng bayad.",
776 "tx_rejected_dust_change": "Ang transaksyon na tinanggihan ng mga patakaran sa network, mababang halaga ng pagbabago (alikabok). Subukang ipadala ang lahat o bawasan ang halaga.",
777 "tx_rejected_dust_output": "Ang transaksyon na tinanggihan ng mga patakaran sa network, mababang halaga ng output (alikabok). Mangyaring dagdagan ang halaga.",
778 "tx_rejected_dust_output_send_all": "Ang transaksyon na tinanggihan ng mga patakaran sa network, mababang halaga ng output (alikabok). Mangyaring suriin ang balanse ng mga barya na napili sa ilalim ng kontrol ng barya.",
779 "tx_rejected_vout_negative": "Hindi sapat na balanse upang magbayad para sa mga bayarin ng transaksyon na ito. Mangyaring suriin ang balanse ng mga barya sa ilalim ng kontrol ng barya.",
780 "tx_wrong_balance_exception": "Wala kang sapat na ${currency} upang maipadala ang halagang ito.",
781 + "tx_wrong_balance_with_amount_exception": "Wala kang sapat ${currency} Upang ipadala ang kabuuang halaga ng ${amount}",
782 "tx_zero_fee_exception": "Hindi maaaring magpadala ng transaksyon na may 0 bayad. Subukan ang pagtaas ng rate o pagsuri sa iyong koneksyon para sa pinakabagong mga pagtatantya.",
783 "unavailable_balance": "Hindi available na balanse",
784 "unavailable_balance_description": "Hindi Available na Balanse: Kasama sa kabuuang ito ang mga pondong naka-lock sa mga nakabinbing transaksyon at ang mga aktibong na-freeze mo sa iyong mga setting ng kontrol ng coin. Magiging available ang mga naka-lock na balanse kapag nakumpleto na ang kani-kanilang mga transaksyon, habang ang mga nakapirming balanse ay nananatiling hindi naa-access para sa mga transaksyon hanggang sa magpasya kang i-unfreeze ang mga ito.",
@@ -809,6 +832,7 @@
832 "warning": "Babala",
833 "welcome": "Maligayang pagdating sa",
834 "welcome_to_cakepay": "Maligayang pagdating sa cake pay!",
835 + "what_is_silent_payments": "Ano ang tahimik na pagbabayad?",
836 "widgets_address": "Address",
837 "widgets_or": "o",
838 "widgets_restore_from_blockheight": "Ibalik mula sa blockheight",
res/values/strings_tr.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "Yedek",
72 "backup_file": "Yedek dosyası",
73 "backup_password": "Yedek parolası",
74 + "balance": "Denge",
75 "balance_page": "Bakiye Sayfası",
76 "bill_amount": "Fatura Tutarı",
77 "billing_address_info": "Eğer fatura adresi istenirse, kargo adresinizi girin",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "Bitcoin Karanlık Teması",
80 "bitcoin_light_theme": "Bitcoin Hafif Tema",
81 "bitcoin_payments_require_1_confirmation": "Bitcoin ödemeleri, 20 dakika veya daha uzun sürebilen 1 onay gerektirir. Sabrınız için teşekkürler! Ödeme onaylandığında e-posta ile bilgilendirileceksiniz.",
82 + "block_remaining": "Kalan 1 blok",
83 "Blocks_remaining": "${status} Blok Kaldı",
84 "bluetooth": "Bluetooth",
85 "bright_theme": "Parlak",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "Ücret kesintisini onaylayın",
142 "confirm_fee_deduction_content": "Ücreti çıktıdan düşürmeyi kabul ediyor musunuz?",
143 "confirm_sending": "Göndermeyi onayla",
144 + "confirm_silent_payments_switch_node": "Şu anda sessiz ödemeleri taramak için düğümleri değiştirmek gerekiyor",
145 "confirmations": "Onay",
146 "confirmed": "Onaylanmış Bakiye",
147 "confirmed_tx": "Onaylanmış",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "Adresini her kullandığında yeni adres oluşturuyoruz, ancak önceki adresler de çalışmaya devam eder",
225 "email_address": "E-posta Adresi",
226 "enable_replace_by_fee": "Farklı Değiştir'i Etkinleştir",
227 + "enable_silent_payments_scanning": "Sessiz ödeme taramasını etkinleştirin",
228 "enabled": "Etkin",
229 "enter_amount": "Miktar Girin",
230 "enter_backup_password": "Yedekleme parolasını buraya gir",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "Parayı buraya gönderceksin:\n${recipient_name}",
283 "failed_authentication": "Doğrulama başarısız oldu. ${state_error}",
284 "faq": "SSS",
285 + "features": "Özellikler",
286 "fetching": "Getiriliyor",
287 "fiat_api": "İtibari Para API",
288 "fiat_balance": "Fiat Bakiyesi",
@@ -531,6 +536,7 @@
536 "save_backup_password_alert": "Yedek parolasını kaydet",
537 "save_to_downloads": "İndirilenlere Kaydet",
538 "saved_the_trade_id": "Takas ID'imi kaydettim",
539 + "scan_one_block": "Bir bloğu tara",
540 "scan_qr_code": "QR kodunu tarayın",
541 "scan_qr_code_to_get_address": "Adresi getirmek için QR kodunu tara",
542 "scan_qr_on_device": "Bu QR kodunu başka bir cihazda tarayın",
@@ -641,11 +647,22 @@
647 "sign_up": "Kaydol",
648 "signTransaction": "İşlem İmzala",
649 "signup_for_card_accept_terms": "Kart için kaydol ve koşulları kabul et.",
650 + "silent_payments": "Sessiz ödemeler",
651 + "silent_payments_always_scan": "Sessiz ödemeleri her zaman tarama ayarlayın",
652 + "silent_payments_disclaimer": "Yeni adresler yeni kimlikler değildir. Farklı bir etikete sahip mevcut bir kimliğin yeniden kullanımıdır.",
653 + "silent_payments_display_card": "Sessiz Ödeme Kartı Göster",
654 + "silent_payments_scan_from_date": "Tarihten tarama",
655 + "silent_payments_scan_from_date_or_blockheight": "Lütfen gelen sessiz ödemeler için taramaya başlamak istediğiniz blok yüksekliğini girin veya bunun yerine tarihi kullanın. Cüzdanın her bloğu taramaya devam edip etmediğini veya yalnızca belirtilen yüksekliği kontrol edip etmediğini seçebilirsiniz.",
656 + "silent_payments_scan_from_height": "Blok yüksekliğinden tarama",
657 + "silent_payments_scanned_tip": "Bahşiş için tarandı! (${tip})",
658 + "silent_payments_scanning": "Sessiz Ödemeler Taraması",
659 + "silent_payments_settings": "Sessiz Ödeme Ayarları",
660 "slidable": "kaydırılabilir",
661 "sort_by": "Göre sırala",
662 "spend_key_private": "Harcama anahtarı (özel)",
663 "spend_key_public": "Harcama anahtarı (genel)",
664 "status": "Durum: ",
665 + "string_default": "Varsayılan",
666 "subaddress_title": "Alt adres listesi",
667 "subaddresses": "Alt adresler",
668 "submit_request": "talep gönder",
@@ -670,10 +687,13 @@
687 "sync_status_starting_sync": "SENKRONİZE BAŞLATILIYOR",
688 "sync_status_syncronized": "SENKRONİZE EDİLDİ",
689 "sync_status_syncronizing": "SENKRONİZE EDİLİYOR",
690 + "sync_status_timed_out": "ZAMAN AŞIMINA UĞRADI",
691 + "sync_status_unsupported": "Desteklenmeyen düğüm",
692 "syncing_wallet_alert_content": "Bakiyeniz ve işlem listeniz, en üstte \"SENKRONİZE EDİLDİ\" yazana kadar tamamlanmamış olabilir. Daha fazla bilgi edinmek için tıklayın/dokunun.",
693 "syncing_wallet_alert_title": "Cüzdanınız senkronize ediliyor",
694 "template": "Şablon",
695 "template_name": "şablon adı",
696 + "testnet_coins_no_value": "TestNet paralarının değeri yok",
697 "third_intro_content": "Yat'lar Cake Wallet'ın dışında da çalışabilir. Dünya üzerindeki herhangi bir cüzdan adresi Yat ile değiştirilebilir!",
698 "third_intro_title": "Yat diğerleriyle iyi çalışır",
699 "thorchain_contract_address_not_supported": "Thorchain bir sözleşme adresine göndermeyi desteklemiyor",
@@ -749,13 +769,16 @@
769 "trusted": "Güvenilir",
770 "tx_commit_exception_no_dust_on_change": "İşlem bu miktarla reddedilir. Bu madeni paralarla değişiklik yapmadan ${min} veya değişikliği döndüren ${max} gönderebilirsiniz.",
771 "tx_commit_failed": "İşlem taahhüdü başarısız oldu. Lütfen Destek ile iletişime geçin.",
772 + "tx_invalid_input": "Bu tür ödeme için yanlış giriş türünü kullanıyorsunuz",
773 "tx_no_dust_exception": "İşlem, çok küçük bir miktar gönderilerek reddedilir. Lütfen miktarı artırmayı deneyin.",
774 "tx_not_enough_inputs_exception": "Yeterli giriş yok. Lütfen madeni para kontrolü altında daha fazlasını seçin",
775 + "tx_rejected_bip68_final": "İşlemin doğrulanmamış girdileri var ve ücrete göre değiştirilemedi.",
776 "tx_rejected_dust_change": "Ağ kurallarına göre reddedilen işlem, düşük değişim miktarı (toz). Tümünü göndermeyi veya miktarı azaltmayı deneyin.",
777 "tx_rejected_dust_output": "Ağ kurallarına göre reddedilen işlem, düşük çıktı miktarı (toz). Lütfen miktarı artırın.",
778 "tx_rejected_dust_output_send_all": "Ağ kurallarına göre reddedilen işlem, düşük çıktı miktarı (toz). Lütfen madeni para kontrolü altında seçilen madeni para dengesini kontrol edin.",
779 "tx_rejected_vout_negative": "Bu işlem ücretleri için ödeme yapmak için yeterli bakiye yok. Lütfen madeni para kontrolü altındaki madeni para dengesini kontrol edin.",
780 "tx_wrong_balance_exception": "Bu miktarı göndermek için yeterli ${currency} yok.",
781 + "tx_wrong_balance_with_amount_exception": "Yeterli değilsiniz ${currency} toplam ${amount} miktarını göndermek için",
782 "tx_zero_fee_exception": "0 ücret ile işlem gönderilemez. En son tahminler için oranı artırmayı veya bağlantınızı kontrol etmeyi deneyin.",
783 "unavailable_balance": "Kullanılamayan bakiye",
784 "unavailable_balance_description": "Kullanılamayan Bakiye: Bu toplam, bekleyen işlemlerde kilitlenen fonları ve jeton kontrol ayarlarınızda aktif olarak dondurduğunuz fonları içerir. Kilitli bakiyeler, ilgili işlemleri tamamlandıktan sonra kullanılabilir hale gelir; dondurulmuş bakiyeler ise siz onları dondurmaya karar verene kadar işlemler için erişilemez durumda kalır.",
@@ -809,6 +832,7 @@
832 "warning": "Uyarı",
833 "welcome": "Hoş Geldiniz",
834 "welcome_to_cakepay": "Cake Pay'e Hoş Geldiniz!",
835 + "what_is_silent_payments": "Sessiz ödemeler nedir?",
836 "widgets_address": "Adres",
837 "widgets_or": "veya",
838 "widgets_restore_from_blockheight": "Blok yüksekliğinden geri yükle",
res/values/strings_uk.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "Резервна копія",
72 "backup_file": "Файл резервної копії",
73 "backup_password": "Пароль резервної копії",
74 + "balance": "Балансувати",
75 "balance_page": "Сторінка балансу",
76 "bill_amount": "Сума рахунку",
77 "billing_address_info": "Якщо буде запропоновано платіжну адресу, вкажіть свою адресу доставки",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "Темна тема Bitcoin",
80 "bitcoin_light_theme": "Світла тема Bitcoin",
81 "bitcoin_payments_require_1_confirmation": "Платежі Bitcoin потребують 1 підтвердження, яке може зайняти 20 хвилин або більше. Дякую за Ваше терпіння! Ви отримаєте електронний лист, коли платіж буде підтверджено.",
82 + "block_remaining": "1 блок, що залишився",
83 "Blocks_remaining": "${status} Залишилось блоків",
84 "bluetooth": "Блюдот",
85 "bright_theme": "Яскрава",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "Підтвердьте відрахування комісії",
142 "confirm_fee_deduction_content": "Чи погоджуєтесь ви вирахувати комісію з сумми одержувача?",
143 "confirm_sending": "Підтвердити відправлення",
144 + "confirm_silent_payments_switch_node": "В даний час потрібно перемикати вузли на сканування мовчазних платежів",
145 "confirmations": "Підтвердження",
146 "confirmed": "Підтверджений баланс",
147 "confirmed_tx": "Підтверджений",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "Ми створюємо нові адреси щоразу, коли ви використовуєте їх, але попередні адреси продовжують працювати",
225 "email_address": "Адреса електронної пошти",
226 "enable_replace_by_fee": "Увімкнути заміну з комісією",
227 + "enable_silent_payments_scanning": "Увімкнути мовчазні платежі сканування",
228 "enabled": "Увімкнено",
229 "enter_amount": "Введіть суму",
230 "enter_backup_password": "Введіть пароль резервної копії",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "Ви будете відправляти кошти\n${recipient_name}",
283 "failed_authentication": "Помилка аутентифікації. ${state_error}",
284 "faq": "FAQ",
285 + "features": "Особливості",
286 "fetching": "Завантаження",
287 "fiat_api": "Фіат API",
288 "fiat_balance": "Фіат Баланс",
@@ -532,6 +537,7 @@
537 "save_backup_password_alert": "Зберегти пароль резервної копії",
538 "save_to_downloads": "Зберегти до завантажень",
539 "saved_the_trade_id": "Я зберіг ID операції",
540 + "scan_one_block": "Сканувати один блок",
541 "scan_qr_code": "Відскануйте QR-код",
542 "scan_qr_code_to_get_address": "Скануйте QR-код для одержання адреси",
543 "scan_qr_on_device": "Відскануйте цей QR-код на іншому пристрої",
@@ -642,11 +648,22 @@
648 "sign_up": "Зареєструватися",
649 "signTransaction": "Підписати транзакцію",
650 "signup_for_card_accept_terms": "Зареєструйтеся на картку та прийміть умови.",
651 + "silent_payments": "Мовчазні платежі",
652 + "silent_payments_always_scan": "Встановити мовчазні платежі завжди сканувати",
653 + "silent_payments_disclaimer": "Нові адреси - це не нові ідентичності. Це повторне використання існуючої ідентичності з іншою етикеткою.",
654 + "silent_payments_display_card": "Покажіть безшумну карту платежів",
655 + "silent_payments_scan_from_date": "Сканувати з дати",
656 + "silent_payments_scan_from_date_or_blockheight": "Введіть висоту блоку, яку ви хочете почати сканувати для вхідних мовчазних платежів, або скористайтеся датою замість цього. Ви можете вибрати, якщо гаманець продовжує сканувати кожен блок, або перевіряє лише вказану висоту.",
657 + "silent_payments_scan_from_height": "Сканування від висоти блоку",
658 + "silent_payments_scanned_tip": "Сканований на підказку! (${tip})",
659 + "silent_payments_scanning": "Мовчазні платежі сканування",
660 + "silent_payments_settings": "Налаштування мовчазних платежів",
661 "slidable": "Розсувний",
662 "sort_by": "Сортувати за",
663 "spend_key_private": "Приватний ключ витрати",
664 "spend_key_public": "Публічний ключ витрати",
665 "status": "Статус: ",
666 + "string_default": "За замовчуванням",
667 "subaddress_title": "Список Субадрес",
668 "subaddresses": "Субадреси",
669 "submit_request": "надіслати запит",
@@ -671,10 +688,13 @@
688 "sync_status_starting_sync": "ПОЧАТОК СИНХРОНІЗАЦІЇ",
689 "sync_status_syncronized": "СИНХРОНІЗОВАНИЙ",
690 "sync_status_syncronizing": "СИНХРОНІЗАЦІЯ",
691 + "sync_status_timed_out": "ТАЙМ-АУТ",
692 + "sync_status_unsupported": "Непідтримуваний вузол",
693 "syncing_wallet_alert_content": "Ваш баланс та список транзакцій може бути неповним, доки вгорі не буде написано «СИНХРОНІЗОВАНО». Натисніть/торкніться, щоб дізнатися більше.",
694 "syncing_wallet_alert_title": "Ваш гаманець синхронізується",
695 "template": "Шаблон",
696 "template_name": "Назва шаблону",
697 + "testnet_coins_no_value": "Монети TestNet не мають значення",
698 "third_intro_content": "Yat знаходиться за межами Cake Wallet. Будь-яку адресу гаманця на землі можна замінити на Yat!",
699 "third_intro_title": "Yat добре взаємодіє з іншими",
700 "thorchain_contract_address_not_supported": "Thorchain не підтримує надсилання на адресу контракту",
@@ -750,13 +770,16 @@
770 "trusted": "довіряють",
771 "tx_commit_exception_no_dust_on_change": "Транзакція відхилена цією сумою. За допомогою цих монет ви можете надіслати ${min} без змін або ${max}, що повертає зміни.",
772 "tx_commit_failed": "Транзакційна комісія не вдалося. Будь ласка, зв'яжіться з підтримкою.",
773 + "tx_invalid_input": "Ви використовуєте неправильний тип введення для цього типу оплати",
774 "tx_no_dust_exception": "Угода відхиляється, відправивши суму занадто мала. Будь ласка, спробуйте збільшити суму.",
775 "tx_not_enough_inputs_exception": "Недостатньо доступних входів. Виберіть більше під контролем монети",
776 + "tx_rejected_bip68_final": "Трансакція має непідтверджені входи і не замінила плату.",
777 "tx_rejected_dust_change": "Транзакція відхилена за допомогою мережевих правил, низька кількість змін (пил). Спробуйте надіслати все або зменшити суму.",
778 "tx_rejected_dust_output": "Транзакція відхилена за допомогою мережевих правил, низька кількість вихідної кількості (пил). Будь ласка, збільшуйте суму.",
779 "tx_rejected_dust_output_send_all": "Транзакція відхилена за допомогою мережевих правил, низька кількість вихідної кількості (пил). Будь ласка, перевірте баланс монет, вибраних під контролем монет.",
780 "tx_rejected_vout_negative": "Недостатньо балансу, щоб оплатити плату за цю транзакцію. Будь ласка, перевірте баланс монет під контролем монет.",
781 "tx_wrong_balance_exception": "У вас недостатньо ${currency}, щоб надіслати цю суму.",
782 + "tx_wrong_balance_with_amount_exception": "У вас недостатньо ${currency}, щоб надіслати загальну кількість ${amount}",
783 "tx_zero_fee_exception": "Не вдається відправити транзакцію з 0 платежами. Спробуйте збільшити ставку або перевірити з'єднання на останні оцінки.",
784 "unavailable_balance": "Недоступний баланс",
785 "unavailable_balance_description": "Недоступний баланс: ця сума включає кошти, заблоковані в незавершених транзакціях, і ті, які ви активно заморозили в налаштуваннях контролю монет. Заблоковані баланси стануть доступними після завершення відповідних транзакцій, тоді як заморожені баланси залишаються недоступними для транзакцій, доки ви не вирішите їх розморозити.",
@@ -810,6 +833,7 @@
833 "warning": "УВАГА",
834 "welcome": "Вітаємо в",
835 "welcome_to_cakepay": "Ласкаво просимо до Cake Pay!",
836 + "what_is_silent_payments": "Що таке мовчазні платежі?",
837 "widgets_address": "Адреса",
838 "widgets_or": "або",
839 "widgets_restore_from_blockheight": "Відновити на висоті блоку",
res/values/strings_ur.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "بیک اپ",
72 "backup_file": "بیک اپ فائل",
73 "backup_password": "بیک اپ پاس ورڈ",
74 + "balance": "بقیہ",
75 "balance_page": "بیلنس صفحہ",
76 "bill_amount": "بل رقم",
77 "billing_address_info": "اگر آپ سے بلنگ کا پتہ پوچھا جائے تو اپنا شپنگ ایڈریس فراہم کریں۔",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "بٹ کوائن ڈارک تھیم",
80 "bitcoin_light_theme": "بٹ کوائن لائٹ تھیم",
81 "bitcoin_payments_require_1_confirmation": "بٹ کوائن کی ادائیگی میں 1 تصدیق کی ضرورت ہوتی ہے ، جس میں 20 منٹ یا اس سے زیادہ وقت لگ سکتا ہے۔ آپ کے صبر کا شکریہ! ادائیگی کی تصدیق ہونے پر آپ کو ای میل کیا جائے گا۔",
82 + "block_remaining": "1 بلاک باقی",
83 "Blocks_remaining": "${status} بلاکس باقی ہیں۔",
84 "bluetooth": "بلوٹوتھ",
85 "bright_theme": "روشن",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "فیس میں کٹوتی کی تصدیق کریں",
142 "confirm_fee_deduction_content": "کیا آپ آؤٹ پٹ سے فیس کم کرنے پر راضی ہیں؟",
143 "confirm_sending": "بھیجنے کی تصدیق کریں۔",
144 + "confirm_silent_payments_switch_node": "فی الحال خاموش ادائیگیوں کو اسکین کرنے کے لئے نوڈس کو تبدیل کرنے کی ضرورت ہے",
145 "confirmations": "تصدیقات",
146 "confirmed": "تصدیق شدہ بیلنس",
147 "confirmed_tx": "تصدیق",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "جب بھی آپ ایک کا استعمال کرتے ہیں تو ہم نئے پتے تیار کرتے ہیں، لیکن پچھلے پتے کام کرتے رہتے ہیں۔",
225 "email_address": "ای میل اڈریس",
226 "enable_replace_by_fee": "فی فیس کو تبدیل کریں",
227 + "enable_silent_payments_scanning": "خاموش ادائیگیوں کو اسکیننگ کے قابل بنائیں",
228 "enabled": "فعال",
229 "enter_amount": "رقم درج کریں۔",
230 "enter_backup_password": "یہاں بیک اپ پاس ورڈ درج کریں۔",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "آپ فنڈز بھیج رہے ہوں گے\n${recipient_name}",
283 "failed_authentication": "ناکام تصدیق۔ ${state_error}",
284 "faq": "عمومی سوالات",
285 + "features": "خصوصیات",
286 "fetching": "لا رہا ہے۔",
287 "fiat_api": "Fiat API",
288 "fiat_balance": "فیاٹ بیلنس",
@@ -533,6 +538,7 @@
538 "save_backup_password_alert": "بیک اپ پاس ورڈ محفوظ کریں۔",
539 "save_to_downloads": "۔ﮟﯾﺮﮐ ﻅﻮﻔﺤﻣ ﮟﯿﻣ ﺯﮈﻮﻟ ﻥﺅﺍﮈ",
540 "saved_the_trade_id": "میں نے تجارتی ID محفوظ کر لی ہے۔",
541 + "scan_one_block": "ایک بلاک اسکین کریں",
542 "scan_qr_code": "پتہ حاصل کرنے کے لیے QR کوڈ اسکین کریں۔",
543 "scan_qr_code_to_get_address": "پتہ حاصل کرنے کے لئے QR کوڈ کو اسکین کریں",
544 "scan_qr_on_device": " ۔ﮟﯾﺮﮐ ﻦﯿﮑﺳﺍ ﺮﭘ ﺲﺋﺍﻮﯾﮈ ﺭﻭﺍ ﯽﺴﮐ ﻮﮐ ﮈﻮﮐ QR ﺱﺍ",
@@ -643,11 +649,22 @@
649 "sign_up": "سائن اپ",
650 "signTransaction": "۔ﮟﯾﺮﮐ ﻂﺨﺘﺳﺩ ﺮﭘ ﻦﯾﺩ ﻦﯿﻟ",
651 "signup_for_card_accept_terms": "کارڈ کے لیے سائن اپ کریں اور شرائط کو قبول کریں۔",
652 + "silent_payments": "خاموش ادائیگی",
653 + "silent_payments_always_scan": "خاموش ادائیگی ہمیشہ اسکیننگ کریں",
654 + "silent_payments_disclaimer": "نئے پتے نئی شناخت نہیں ہیں۔ یہ ایک مختلف لیبل کے ساتھ موجودہ شناخت کا دوبارہ استعمال ہے۔",
655 + "silent_payments_display_card": "خاموش ادائیگی کارڈ دکھائیں",
656 + "silent_payments_scan_from_date": "تاریخ سے اسکین کریں",
657 + "silent_payments_scan_from_date_or_blockheight": "براہ کرم بلاک اونچائی میں داخل ہوں جس سے آپ آنے والی خاموش ادائیگیوں کے لئے اسکیننگ شروع کرنا چاہتے ہیں ، یا اس کے بجائے تاریخ کا استعمال کریں۔ آپ یہ منتخب کرسکتے ہیں کہ اگر پرس ہر بلاک کو اسکیننگ جاری رکھے ہوئے ہے ، یا صرف مخصوص اونچائی کی جانچ پڑتال کرتا ہے۔",
658 + "silent_payments_scan_from_height": "بلاک اونچائی سے اسکین کریں",
659 + "silent_payments_scanned_tip": "نوکنے کے لئے اسکین! (${tip})",
660 + "silent_payments_scanning": "خاموش ادائیگی اسکیننگ",
661 + "silent_payments_settings": "خاموش ادائیگی کی ترتیبات",
662 "slidable": "سلائیڈ ایبل",
663 "sort_by": "ترتیب دیں",
664 "spend_key_private": "خرچ کی کلید (نجی)",
665 "spend_key_public": "خرچ کی کلید (عوامی)",
666 "status": "حالت:",
667 + "string_default": "پہلے سے طے شدہ",
668 "subaddress_title": "ذیلی ایڈریس کی فہرست",
669 "subaddresses": "ذیلی پتے",
670 "submit_request": "درخواست بھیج دو",
@@ -672,10 +689,13 @@
689 "sync_status_starting_sync": "مطابقت پذیری شروع کر رہا ہے۔",
690 "sync_status_syncronized": "مطابقت پذیر",
691 "sync_status_syncronizing": "مطابقت پذیری",
692 + "sync_status_timed_out": "وقت ختم",
693 + "sync_status_unsupported": "غیر تعاون یافتہ نوڈ",
694 "syncing_wallet_alert_content": "آپ کے بیلنس اور لین دین کی فہرست اس وقت تک مکمل نہیں ہو سکتی جب تک کہ یہ سب سے اوپر \"SYNCRONIZED\" نہ کہے۔ مزید جاننے کے لیے کلک/تھپتھپائیں۔",
695 "syncing_wallet_alert_title": "آپ کا بٹوہ مطابقت پذیر ہو رہا ہے۔",
696 "template": "سانچے",
697 "template_name": "ٹیمپلیٹ کا نام",
698 + "testnet_coins_no_value": "ٹیسٹ نیٹ سکے کی کوئی قیمت نہیں ہے",
699 "third_intro_content": "Yats بھی Cake والیٹ سے باہر رہتے ہیں۔ زمین پر کسی بھی بٹوے کے پتے کو Yat سے تبدیل کیا جا سکتا ہے!",
700 "third_intro_title": "Yat دوسروں کے ساتھ اچھی طرح کھیلتا ہے۔",
701 "thorchain_contract_address_not_supported": "تھورچین معاہدے کے پتے بھیجنے کی حمایت نہیں کرتا ہے",
@@ -751,13 +771,16 @@
771 "trusted": "قابل اعتماد",
772 "tx_commit_exception_no_dust_on_change": "اس رقم سے لین دین کو مسترد کردیا گیا ہے۔ ان سککوں کے ذریعہ آپ بغیر کسی تبدیلی کے ${min} یا ${max} بھیج سکتے ہیں جو لوٹتے ہیں۔",
773 "tx_commit_failed": "ٹرانزیکشن کمٹ ناکام ہوگیا۔ براہ کرم سپورٹ سے رابطہ کریں۔",
774 + "tx_invalid_input": "آپ اس قسم کی ادائیگی کے لئے غلط ان پٹ کی قسم استعمال کررہے ہیں",
775 "tx_no_dust_exception": "لین دین کو بہت چھوٹی رقم بھیج کر مسترد کردیا جاتا ہے۔ براہ کرم رقم میں اضافہ کرنے کی کوشش کریں۔",
776 "tx_not_enough_inputs_exception": "کافی ان پٹ دستیاب نہیں ہے۔ براہ کرم سکے کے کنٹرول میں مزید منتخب کریں",
777 + "tx_rejected_bip68_final": "لین دین میں غیر مصدقہ آدانوں کی ہے اور وہ فیس کے ذریعہ تبدیل کرنے میں ناکام رہا ہے۔",
778 "tx_rejected_dust_change": "نیٹ ورک کے قواعد ، کم تبدیلی کی رقم (دھول) کے ذریعہ لین دین کو مسترد کردیا گیا۔ سب کو بھیجنے یا رقم کو کم کرنے کی کوشش کریں۔",
779 "tx_rejected_dust_output": "لین دین کو نیٹ ورک کے قواعد ، کم آؤٹ پٹ رقم (دھول) کے ذریعہ مسترد کردیا گیا۔ براہ کرم رقم میں اضافہ کریں۔",
780 "tx_rejected_dust_output_send_all": "لین دین کو نیٹ ورک کے قواعد ، کم آؤٹ پٹ رقم (دھول) کے ذریعہ مسترد کردیا گیا۔ براہ کرم سکے کے کنٹرول میں منتخب کردہ سکے کا توازن چیک کریں۔",
781 "tx_rejected_vout_negative": "اس لین دین کی فیسوں کی ادائیگی کے لئے کافی توازن نہیں ہے۔ براہ کرم سکے کے کنٹرول میں سکے کا توازن چیک کریں۔",
782 "tx_wrong_balance_exception": "آپ کے پاس یہ رقم بھیجنے کے لئے کافی ${currency} نہیں ہے۔",
783 + "tx_wrong_balance_with_amount_exception": "آپ کے پاس کافی نہیں ہے ${currency} ${amount}",
784 "tx_zero_fee_exception": "0 فیس کے ساتھ لین دین نہیں بھیج سکتا۔ شرح کو بڑھانے یا تازہ ترین تخمینے کے ل your اپنے کنکشن کی جانچ پڑتال کرنے کی کوشش کریں۔",
785 "unavailable_balance": "ﺲﻨﻠﯿﺑ ﺏﺎﯿﺘﺳﺩ ﺮﯿﻏ",
786 "unavailable_balance_description": "۔ﮯﺗﺮﮐ ﮟﯿﮩﻧ ﮧﻠﺼﯿﻓ ﺎﮐ ﮯﻧﺮﮐ ﺪﻤﺠﻨﻣ ﻥﺍ ﮟﯿﮩﻧﺍ ﭖﺁ ﮧﮐ ﮏﺗ ﺐﺟ ﮟﯿﮨ ﮯﺘﮨﺭ ﯽﺋﺎﺳﺭ ﻞﺑﺎﻗﺎﻧ ﮏﺗ ﺖﻗﻭ ﺱﺍ ﮯﯿﻟ ﮯﮐ ﻦﯾﺩ ﻦﯿﻟ ﺲﻨﻠﯿﺑ ﺪﻤﺠﻨﻣ ﮧﮐ ﺐﺟ ،ﮯﮔ ﮟﯿﺋﺎﺟ ﻮﮨ ﺏﺎﯿﺘﺳﺩ ﺲﻨﻠﯿﺑ ﻞﻔﻘﻣ ﺪﻌﺑ ﮯﮐ ﮯﻧﻮﮨ ﻞﻤﮑﻣ ﻦﯾﺩ ﻦﯿﻟ ﮧﻘﻠﻌﺘﻣ ﮯﮐ ﻥﺍ ۔ﮯﮨ ﺎﮭﮐﺭ ﺮ",
@@ -811,6 +834,7 @@
834 "warning": "وارننگ",
835 "welcome": "میں خوش آمدید",
836 "welcome_to_cakepay": "Cake پے میں خوش آمدید!",
837 + "what_is_silent_payments": "خاموش ادائیگی کیا ہے؟",
838 "widgets_address": "پتہ",
839 "widgets_or": "یا",
840 "widgets_restore_from_blockheight": "بلاک ہائیٹ سے بحال کریں۔",
res/values/strings_yo.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "Ṣẹ̀dà",
72 "backup_file": "Ṣẹ̀dà akọsílẹ̀",
73 "backup_password": "Ṣẹ̀dà ọ̀rọ̀ aṣínà",
74 + "balance": "Iwọntunwọnsi",
75 "balance_page": "Oju-iwe iwọntunwọnsi",
76 "bill_amount": "Iye ìwé owó",
77 "billing_address_info": "Tí ọlọ́jà bá bèèrè àdírẹ́sì sísan yín, fún òun ni àdírẹ́sì t'á ránṣẹ́ káàdì yìí sí",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "Bitcoin Dark Akori",
80 "bitcoin_light_theme": "Bitcoin Light Akori",
81 "bitcoin_payments_require_1_confirmation": "Àwọn àránṣẹ́ Bitcoin nílò ìjẹ́rìísí kan. Ó lè lo ìṣéjú ogun tàbí ìṣéjú jù. A dúpẹ́ fún sùúrù yín! Ẹ máa gba ímeèlì t'ó bá jẹ́rìísí àránṣẹ́ náà.",
82 + "block_remaining": "1 bulọọki to ku",
83 "Blocks_remaining": "Àkójọpọ̀ ${status} kikù",
84 "bluetooth": "Bluetooth",
85 "bright_theme": "Funfun",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "Jẹrisi iyọkuro owo",
142 "confirm_fee_deduction_content": "Ṣe o gba lati yọkuro idiyele naa kuro ni iṣejade?",
143 "confirm_sending": "Jẹ́rìí sí ránṣẹ́",
144 + "confirm_silent_payments_switch_node": "Lọwọlọwọ o nilo lati yi awọn apa pada si awọn sisanwo ipalọlọ",
145 "confirmations": "Àwọn ẹ̀rí",
146 "confirmed": "A ti jẹ́rìí ẹ̀",
147 "confirmed_tx": "Jẹrisi",
@@ -222,6 +225,7 @@
225 "electrum_address_disclaimer": "A dá àwọn àdírẹ́sì títun ní gbogbo àwọn ìgbà t'ẹ́ lo ó kan ṣùgbọ́n ẹ lè tẹ̀síwájú lo àwọn àdírẹ́sì tẹ́lẹ̀tẹ́lẹ̀.",
226 "email_address": "Àdírẹ́sì ímeèlì",
227 "enable_replace_by_fee": "Mu ki o rọpo",
228 + "enable_silent_payments_scanning": "Mu ki awọn sisanwo ipalọlọ",
229 "enabled": "Wọ́n tíwọn ti tan",
230 "enter_amount": "Tẹ̀ iye",
231 "enter_backup_password": "Tẹ̀ ọ̀rọ̀ aṣínà ti ẹ̀dà ḿbí",
@@ -279,6 +283,7 @@
283 "extracted_address_content": "Ẹ máa máa fi owó ránṣẹ́ sí\n${recipient_name}",
284 "failed_authentication": "Ìfẹ̀rílàdí pipòfo. ${state_error}",
285 "faq": "Àwọn ìbéèrè l'a máa ń bèèrè",
286 + "features": "Awọn ẹya",
287 "fetching": "ń wá",
288 "fiat_api": "Ojú ètò áàpù owó tí ìjọba pàṣẹ wa lò",
289 "fiat_balance": "Fiat Iwontunws.funfun",
@@ -532,6 +537,7 @@
537 "save_backup_password_alert": "Pamọ́ ọ̀rọ̀ aṣínà ti ẹ̀dà",
538 "save_to_downloads": "Fipamọ si Awọn igbasilẹ",
539 "saved_the_trade_id": "Mo ti pamọ́ àmì ìdánimọ̀ pàṣípààrọ̀",
540 + "scan_one_block": "Ọlọjẹ ọkan bulọki",
541 "scan_qr_code": "Yan QR koodu",
542 "scan_qr_code_to_get_address": "Ṣayẹwo koodu QR naa lati gba adirẹsi naa",
543 "scan_qr_on_device": "Ṣe ayẹwo koodu QR yii lori ẹrọ miiran",
@@ -642,11 +648,22 @@
648 "sign_up": "Forúkọ sílẹ̀",
649 "signTransaction": "Wole Idunadura",
650 "signup_for_card_accept_terms": "Ẹ f'orúkọ sílẹ̀ láti gba káàdì àti àjọrò.",
651 + "silent_payments": "Awọn sisanwo ipalọlọ",
652 + "silent_payments_always_scan": "Ṣeto awọn sisanwo ipalọlọ nigbagbogbo n ṣatunṣe",
653 + "silent_payments_disclaimer": "Awọn adirẹsi tuntun kii ṣe awọn idanimọ tuntun. O jẹ yiyan ti idanimọ ti o wa pẹlu aami oriṣiriṣi.",
654 + "silent_payments_display_card": "Ṣafihan kaadi isanwo ti o dakẹ",
655 + "silent_payments_scan_from_date": "Scan lati ọjọ",
656 + "silent_payments_scan_from_date_or_blockheight": "Jọwọ tẹ giga idibo ti o fẹ bẹrẹ ọlọjẹ fun awọn sisanwo ipalọlọ, tabi, lo ọjọ dipo. O le yan ti apamọwọ naa tẹsiwaju nṣapẹẹrẹ gbogbo bulọọki, tabi ṣayẹwo nikan giga ti o sọ tẹlẹ.",
657 + "silent_payments_scan_from_height": "Scan lati Iga Iga",
658 + "silent_payments_scanned_tip": "Ṣayẹwo si sample! (${tip})",
659 + "silent_payments_scanning": "Awọn sisanwo ipalọlọ",
660 + "silent_payments_settings": "Awọn eto isanwo ti o dakẹ",
661 "slidable": "Slidable",
662 "sort_by": "Sa pelu",
663 "spend_key_private": "Kọ́kọ́rọ́ sísan (àdáni)",
664 "spend_key_public": "Kọ́kọ́rọ́ sísan (kò àdáni)",
665 "status": "Tó ń ṣẹlẹ̀: ",
666 + "string_default": "Aiyipada",
667 "subaddress_title": "Àkọsílẹ̀ ni nínú àwọn àdírẹ́sì tíwọn rẹ̀lẹ̀",
668 "subaddresses": "Àwọn àdírẹ́sì kékeré",
669 "submit_request": "Ṣé ìbéèrè",
@@ -671,10 +688,13 @@
688 "sync_status_starting_sync": "Ń BẸ̀RẸ̀ RẸ́",
689 "sync_status_syncronized": "TI MÚDỌ́GBA",
690 "sync_status_syncronizing": "Ń MÚDỌ́GBA",
691 + "sync_status_timed_out": "Ti akoko jade",
692 + "sync_status_unsupported": "Ile-igbimọ ti ko ni atilẹyin",
693 "syncing_wallet_alert_content": "Iwontunws.funfun rẹ ati atokọ idunadura le ma pari titi ti yoo fi sọ “SYNCHRONIZED” ni oke. Tẹ/tẹ ni kia kia lati ni imọ siwaju sii.",
694 "syncing_wallet_alert_title": "Apamọwọ rẹ n muṣiṣẹpọ",
695 "template": "Àwòṣe",
696 "template_name": "Orukọ Awoṣe",
697 + "testnet_coins_no_value": "Awọn aṣọ irekọja ko ni iye",
698 "third_intro_content": "A sì lè lo Yats níta Cake Wallet. A lè rọ́pò Àdírẹ́sì kankan àpamọ́wọ́ fún Yat!",
699 "third_intro_title": "Àlàáfíà ni Yat àti àwọn ìmíìn jọ wà",
700 "thorchain_contract_address_not_supported": "Thorchain ko ṣe atilẹyin fifiranṣẹ si adirẹsi adehun kan",
@@ -750,13 +770,16 @@
770 "trusted": "A ti fọkàn ẹ̀ tán",
771 "tx_commit_exception_no_dust_on_change": "Iṣowo naa ti kọ pẹlu iye yii. Pẹlu awọn owó wọnyi o le firanṣẹ ${min} laisi ayipada tabi ${max} ni iyipada iyipada.",
772 "tx_commit_failed": "Idunadura iṣowo kuna. Jọwọ kan si atilẹyin.",
773 + "tx_invalid_input": "O nlo iru titẹ nkan ti ko tọ fun iru isanwo yii",
774 "tx_no_dust_exception": "Iṣowo naa ni kọ nipa fifiranṣẹ iye ti o kere ju. Jọwọ gbiyanju pọ si iye naa.",
775 "tx_not_enough_inputs_exception": "Ko to awọn titẹsi to. Jọwọ yan diẹ sii labẹ iṣakoso owo",
776 + "tx_rejected_bip68_final": "Iṣowo ni awọn igbewọle gbangba ati kuna lati rọpo nipasẹ owo.",
777 "tx_rejected_dust_change": "Idunadura kọ nipasẹ awọn ofin nẹtiwọọki, iye iyipada kekere (eruku). Gbiyanju lati firanṣẹ gbogbo rẹ tabi dinku iye.",
778 "tx_rejected_dust_output": "Idunadura kọ nipasẹ awọn ofin nẹtiwọọki, iye ti o wuwe kekere (eruku). Jọwọ mu iye naa pọ si.",
779 "tx_rejected_dust_output_send_all": "Idunadura kọ nipasẹ awọn ofin nẹtiwọọki, iye ti o wuwe kekere (eruku). Jọwọ ṣayẹwo dọgbadọgba ti awọn owo ti a yan labẹ iṣakoso owo.",
780 "tx_rejected_vout_negative": "Iwontunws.funfun ti o to lati sanwo fun awọn idiyele iṣowo yii. Jọwọ ṣayẹwo iwọntunwọnsi ti awọn owo labẹ iṣakoso owo.",
781 "tx_wrong_balance_exception": "O ko ni to ${currency} lati firanṣẹ iye yii.",
782 + "tx_wrong_balance_with_amount_exception": "O ko ni to ${currency} Lati firanṣẹ lapapọ iye ${amount}",
783 "tx_zero_fee_exception": "Ko le firanṣẹ idunadura pẹlu ọya 0. Gbiyanju jijẹ oṣuwọn tabi ṣayẹwo asopọ rẹ fun awọn iṣiro tuntun.",
784 "unavailable_balance": "Iwontunwonsi ti ko si",
785 "unavailable_balance_description": "Iwontunws.funfun ti ko si: Lapapọ yii pẹlu awọn owo ti o wa ni titiipa ni awọn iṣowo isunmọ ati awọn ti o ti didi ni itara ninu awọn eto iṣakoso owo rẹ. Awọn iwọntunwọnsi titiipa yoo wa ni kete ti awọn iṣowo oniwun wọn ba ti pari, lakoko ti awọn iwọntunwọnsi tio tutunini ko ni iraye si fun awọn iṣowo titi iwọ o fi pinnu lati mu wọn kuro.",
@@ -810,6 +833,7 @@
833 "warning": "Ikilo",
834 "welcome": "Ẹ káàbọ sí",
835 "welcome_to_cakepay": "Ẹ káàbọ̀ sí Cake Pay!",
836 + "what_is_silent_payments": "Kini awọn sisanwo ipalọlọ?",
837 "widgets_address": "Àdírẹ́sì",
838 "widgets_or": "tàbí",
839 "widgets_restore_from_blockheight": "Dá padà sípò láti gíga àkójọpọ̀",
res/values/strings_zh.arb
+24
@@ -71,6 +71,7 @@
71 "backup": "备份",
72 "backup_file": "备份文件",
73 "backup_password": "备份密码",
74 + "balance": "平衡",
75 "balance_page": "余额页",
76 "bill_amount": "账单金额",
77 "billing_address_info": "如果要求提供帐单地址,请提供您的送货地址",
@@ -78,6 +79,7 @@
79 "bitcoin_dark_theme": "比特币黑暗主题",
80 "bitcoin_light_theme": "比特币浅色主题",
81 "bitcoin_payments_require_1_confirmation": "比特币支付需要 1 次确认,这可能需要 20 分钟或更长时间。谢谢你的耐心!确认付款后,您将收到电子邮件。",
82 + "block_remaining": "剩下1个块",
83 "Blocks_remaining": "${status} 剩余的块",
84 "bluetooth": "蓝牙",
85 "bright_theme": "明亮",
@@ -139,6 +141,7 @@
141 "confirm_fee_deduction": "确认费用扣除",
142 "confirm_fee_deduction_content": "您是否同意从产出中扣除费用?",
143 "confirm_sending": "确认发送",
144 + "confirm_silent_payments_switch_node": "目前需要切换节点来扫描无声付款",
145 "confirmations": "确认",
146 "confirmed": "确认余额",
147 "confirmed_tx": "确认的",
@@ -221,6 +224,7 @@
224 "electrum_address_disclaimer": "每次您使用一个地址时,我们都会生成新地址,但之前的地址仍然有效",
225 "email_address": "电子邮件地址",
226 "enable_replace_by_fee": "启用by-Fee替换",
227 + "enable_silent_payments_scanning": "启用无声付款扫描",
228 "enabled": "启用",
229 "enter_amount": "输入金额",
230 "enter_backup_password": "在此处输入備用密码",
@@ -278,6 +282,7 @@
282 "extracted_address_content": "您将汇款至\n${recipient_name}",
283 "failed_authentication": "身份验证失败. ${state_error}",
284 "faq": "FAQ",
285 + "features": "特征",
286 "fetching": "正在获取",
287 "fiat_api": "法币API",
288 "fiat_balance": "法币余额",
@@ -531,6 +536,7 @@
536 "save_backup_password_alert": "保存备份密码",
537 "save_to_downloads": "保存到下载",
538 "saved_the_trade_id": "我已经保存了交易编号",
539 + "scan_one_block": "扫描一个街区",
540 "scan_qr_code": "扫描二维码",
541 "scan_qr_code_to_get_address": "扫描二维码获取地址",
542 "scan_qr_on_device": "在另一台设备上扫描此二维码",
@@ -641,11 +647,22 @@
647 "sign_up": "注册",
648 "signTransaction": "签署交易",
649 "signup_for_card_accept_terms": "注册卡并接受条款。",
650 + "silent_payments": "无声付款",
651 + "silent_payments_always_scan": "设置无声付款总是扫描",
652 + "silent_payments_disclaimer": "新地址不是新的身份。这是重复使用具有不同标签的现有身份。",
653 + "silent_payments_display_card": "显示无声支付卡",
654 + "silent_payments_scan_from_date": "从日期开始扫描",
655 + "silent_payments_scan_from_date_or_blockheight": "请输入您要开始扫描输入静音付款的块高度,或者使用日期。您可以选择钱包是否继续扫描每个块,或仅检查指定的高度。",
656 + "silent_payments_scan_from_height": "从块高度扫描",
657 + "silent_payments_scanned_tip": "扫描到小费! (${tip})",
658 + "silent_payments_scanning": "无声付款扫描",
659 + "silent_payments_settings": "无声付款设置",
660 "slidable": "可滑动",
661 "sort_by": "排序方式",
662 "spend_key_private": "Spend 密钥 (私钥)",
663 "spend_key_public": "Spend 密钥 (公钥)",
664 "status": "状态: ",
665 + "string_default": "默认",
666 "subaddress_title": "子地址列表",
667 "subaddresses": "子地址",
668 "submit_request": "提交请求",
@@ -670,10 +687,13 @@
687 "sync_status_starting_sync": "开始同步",
688 "sync_status_syncronized": "已同步",
689 "sync_status_syncronizing": "正在同步",
690 + "sync_status_timed_out": "时间到",
691 + "sync_status_unsupported": "不支持的节点",
692 "syncing_wallet_alert_content": "您的余额和交易列表可能不完整,直到顶部显示“已同步”。单击/点击以了解更多信息。",
693 "syncing_wallet_alert_title": "您的钱包正在同步",
694 "template": "模板",
695 "template_name": "模板名称",
696 + "testnet_coins_no_value": "TestNet硬币没有价值",
697 "third_intro_content": "Yats 也住在 Cake Wallet 之外。 地球上任何一個錢包地址都可以用一個Yat來代替!",
698 "third_intro_title": "Yat 和別人玩得很好",
699 "thorchain_contract_address_not_supported": "Thorchain不支持发送到合同地址",
@@ -749,13 +769,16 @@
769 "trusted": "值得信赖",
770 "tx_commit_exception_no_dust_on_change": "交易被此金额拒绝。使用这些硬币,您可以发送${min}无需更改或返回${max}的变化。",
771 "tx_commit_failed": "交易承诺失败。请联系支持。",
772 + "tx_invalid_input": "您正在使用错误的输入类型进行此类付款",
773 "tx_no_dust_exception": "通过发送太小的金额来拒绝交易。请尝试增加金额。",
774 "tx_not_enough_inputs_exception": "没有足够的输入。请在硬币控制下选择更多",
775 + "tx_rejected_bip68_final": "交易未确认投入,未能取代费用。",
776 "tx_rejected_dust_change": "交易被网络规则拒绝,较低的变化数量(灰尘)。尝试发送全部或减少金额。",
777 "tx_rejected_dust_output": "交易被网络规则,低输出量(灰尘)拒绝。请增加金额。",
778 "tx_rejected_dust_output_send_all": "交易被网络规则,低输出量(灰尘)拒绝。请检查在硬币控制下选择的硬币的余额。",
779 "tx_rejected_vout_negative": "没有足够的余额来支付此交易费用。请检查硬币控制下的硬币余额。",
780 "tx_wrong_balance_exception": "您没有足够的${currency}来发送此金额。",
781 + "tx_wrong_balance_with_amount_exception": "您没有足够的${currency} ${amount}",
782 "tx_zero_fee_exception": "无法以0费用发送交易。尝试提高速率或检查连接以获取最新估计。",
783 "unavailable_balance": "不可用余额",
784 "unavailable_balance_description": "不可用余额:此总额包括锁定在待处理交易中的资金以及您在硬币控制设置中主动冻结的资金。一旦各自的交易完成,锁定的余额将变得可用,而冻结的余额在您决定解冻之前仍然无法进行交易。",
@@ -809,6 +832,7 @@
832 "warning": "警告",
833 "welcome": "欢迎使用",
834 "welcome_to_cakepay": "欢迎来到 Cake Pay!",
835 + "what_is_silent_payments": "什么是无声付款?",
836 "widgets_address": "地址",
837 "widgets_or": "或者",
838 "widgets_restore_from_blockheight": "从块高还原",
scripts/android/app_env.fish new
+75
@@ -0,0 +1,75 @@
1 +#!/usr/bin/env fish
2 +
3 +set APP_ANDROID_NAME ""
4 +set APP_ANDROID_VERSION ""
5 +set APP_ANDROID_BUILD_VERSION ""
6 +set APP_ANDROID_ID ""
7 +set APP_ANDROID_PACKAGE ""
8 +set APP_ANDROID_SCHEME ""
9 +
10 +set MONERO_COM "monero.com"
11 +set CAKEWALLET "cakewallet"
12 +set HAVEN "haven"
13 +
14 +set -l TYPES $MONERO_COM $CAKEWALLET $HAVEN
15 +set APP_ANDROID_TYPE $argv[1]
16 +
17 +set MONERO_COM_NAME "Monero.com"
18 +set MONERO_COM_VERSION "1.12.3"
19 +set MONERO_COM_BUILD_NUMBER 84
20 +set MONERO_COM_BUNDLE_ID "com.monero.app"
21 +set MONERO_COM_PACKAGE "com.monero.app"
22 +set MONERO_COM_SCHEME "monero.com"
23 +
24 +set CAKEWALLET_NAME "Cake Wallet"
25 +set CAKEWALLET_VERSION "4.15.6"
26 +set CAKEWALLET_BUILD_NUMBER 207
27 +set CAKEWALLET_BUNDLE_ID "com.cakewallet.cake_wallet"
28 +set CAKEWALLET_PACKAGE "com.cakewallet.cake_wallet"
29 +set CAKEWALLET_SCHEME "cakewallet"
30 +
31 +set HAVEN_NAME "Haven"
32 +set HAVEN_VERSION "1.0.0"
33 +set HAVEN_BUILD_NUMBER 1
34 +set HAVEN_BUNDLE_ID "com.cakewallet.haven"
35 +set HAVEN_PACKAGE "com.cakewallet.haven"
36 +
37 +if not contains $APP_ANDROID_TYPE $TYPES
38 + echo "Wrong app type."
39 + return 1
40 + exit 1
41 +end
42 +
43 +switch $APP_ANDROID_TYPE
44 + case $MONERO_COM
45 + set APP_ANDROID_NAME $MONERO_COM_NAME
46 + set APP_ANDROID_VERSION $MONERO_COM_VERSION
47 + set APP_ANDROID_BUILD_NUMBER $MONERO_COM_BUILD_NUMBER
48 + set APP_ANDROID_BUNDLE_ID $MONERO_COM_BUNDLE_ID
49 + set APP_ANDROID_PACKAGE $MONERO_COM_PACKAGE
50 + set APP_ANDROID_SCHEME $MONERO_COM_SCHEME
51 + ;;
52 + case $CAKEWALLET
53 + set APP_ANDROID_NAME $CAKEWALLET_NAME
54 + set APP_ANDROID_VERSION $CAKEWALLET_VERSION
55 + set APP_ANDROID_BUILD_NUMBER $CAKEWALLET_BUILD_NUMBER
56 + set APP_ANDROID_BUNDLE_ID $CAKEWALLET_BUNDLE_ID
57 + set APP_ANDROID_PACKAGE $CAKEWALLET_PACKAGE
58 + set APP_ANDROID_SCHEME $CAKEWALLET_SCHEME
59 + ;;
60 + case $HAVEN
61 + set APP_ANDROID_NAME $HAVEN_NAME
62 + set APP_ANDROID_VERSION $HAVEN_VERSION
63 + set APP_ANDROID_BUILD_NUMBER $HAVEN_BUILD_NUMBER
64 + set APP_ANDROID_BUNDLE_ID $HAVEN_BUNDLE_ID
65 + set APP_ANDROID_PACKAGE $HAVEN_PACKAGE
66 + ;;
67 +end
68 +
69 +export APP_ANDROID_TYPE
70 +export APP_ANDROID_NAME
71 +export APP_ANDROID_VERSION
72 +export APP_ANDROID_BUILD_NUMBER
73 +export APP_ANDROID_BUNDLE_ID
74 +export APP_ANDROID_PACKAGE
75 +export APP_ANDROID_SCHEME
scripts/android/app_env.sh
+4 -4
@@ -15,15 +15,15 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
15 APP_ANDROID_TYPE=$1
16
17 MONERO_COM_NAME="Monero.com"
18 -MONERO_COM_VERSION="1.14.0"
19 -MONERO_COM_BUILD_NUMBER=89
18 +MONERO_COM_VERSION="1.15.0"
19 +MONERO_COM_BUILD_NUMBER=90
20 MONERO_COM_BUNDLE_ID="com.monero.app"
21 MONERO_COM_PACKAGE="com.monero.app"
22 MONERO_COM_SCHEME="monero.com"
23
24 CAKEWALLET_NAME="Cake Wallet"
25 -CAKEWALLET_VERSION="4.17.0"
26 -CAKEWALLET_BUILD_NUMBER=215
25 +CAKEWALLET_VERSION="4.18.0"
26 +CAKEWALLET_BUILD_NUMBER=216
27 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet"
28 CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet"
29 CAKEWALLET_SCHEME="cakewallet"
scripts/ios/app_env.sh
+4 -4
@@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
13 APP_IOS_TYPE=$1
14
15 MONERO_COM_NAME="Monero.com"
16 -MONERO_COM_VERSION="1.14.0"
17 -MONERO_COM_BUILD_NUMBER=87
16 +MONERO_COM_VERSION="1.15.0"
17 +MONERO_COM_BUILD_NUMBER=88
18 MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
19
20 CAKEWALLET_NAME="Cake Wallet"
21 -CAKEWALLET_VERSION="4.17.0"
22 -CAKEWALLET_BUILD_NUMBER=244
21 +CAKEWALLET_VERSION="4.18.0"
22 +CAKEWALLET_BUILD_NUMBER=248
23 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
24
25 HAVEN_NAME="Haven"
scripts/macos/app_env.sh
+4 -4
@@ -16,13 +16,13 @@ if [ -n "$1" ]; then
16 fi
17
18 MONERO_COM_NAME="Monero.com"
19 -MONERO_COM_VERSION="1.4.0"
20 -MONERO_COM_BUILD_NUMBER=20
19 +MONERO_COM_VERSION="1.5.0"
20 +MONERO_COM_BUILD_NUMBER=21
21 MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
22
23 CAKEWALLET_NAME="Cake Wallet"
24 -CAKEWALLET_VERSION="1.10.0"
25 -CAKEWALLET_BUILD_NUMBER=76
24 +CAKEWALLET_VERSION="1.11.0"
25 +CAKEWALLET_BUILD_NUMBER=78
26 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
27
28 if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then
tool/configure.dart
+23 -11
@@ -71,28 +71,26 @@ Future<void> generateBitcoin(bool hasImplementation) async {
71 final outputFile = File(bitcoinOutputPath);
72 const bitcoinCommonHeaders = """
73 import 'dart:typed_data';
74 -import 'package:cw_core/node.dart';
74 +import 'package:bitcoin_base/bitcoin_base.dart';
75 import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
76 import 'package:cake_wallet/view_model/send/output.dart';
77 import 'package:cw_core/hardware/hardware_account_data.dart';
78 +import 'package:cw_core/node.dart';
79 +import 'package:cw_core/output_info.dart';
80 import 'package:cw_core/pending_transaction.dart';
81 import 'package:cw_core/receive_page_option.dart';
82 +import 'package:cw_core/transaction_priority.dart';
83 +import 'package:cw_core/unspent_coins_info.dart';
84 import 'package:cw_core/unspent_transaction_output.dart';
85 import 'package:cw_core/wallet_base.dart';
86 import 'package:cw_core/wallet_credentials.dart';
87 import 'package:cw_core/wallet_info.dart';
84 -import 'package:cw_core/transaction_priority.dart';
85 -import 'package:cw_core/output_info.dart';
86 -import 'package:cw_core/unspent_coins_info.dart';
88 import 'package:cw_core/wallet_service.dart';
89 import 'package:cw_core/wallet_type.dart';
90 import 'package:hive/hive.dart';
91 import 'package:ledger_flutter/ledger_flutter.dart';
91 -import 'package:bitcoin_base/bitcoin_base.dart';
92 import 'package:bitcoin_flutter/bitcoin_flutter.dart' as btc;
93 -import 'package:bip32/bip32.dart' as bip32;
93 import 'package:bip39/bip39.dart' as bip39;
95 -import 'package:hive/hive.dart';
94 """;
95 const bitcoinCWHeaders = """
96 import 'package:cw_bitcoin/utils.dart';
@@ -112,8 +110,8 @@ import 'package:cw_bitcoin/bitcoin_amount_format.dart';
110 import 'package:cw_bitcoin/bitcoin_address_record.dart';
111 import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
112 import 'package:cw_bitcoin/litecoin_wallet_service.dart';
113 +import 'package:cw_core/get_height_by_date.dart';
114 import 'package:cw_bitcoin/script_hash.dart';
116 -import 'package:cw_bitcoin/pending_bitcoin_transaction.dart';
115 import 'package:cw_bitcoin/bitcoin_hardware_wallet_service.dart';
116 import 'package:mobx/mobx.dart';
117 """;
@@ -162,8 +160,9 @@ abstract class Bitcoin {
160 Object createBitcoinTransactionCredentials(List<Output> outputs, {required TransactionPriority priority, int? feeRate});
161 Object createBitcoinTransactionCredentialsRaw(List<OutputInfo> outputs, {TransactionPriority? priority, required int feeRate});
162
165 - List<String> getAddresses(Object wallet);
163 String getAddress(Object wallet);
164 + List<ElectrumSubAddress> getSilentPaymentAddresses(Object wallet);
165 + List<ElectrumSubAddress> getSilentPaymentReceivedAddresses(Object wallet);
166
167 Future<int> estimateFakeSendAllTxAmount(Object wallet, TransactionPriority priority);
168 List<ElectrumSubAddress> getSubAddresses(Object wallet);
@@ -175,7 +174,8 @@ abstract class Bitcoin {
174
175 List<Unspent> getUnspents(Object wallet);
176 Future<void> updateUnspents(Object wallet);
178 - WalletService createBitcoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource);
177 + WalletService createBitcoinWalletService(
178 + Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan);
179 WalletService createLitecoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource);
180 TransactionPriority getBitcoinTransactionPriorityMedium();
181 TransactionPriority getBitcoinTransactionPriorityCustom();
@@ -190,13 +190,25 @@ abstract class Bitcoin {
190 ReceivePageOption getSelectedAddressType(Object wallet);
191 List<ReceivePageOption> getBitcoinReceivePageOptions();
192 BitcoinAddressType getBitcoinAddressType(ReceivePageOption option);
193 + bool hasSelectedSilentPayments(Object wallet);
194 + bool isBitcoinReceivePageOption(ReceivePageOption option);
195 + BitcoinAddressType getOptionToType(ReceivePageOption option);
196 bool hasTaprootInput(PendingTransaction pendingTransaction);
197 + bool getScanningActive(Object wallet);
198 + Future<void> setScanningActive(Object wallet, bool active);
199 + bool isTestnet(Object wallet);
200
201 Future<PendingTransaction> replaceByFee(Object wallet, String transactionHash, String fee);
202 Future<bool> canReplaceByFee(Object wallet, String transactionHash);
203 Future<bool> isChangeSufficientForFee(Object wallet, String txId, String newFee);
204 int getFeeAmountForPriority(Object wallet, TransactionPriority priority, int inputsCount, int outputsCount, {int? size});
199 - int getEstimatedFeeWithFeeRate(Object wallet, int feeRate, int? amount, {int? outputsCount, int? size});
205 + int getEstimatedFeeWithFeeRate(Object wallet, int feeRate, int? amount,
206 + {int? outputsCount, int? size});
207 + int getHeightByDate({required DateTime date});
208 + Future<void> rescan(Object wallet, {required int height, bool? doSingleScan});
209 + Future<bool> getNodeIsElectrsSPEnabled(Object wallet);
210 + void deleteSilentPaymentAddress(Object wallet, String address);
211 + Future<void> updateFeeRates(Object wallet);
212 int getMaxCustomFeeRate(Object wallet);
213
214 void setLedger(WalletBase wallet, Ledger ledger, LedgerDevice device);