Yat, electrum addresses fixes

M committed Dec 8, 2021 at 11:09 UTC 42104fd825a47ef4e8adb9326b7b6c3c5ff759d9
14 files changed +330 -133
.gitignore
+4
@@ -109,3 +109,7 @@ ios/Flutter/.last_build_id
109 ios/build
110 *.sublime-workspace
111 *.sublime-project
112 +
113 +shared_external/**
114 +cw_shared_external/**
115 +cw_haven/**
lib/bitcoin/bitcoin_wallet.dart
+5 -1
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart';
2 import 'package:cake_wallet/bitcoin/unspent_coins_info.dart';
3 import 'package:hive/hive.dart';
4 import 'package:mobx/mobx.dart';
@@ -35,7 +36,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
36 walletInfo,
37 initialAddresses: initialAddresses,
38 accountIndex: accountIndex,
38 - hd: hd,
39 + mainHd: hd,
40 + sideHd: bitcoin.HDWallet.fromSeed(
41 + mnemonicToSeedBytes(mnemonic), network: networkType)
42 + .derivePath("m/0'/1"),
43 networkType: networkType);
44 }
45
lib/bitcoin/bitcoin_wallet_addresses.dart
+4 -2
@@ -17,13 +17,15 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses
17 WalletInfo walletInfo,
18 {@required List<BitcoinAddressRecord> initialAddresses,
19 int accountIndex = 0,
20 - @required bitcoin.HDWallet hd,
20 + @required bitcoin.HDWallet mainHd,
21 + @required bitcoin.HDWallet sideHd,
22 @required this.networkType})
23 : super(
24 walletInfo,
25 initialAddresses: initialAddresses,
26 accountIndex: accountIndex,
26 - hd: hd);
27 + mainHd: mainHd,
28 + sideHd: sideHd);
29
30 bitcoin.NetworkType networkType;
31
lib/bitcoin/electrum_wallet_addresses.dart
+30 -8
@@ -14,19 +14,23 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
14 ElectrumWalletAddressesBase(WalletInfo walletInfo,
15 {@required List<BitcoinAddressRecord> initialAddresses,
16 int accountIndex = 0,
17 - @required bitcoin.HDWallet hd})
17 + this.mainHd,
18 + this.sideHd})
19 : super(walletInfo) {
19 - this.hd = hd;
20 this.accountIndex = accountIndex;
21 addresses = ObservableList<BitcoinAddressRecord>.of(
22 (initialAddresses ?? []).toSet());
23 }
24
25 + static const regularAddressesCount = 22;
26 + static const hiddenAddressesCount = 17;
27 +
28 @override
29 @observable
30 String address;
31
29 - bitcoin.HDWallet hd;
32 + bitcoin.HDWallet mainHd;
33 + bitcoin.HDWallet sideHd;
34
35 ObservableList<BitcoinAddressRecord> addresses;
36
@@ -53,19 +57,36 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
57 }
58
59 Future<void> generateAddresses() async {
56 - if (addresses.length < 33) {
57 - final addressesCount = 33 - addresses.length;
60 + final regularAddresses = <BitcoinAddressRecord>[];
61 + final hiddenAddresses = <BitcoinAddressRecord>[];
62 +
63 + addresses.forEach((addr) {
64 + if (addr.isHidden) {
65 + hiddenAddresses.add(addr);
66 + return;
67 + }
68 +
69 + regularAddresses.add(addr);
70 + });
71 +
72 + if (regularAddresses.length < regularAddressesCount) {
73 + final addressesCount = regularAddressesCount - regularAddresses.length;
74 + await generateNewAddresses(addressesCount,
75 + startIndex: regularAddresses.length, hd: mainHd, isHidden: false);
76 + }
77 +
78 + if (hiddenAddresses.length < hiddenAddressesCount) {
79 + final addressesCount = hiddenAddressesCount - hiddenAddresses.length;
80 await generateNewAddresses(addressesCount,
59 - startIndex: addresses.length, hd: hd);
81 + startIndex: hiddenAddresses.length, hd: sideHd, isHidden: true);
82 }
83 }
84
85 Future<BitcoinAddressRecord> generateNewAddress(
86 {bool isHidden = false, bitcoin.HDWallet hd}) async {
87 accountIndex += 1;
66 - final _hd = hd ?? this.hd;
88 final address = BitcoinAddressRecord(
68 - getAddress(index: accountIndex, hd: _hd),
89 + getAddress(index: accountIndex, hd: hd),
90 index: accountIndex,
91 isHidden: isHidden);
92 addresses.add(address);
@@ -79,6 +100,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
100 for (var i = startIndex; i < count + startIndex; i++) {
101 final address = BitcoinAddressRecord(getAddress(index: i, hd: hd),
102 index: i, isHidden: isHidden);
103 + print(address.address);
104 list.add(address);
105 }
106
lib/bitcoin/litecoin_wallet.dart
+7 -3
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart';
2 import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart';
3 import 'package:cake_wallet/bitcoin/unspent_coins_info.dart';
4 import 'package:cake_wallet/bitcoin/litecoin_wallet_addresses.dart';
@@ -11,6 +12,7 @@ import 'package:cake_wallet/bitcoin/electrum_wallet.dart';
12 import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
13 import 'package:cake_wallet/bitcoin/electrum_balance.dart';
14 import 'package:cake_wallet/bitcoin/litecoin_network.dart';
15 +import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
16
17 part 'litecoin_wallet.g.dart';
18
@@ -37,9 +39,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
39 walletInfo,
40 initialAddresses: initialAddresses,
41 accountIndex: accountIndex,
40 - hd: hd,
41 - networkType: networkType,
42 - mnemonic: mnemonic);
42 + mainHd: hd,
43 + sideHd: bitcoin.HDWallet
44 + .fromSeed(mnemonicToSeedBytes(mnemonic), network: networkType)
45 + .derivePath("m/0'/1"),
46 + networkType: networkType,);
47 }
48
49 static Future<LitecoinWallet> open({
lib/bitcoin/litecoin_wallet_addresses.dart
+7 -13
@@ -18,18 +18,18 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses
18 WalletInfo walletInfo,
19 {@required List<BitcoinAddressRecord> initialAddresses,
20 int accountIndex = 0,
21 - @required bitcoin.HDWallet hd,
22 - @required this.networkType,
23 - @required this.mnemonic})
21 + @required bitcoin.HDWallet mainHd,
22 + @required bitcoin.HDWallet sideHd,
23 + @required this.networkType})
24 : super(
25 walletInfo,
26 initialAddresses: initialAddresses,
27 accountIndex: accountIndex,
28 - hd: hd);
28 + mainHd: mainHd,
29 + sideHd: sideHd);
30
31 bitcoin.NetworkType networkType;
32
32 - final String mnemonic;
33
34 @override
35 String getAddress({@required int index, @required bitcoin.HDWallet hd}) =>
@@ -40,15 +40,9 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses
40 if (addresses.length < 33) {
41 final addressesCount = 22 - addresses.length;
42 await generateNewAddresses(addressesCount,
43 - hd: hd, startIndex: addresses.length);
44 -
45 - final changeRoot = bitcoin.HDWallet.fromSeed(
46 - mnemonicToSeedBytes(mnemonic),
47 - network: networkType)
48 - .derivePath("m/0'/1");
49 -
43 + hd: mainHd, startIndex: addresses.length);
44 await generateNewAddresses(11,
51 - startIndex: 0, hd: changeRoot, isHidden: true);
45 + startIndex: 0, hd: sideHd, isHidden: true);
46 }
47 }
48 }
\ No newline at end of file
lib/di.dart
+3 -2
@@ -194,8 +194,9 @@ Future setup(
194 getIt.registerSingleton<ExchangeTemplateStore>(
195 ExchangeTemplateStore(templateSource: _exchangeTemplates));
196 getIt.registerSingleton<YatStore>(YatStore(
197 - appStore: getIt.get<AppStore>()
198 - ));
197 + appStore: getIt.get<AppStore>(),
198 + secureStorage: getIt.get<FlutterSecureStorage>())
199 + ..init());
200
201 final secretStore =
202 await SecretStoreBase.load(getIt.get<FlutterSecureStorage>());
lib/entities/wallet_info.dart
+17 -6
@@ -1,6 +1,7 @@
1 import 'package:flutter/foundation.dart';
2 import 'package:hive/hive.dart';
3 import 'package:cake_wallet/entities/wallet_type.dart';
4 +import 'dart:async';
5
6 part 'wallet_info.g.dart';
7
@@ -8,7 +9,8 @@ part 'wallet_info.g.dart';
9 class WalletInfo extends HiveObject {
10 WalletInfo(this.id, this.name, this.type, this.isRecovery, this.restoreHeight,
11 this.timestamp, this.dirPath, this.path, this.address, this.yatEid,
11 - this.yatRefreshToken);
12 + this.yatLastUsedAddressRaw)
13 + : _yatLastUsedAddressController = StreamController<String>.broadcast();
14
15 factory WalletInfo.external(
16 {@required String id,
@@ -21,10 +23,10 @@ class WalletInfo extends HiveObject {
23 @required String path,
24 @required String address,
25 String yatEid ='',
24 - String yatRefreshToken = ''}) {
26 + String yatLastUsedAddressRaw = ''}) {
27 return WalletInfo(id, name, type, isRecovery, restoreHeight,
28 date.millisecondsSinceEpoch ?? 0, dirPath, path, address,
27 - yatEid, yatRefreshToken);
29 + yatEid, yatLastUsedAddressRaw);
30 }
31
32 static const typeId = 4;
@@ -64,11 +66,20 @@ class WalletInfo extends HiveObject {
66 String yatEid;
67
68 @HiveField(12)
67 - String yatRefreshToken;
69 + String yatLastUsedAddressRaw;
70
69 - String get yatEmojiId => yatEid ?? '';
71 + String get yatLastUsedAddress => yatLastUsedAddressRaw;
72 +
73 + set yatLastUsedAddress(String address) {
74 + yatLastUsedAddressRaw = address;
75 + _yatLastUsedAddressController.add(address);
76 + }
77
71 - String get yatToken => yatRefreshToken ?? '';
78 + String get yatEmojiId => yatEid ?? '';
79
80 DateTime get date => DateTime.fromMillisecondsSinceEpoch(timestamp);
81 +
82 + Stream<String> get yatLastUsedAddressStream => _yatLastUsedAddressController.stream;
83 +
84 + StreamController<String> _yatLastUsedAddressController;
85 }
lib/main.dart
-2
@@ -110,8 +110,6 @@ Future<void> main() async {
110 final unspentCoinsInfoSource =
111 await Hive.openBox<UnspentCoinsInfo>(UnspentCoinsInfo.boxName);
112
113 - await visualisationForEmojiId('%E2%98%A0%EF%B8%8F%F0%9F%90%99%E2%98%A0%EF%B8%8F');
114 -
113 await initialSetup(
114 sharedPreferences: await SharedPreferences.getInstance(),
115 nodes: nodes,
lib/reactions/on_current_wallet_change.dart
+24
@@ -13,14 +13,38 @@ import 'package:cake_wallet/store/settings_store.dart';
13 import 'package:cake_wallet/core/fiat_conversion_service.dart';
14 import 'package:cake_wallet/core/wallet_base.dart';
15 import 'package:cake_wallet/entities/wallet_type.dart';
16 +import 'package:cake_wallet/store/yat/yat_store.dart';
17
18 ReactionDisposer _onCurrentWalletChangeReaction;
19 ReactionDisposer _onCurrentWalletChangeFiatRateUpdateReaction;
20 +ReactionDisposer _onCurrentWalletAddressChangeReaction;
21
22 void startCurrentWalletChangeReaction(AppStore appStore,
23 SettingsStore settingsStore, FiatConversionStore fiatConversionStore) {
24 _onCurrentWalletChangeReaction?.reaction?.dispose();
25 _onCurrentWalletChangeFiatRateUpdateReaction?.reaction?.dispose();
26 + _onCurrentWalletAddressChangeReaction?.reaction?.dispose();
27 +
28 + _onCurrentWalletAddressChangeReaction = reaction((_) => appStore.wallet.walletAddresses.address,
29 + (String address) async {
30 + if (address == appStore.wallet.walletInfo.yatLastUsedAddress) {
31 + return;
32 + }
33 +
34 + try {
35 + final yatStore = getIt.get<YatStore>();
36 + await updateEmojiIdAddress(
37 + appStore.wallet.walletInfo.yatEmojiId,
38 + appStore.wallet.walletAddresses.address,
39 + yatStore.apiKey,
40 + appStore.wallet.type
41 + );
42 + appStore.wallet.walletInfo.yatLastUsedAddress = address;
43 + await appStore.wallet.walletInfo.save();
44 + } catch (e) {
45 + print(e.toString());
46 + }
47 + });
48
49 _onCurrentWalletChangeReaction = reaction((_) => appStore.wallet, (WalletBase<
50 Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>
lib/src/screens/dashboard/dashboard_page.dart
+37 -2
@@ -98,6 +98,10 @@ class DashboardPage extends BasePage {
98 height: 22.24,
99 width: 24,
100 color: Theme.of(context).accentTextTheme.display3.backgroundColor);
101 + final sellImage = Image.asset('assets/images/restore_wallet_image.png',
102 + height: 22.24,
103 + width: 24,
104 + color: Theme.of(context).accentTextTheme.display3.backgroundColor);
105 _setEffects(context);
106
107 return SafeArea(
@@ -144,6 +148,12 @@ class DashboardPage extends BasePage {
148 onClick: () async =>
149 await _onClickBuyButton(context),
150 ),
151 + ActionButton(
152 + image: sellImage,
153 + title: 'Sell',
154 + onClick: () async =>
155 + await _onClickSellButton(context),
156 + ),
157 ],
158 ),
159 )
@@ -227,7 +237,13 @@ class DashboardPage extends BasePage {
237 final walletType = walletViewModel.type;
238
239 switch (walletType) {
230 - case WalletType.monero:
240 + case WalletType.bitcoin:
241 + Navigator.of(context).pushNamed(Routes.preOrder);
242 + break;
243 + case WalletType.litecoin:
244 + Navigator.of(context).pushNamed(Routes.preOrder);
245 + break;
246 + default:
247 await showPopUp<void>(
248 context: context,
249 builder: (BuildContext context) {
@@ -237,10 +253,29 @@ class DashboardPage extends BasePage {
253 buttonText: S.of(context).ok,
254 buttonAction: () => Navigator.of(context).pop());
255 });
256 + }
257 + }
258 +
259 + Future<void> _onClickSellButton(BuildContext context) async {
260 + final walletType = walletViewModel.type;
261 +
262 + switch (walletType) {
263 + case WalletType.bitcoin:
264 + Navigator.of(context).pushNamed(Routes.preOrder);
265 break;
241 - default:
266 + case WalletType.litecoin:
267 Navigator.of(context).pushNamed(Routes.preOrder);
268 break;
269 + default:
270 + await showPopUp<void>(
271 + context: context,
272 + builder: (BuildContext context) {
273 + return AlertWithOneAction(
274 + alertTitle: S.of(context).buy,
275 + alertContent: S.of(context).buy_alert_content,
276 + buttonText: S.of(context).ok,
277 + buttonAction: () => Navigator.of(context).pop());
278 + });
279 }
280 }
281 }
lib/src/screens/root/root.dart
+1 -1
@@ -32,7 +32,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
32
33 @override
34 void initState() {
35 - _isInactiveController = StreamController<bool>();
35 + _isInactiveController = StreamController<bool>.broadcast();
36 _isInactive = false;
37 _postFrameCallback = false;
38 WidgetsBinding.instance.addObserver(this);
lib/store/yat/yat_store.dart
+161 -91
@@ -13,6 +13,7 @@ import 'dart:convert';
13 import 'package:cake_wallet/store/yat/yat_exception.dart';
14 import 'package:http/http.dart';
15 import 'dart:async';
16 +import 'package:flutter_secure_storage/flutter_secure_storage.dart';
17
18 part 'yat_store.g.dart';
19
@@ -23,61 +24,135 @@ class YatLink {
24 static const signInSuffix = '/partner/$partnerId/link-email';
25 static const createSuffix = '/create';
26 static const managePath = '/partner/$partnerId/manage';
26 - static const queryParameter = '?addresses=';
27 + static const queryParameter = '?address_json=';
28 + static const apiDevUrl = 'https://a.yat.fyi';
29 + static const apiReleaseUrl = 'https://a.y.at';
30 static const requestDevUrl = 'https://a.yat.fyi/emoji_id/';
31 static const requestReleaseUrl = 'https://a.y.at/emoji_id/';
29 - static const startFlowUrl = 'https://www.y03btrk.com/4RQSJ/55M6S/';
30 - static const isDevMode = false;
32 + static const startFlowUrl = 'https://www.y03btrk.com/4RQSJ/6JHXF/';
33 + static const isDevMode = true;
34 static const tags = <String, List<String>>{"XMR" : ['0x1001', '0x1002'],
35 "BTC" : ['0x1003'], "LTC" : ['0x3fff']};
36
37 + static String get apiUrl => YatLink.isDevMode
38 + ? YatLink.apiDevUrl
39 + : YatLink.apiReleaseUrl;
40 +
41 + static String get emojiIdUrl => apiUrl + '/emoji_id/';
42 +
43 static String get baseUrl => YatLink.isDevMode
44 ? YatLink.baseDevUrl
45 : YatLink.baseReleaseUrl;
46 }
47
48 Future<List<String>> fetchYatAddress(String emojiId, String ticker) async {
40 - final requestURL = YatLink.isDevMode
41 - ? YatLink.requestDevUrl
42 - : YatLink.requestReleaseUrl;
43 - final url = requestURL + emojiId;
49 + final url = YatLink.emojiIdUrl + emojiId + '/payment';
50 final response = await get(url);
45 -
51 +
52 if (response.statusCode != 200) {
53 throw YatException(text: response.body.toString());
54 }
49 -
55 +
56 + final addresses = <String>[];
57 + final currency = ticker.toUpperCase();
58 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
51 - final result = responseJSON['result'] as List<dynamic>;
52 -
53 - if (result?.isEmpty ?? true) {
54 - return [];
55 - }
59 + final result = responseJSON['result'] as Map<dynamic, dynamic>;
60 + result.forEach((dynamic key, dynamic value) {
61 + final tag = key as String ?? '';
62 + final record = value as Map<String, dynamic>;
63 +
64 + if (YatLink.tags[currency]?.contains(tag) ?? false) {
65 + final address = record['address'] as String;
66 + if (address?.isNotEmpty ?? false) {
67 + addresses.add(address);
68 + }
69 + }
70 + });
71
57 - final List<String> addresses = [];
58 - final currency = ticker.toUpperCase();
72 + return addresses;
73 +}
74
60 - for (var elem in result) {
61 - final tag = elem['tag'] as String;
62 - if (tag?.isEmpty ?? true) {
63 - continue;
75 +Future<String> fetchYatAccessToken(String refreshToken) async {
76 + try {
77 + final url = YatLink.apiUrl + '/auth/token/refresh';
78 + final bodyJson = json.encode({'refresh_token': refreshToken});
79 + final response = await post(
80 + url,
81 + headers: <String, String>{
82 + 'Content-Type': 'application/json',
83 + 'Accept': '*/*'
84 + },
85 + body: bodyJson);
86 +
87 + if (response.statusCode != 200) {
88 + throw YatException(text: response.body.toString());
89 }
65 - if (YatLink.tags[currency]?.contains(tag) ?? false) {
66 - final yatAddress = elem['data'] as String;
67 - if (yatAddress?.isNotEmpty ?? false) {
68 - addresses.add(yatAddress);
69 - }
90 +
91 + final responseJSON = json.decode(response.body) as Map<String, dynamic>;
92 + return responseJSON['access_token'] as String;
93 + }catch(_) {
94 + return '';
95 + }
96 +}
97 +
98 +Future<String> fetchYatApiKey(String accessKey) async {
99 + try {
100 + final url = YatLink.apiUrl + '/api_keys';
101 + final bodyJson = json.encode({'name': 'CW'});
102 + final response = await post(
103 + url,
104 + headers: <String, String>{
105 + 'Authorization': 'Bearer $accessKey',
106 + 'Content-Type': 'application/json',
107 + 'Accept': '*/*'
108 + },
109 + body: bodyJson);
110 +
111 + if (response.statusCode != 200) {
112 + throw YatException(text: response.body.toString());
113 }
114 +
115 + final responseJSON = json.decode(response.body) as Map<String, dynamic>;
116 + return responseJSON['api_key'] as String;
117 + }catch(_) {
118 + return '';
119 }
120 +}
121
73 - return addresses;
122 +Future<void> updateEmojiIdAddress(String emojiId, String address, String apiKey, WalletType type) async {
123 + final url = YatLink.emojiIdUrl + emojiId;
124 + final cur = walletTypeToCryptoCurrency(type);
125 + final curFormatted = cur.toString().toUpperCase();
126 + var tag = '';
127 +
128 + if (type == WalletType.monero && !address.startsWith('4')) {
129 + tag = YatLink.tags[curFormatted].last;
130 + } else {
131 + tag = YatLink.tags[curFormatted].first;
132 + }
133 +
134 + final bodyJson = json.encode({
135 + 'insert': [{
136 + 'data': address,
137 + 'tag': tag
138 + }]
139 + });
140 + final response = await patch(
141 + url,
142 + headers: <String, String>{
143 + 'x-api-key': apiKey,
144 + 'Content-Type': 'application/json',
145 + 'Accept': '*/*'
146 + },
147 + body: bodyJson);
148 +
149 + if (response.statusCode != 200) {
150 + throw YatException(text: response.body.toString());
151 + }
152 }
153
154 Future<String> visualisationForEmojiId(String emojiId) async {
77 - final requestURL = YatLink.isDevMode
78 - ? YatLink.requestDevUrl
79 - : YatLink.requestReleaseUrl;
80 - final url = requestURL + emojiId + '/json/VisualizerFileLocations';
155 + final url = YatLink.emojiIdUrl + emojiId + '/json/VisualizerFileLocations';
156 final response = await get(url);
157 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
158 final data = responseJSON['data'] as Map<String, dynamic>;
@@ -88,23 +163,39 @@ Future<String> visualisationForEmojiId(String emojiId) async {
163 class YatStore = YatStoreBase with _$YatStore;
164
165 abstract class YatStoreBase with Store {
91 - YatStoreBase({@required this.appStore}) {
166 + YatStoreBase({@required this.appStore, @required this.secureStorage}) {
167 _wallet ??= appStore.wallet;
168 emoji = _wallet?.walletInfo?.yatEmojiId ?? '';
94 - refreshToken = _wallet?.walletInfo?.yatToken ?? '';
169 reaction((_) => appStore.wallet, _onWalletChange);
96 - reaction((_) => emoji, (String emoji) => _onEmojiChange());
97 - emojiIncommingSC = StreamController<String>();
170 + reaction((_) => emoji, (String _) => _onEmojiChange());
171 + reaction((_) => refreshToken, (String _) => _onRefreshTokenChange());
172 + emojiIncommingSC = StreamController<String>.broadcast();
173 }
174
175 + static const yatRefreshTokenKeyBase = 'yat_refresh_token';
176 + static const yatAccessTokenKeyBase = 'yat_access_token';
177 + static const yatApiKeyBase = 'yat_api_key';
178 +
179 + static String yatRefreshTokenKey(String name) => '${yatRefreshTokenKeyBase}_$name';
180 + static String yatAccessTokenKey(String name) => '${yatAccessTokenKeyBase}_$name';
181 + static String yatApiKey(String name) => '${yatApiKeyBase}_$name';
182 +
183 AppStore appStore;
184
185 + FlutterSecureStorage secureStorage;
186 +
187 @observable
188 String emoji;
189
190 @observable
191 String refreshToken;
192
193 + @observable
194 + String accessToken;
195 +
196 + @observable
197 + String apiKey;
198 +
199 StreamController<String> emojiIncommingSC;
200
201 Stream<String> get emojiIncommingStream => emojiIncommingSC.stream;
@@ -113,6 +204,16 @@ abstract class YatStoreBase with Store {
204 WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>
205 _wallet;
206
207 + Future<void> init() async {
208 + if (_wallet == null) {
209 + return;
210 + }
211 +
212 + refreshToken = await secureStorage.read(key: yatRefreshTokenKey(_wallet.walletInfo.name));
213 + accessToken = await secureStorage.read(key: yatAccessTokenKey(_wallet.walletInfo.name));
214 + apiKey = await secureStorage.read(key: yatApiKey(_wallet.walletInfo.name));
215 + }
216 +
217 @action
218 void _onWalletChange(
219 WalletBase<Balance, TransactionHistoryBase<TransactionInfo>,
@@ -120,7 +221,7 @@ abstract class YatStoreBase with Store {
221 wallet) {
222 this._wallet = wallet;
223 emoji = wallet?.walletInfo?.yatEmojiId ?? '';
123 - refreshToken = wallet?.walletInfo?.yatToken ?? '';
224 + init();
225 }
226
227 @action
@@ -133,7 +234,6 @@ abstract class YatStoreBase with Store {
234 }
235
236 walletInfo.yatEid = emoji;
136 - walletInfo.yatRefreshToken = refreshToken;
237
238 if (walletInfo.isInBox) {
239 walletInfo.save();
@@ -143,62 +243,32 @@ abstract class YatStoreBase with Store {
243 }
244 }
245
246 + @action
247 + Future<void> _onRefreshTokenChange() async {
248 + try {
249 + await secureStorage.write(key: yatRefreshTokenKey(_wallet.walletInfo.name), value: refreshToken);
250 + accessToken = await fetchYatAccessToken(refreshToken);
251 + await secureStorage.write(key: yatAccessTokenKey(_wallet.walletInfo.name), value: accessToken);
252 + apiKey = await fetchYatApiKey(accessToken);
253 + await secureStorage.write(key: yatApiKey(_wallet.walletInfo.name), value: accessToken);
254 + } catch (e) {
255 + print(e.toString());
256 + }
257 + }
258 +
259 String defineQueryParameters() {
147 - String parameters = '';
148 - switch (_wallet.type) {
149 - case WalletType.monero:
150 - final wallet = _wallet as MoneroWallet;
151 - final subaddressList = MoneroSubaddressList();
152 - var isFirstAddress = true;
153 -
154 - wallet.walletAddresses.accountList.accounts.forEach((account) {
155 - subaddressList.update(accountIndex: account.id);
156 - subaddressList.subaddresses.forEach((subaddress) {
157 - if (!isFirstAddress) {
158 - parameters += '%7C';
159 - } else {
160 - isFirstAddress = !isFirstAddress;
161 - }
162 -
163 - parameters += subaddress.address.startsWith('4')
164 - ? YatLink.tags["XMR"].first + '%3D'
165 - : YatLink.tags["XMR"].last + '%3D';
166 -
167 - parameters += subaddress.address;
168 - });
169 - });
170 - break;
171 - case WalletType.bitcoin:
172 - final wallet = _wallet as ElectrumWallet;
173 - var isFirstAddress = true;
174 -
175 - wallet.walletAddresses.addresses.forEach((record) {
176 - if (!isFirstAddress) {
177 - parameters += '%7C';
178 - } else {
179 - isFirstAddress = !isFirstAddress;
180 - }
181 -
182 - parameters += YatLink.tags["BTC"].first + '%3D' + record.address;
183 - });
184 - break;
185 - case WalletType.litecoin:
186 - final wallet = _wallet as ElectrumWallet;
187 - var isFirstAddress = true;
188 -
189 - wallet.walletAddresses.addresses.forEach((record) {
190 - if (!isFirstAddress) {
191 - parameters += '%7C';
192 - } else {
193 - isFirstAddress = !isFirstAddress;
194 - }
195 -
196 - parameters += YatLink.tags["LTC"].first + '%3D' + record.address;
197 - });
198 - break;
199 - default:
200 - parameters = '';
260 + final result = <String, String>{};
261 + final tags = YatLink.tags[_wallet.currency.toString().toUpperCase()];
262 + String tag = tags.first;
263 +
264 + if (_wallet.type == WalletType.monero
265 + && _wallet.walletAddresses.address.startsWith('4')) {
266 + tag = tags.last;
267 }
202 - return parameters;
268 + result[tag] = '${_wallet.walletAddresses.address}|${_wallet.name}';
269 + final addressJson = json.encode([result]);
270 + final addressJsonBytes = utf8.encode(addressJson);
271 +
272 + return base64.encode(addressJsonBytes);
273 }
274 }
\ No newline at end of file
lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
+30 -2
@@ -14,6 +14,7 @@ import 'package:cake_wallet/core/transaction_history.dart';
14 import 'package:cake_wallet/entities/balance.dart';
15 import 'package:cake_wallet/entities/transaction_info.dart';
16 import 'package:cake_wallet/store/app_store.dart';
17 +import 'dart:async';
18
19 part 'wallet_address_list_view_model.g.dart';
20
@@ -66,7 +67,31 @@ abstract class WalletAddressListViewModelBase with Store {
67 }) {
68 _appStore = appStore;
69 _wallet = _appStore.wallet;
70 + emoji = '';
71 hasAccounts = _wallet?.type == WalletType.monero;
72 + reaction((_) => _wallet.walletAddresses.address, (String address) {
73 + if (address == _wallet.walletInfo.yatLastUsedAddress) {
74 + emoji = yatStore.emoji;
75 + } else {
76 + emoji = '';
77 + }
78 + });
79 +
80 + reaction((_) => yatStore.emoji, (String emojiId) => this.emoji = emojiId);
81 +
82 + _onLastUsedYatAddressSubscription =
83 + _wallet.walletInfo.yatLastUsedAddressStream.listen((String yatAddress) {
84 + if (yatAddress == _wallet.walletAddresses.address) {
85 + emoji = yatStore.emoji;
86 + } else {
87 + emoji = '';
88 + }
89 + });
90 +
91 + if (_wallet.walletAddresses.address == _wallet.walletInfo.yatLastUsedAddress) {
92 + emoji = yatStore.emoji;
93 + }
94 +
95 _onWalletChangeReaction = reaction((_) => _appStore.wallet, (WalletBase<
96 Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>
97 wallet) {
@@ -154,8 +179,8 @@ abstract class WalletAddressListViewModelBase with Store {
179 @computed
180 bool get hasAddressList => _wallet.type == WalletType.monero;
181
157 - @computed
158 - String get emoji => yatStore.emoji;
182 + @observable
183 + String emoji;
184
185 @observable
186 WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>
@@ -169,6 +194,9 @@ abstract class WalletAddressListViewModelBase with Store {
194
195 ReactionDisposer _onWalletChangeReaction;
196
197 + StreamSubscription<String> _onLastUsedYatAddressSubscription;
198 + StreamSubscription<String> _onEmojiIdChangeSubscription;
199 +
200 @action
201 void setAddress(WalletAddressListItem address) =>
202 _wallet.walletAddresses.address = address.address;