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;