dev
dart 222 lines 6.81 KB
Raw
1 import 'dart:convert';
2 import 'dart:developer';
3
4 import 'package:cake_wallet/entities/solana_nft_asset_model.dart';
5 import 'package:cake_wallet/generated/i18n.dart';
6 import 'package:cake_wallet/reactions/wallet_connect.dart';
7 import 'package:cake_wallet/src/screens/wallet_connect/services/bottom_sheet_service.dart';
8 import 'package:cake_wallet/src/screens/wallet_connect/widgets/bottom_sheet/bottom_sheet_message_display_widget.dart';
9 import 'package:cake_wallet/evm/evm.dart';
10 import 'package:cake_wallet/utils/debounce.dart';
11 import 'package:cw_core/wallet_type.dart';
12 import 'package:cw_core/utils/proxy_wrapper.dart';
13 import 'package:mobx/mobx.dart';
14 import 'package:cake_wallet/.secrets.g.dart' as secrets;
15
16 import 'package:cake_wallet/entities/wallet_nft_response.dart';
17 import 'package:cake_wallet/store/app_store.dart';
18
19 part 'nft_view_model.g.dart';
20
21 class NFTViewModel = NFTViewModelBase with _$NFTViewModel;
22
23 abstract class NFTViewModelBase with Store {
24 NFTViewModelBase(this.appStore, this.bottomSheetService)
25 : isLoading = false,
26 isImportNFTLoading = false,
27 nftAssetByWalletModels = ObservableList(),
28 solanaNftAssetModels = ObservableList() {
29 if (isEVMCompatibleChain(appStore.wallet!.type)) {
30 reaction((_) {
31 final wallet = appStore.wallet;
32 if (wallet != null) return wallet.chainId;
33
34 return null;
35 }, (_) => _fetchDebounce.run(() => getNFTAssetByWallet()));
36 }
37 }
38
39 final AppStore appStore;
40 bool _nftErrorPopupShown = false;
41 final BottomSheetService bottomSheetService;
42 final Debounce _fetchDebounce = Debounce(const Duration(milliseconds: 500));
43
44 @observable
45 bool isLoading;
46
47 @observable
48 bool isImportNFTLoading;
49
50 ObservableList<NFTAssetModel> nftAssetByWalletModels;
51
52 ObservableList<SolanaNFTAssetModel> solanaNftAssetModels;
53
54 @action
55 Future<void> getNFTAssetByWallet() async {
56 final wallet = appStore.wallet!;
57
58 if (!isNFTACtivatedChain(wallet.type, chainId: wallet.chainId)) return;
59
60 final walletAddress = wallet.walletInfo.address;
61 log('Fetching wallet NFTs for $walletAddress');
62
63 final chainName = getChainNameBasedOnWalletType(wallet.type, chainId: wallet.chainId);
64 // the [chain] refers to the chain network that the nft is on
65 // the [format] refers to the number format type of the responses
66 // the [normalizedMetadata] field is a boolean that determines if
67 // the response would include a json string of the NFT Metadata that can be decoded
68 // and used within the wallet
69 // the [excludeSpam] field is a boolean that determines if spam nfts be excluded from the response.
70
71 Uri uri;
72 if (wallet.type == WalletType.solana) {
73 uri = Uri.https(
74 'solana-gateway.moralis.io',
75 '/account/$chainName/$walletAddress/nft',
76 );
77 } else {
78 uri = Uri.https(
79 'deep-index.moralis.io',
80 '/api/v2.2/$walletAddress/nft',
81 {
82 "chain": chainName,
83 "format": "decimal",
84 "media_items": "false",
85 "exclude_spam": "true",
86 "normalizeMetadata": "true",
87 },
88 );
89 }
90
91 try {
92 if (isLoading) return;
93
94 isLoading = true;
95
96 final response = await ProxyWrapper().get(
97 clearnetUri: uri,
98 headers: {
99 "Accept": "application/json",
100 "X-API-Key": secrets.moralisApiKey,
101 },
102 );
103
104 final decodedResponse = jsonDecode(response.body);
105
106 _nftErrorPopupShown = false;
107
108 if (wallet.type == WalletType.solana) {
109 final results = await Future.wait(
110 (decodedResponse as List<dynamic>).map(
111 (x) {
112 final data = x as Map<String, dynamic>;
113 final mint = data['mint'] as String? ?? '';
114 return getSolanaNFTDetails(mint, chainName);
115 },
116 ).toList(),
117 );
118
119 solanaNftAssetModels.clear();
120
121 solanaNftAssetModels.addAll(results);
122 } else {
123 final result =
124 WalletNFTsResponseModel.fromJson(decodedResponse as Map<String, dynamic>).result ?? [];
125
126 nftAssetByWalletModels.clear();
127
128 nftAssetByWalletModels.addAll(result);
129 }
130 } catch (e) {
131 log(e.toString());
132 if (!_nftErrorPopupShown) {
133 _nftErrorPopupShown = true;
134 bottomSheetService.queueBottomSheet(
135 isModalDismissible: true,
136 widget: BottomSheetMessageDisplayWidget(
137 message: S.current.moralis_nft_error,
138 ),
139 );
140 }
141 } finally {
142 isLoading = false;
143 }
144 }
145
146 Future<SolanaNFTAssetModel> getSolanaNFTDetails(String address, String chainName) async {
147 final uri = Uri.https(
148 'solana-gateway.moralis.io',
149 '/nft/$chainName/$address/metadata',
150 );
151
152 final response = await ProxyWrapper().get(
153 clearnetUri: uri,
154 headers: {
155 "Accept": "application/json",
156 "X-API-Key": secrets.moralisApiKey,
157 },
158 );
159
160 final decodedResponse = jsonDecode(response.body) as Map<String, dynamic>;
161
162 return SolanaNFTAssetModel.fromJson(decodedResponse);
163 }
164
165 @action
166 Future<void> importNFT(String tokenAddress, String? tokenId) async {
167 final walletType = appStore.wallet!.type;
168 int? chainId;
169 if (isEVMCompatibleChain(walletType)) {
170 chainId = evm!.getSelectedChainId(appStore.wallet!);
171 }
172 final chainName = getChainNameBasedOnWalletType(walletType, chainId: chainId);
173 // the [chain] refers to the chain network that the nft is on
174 // the [format] refers to the number format type of the responses
175 // the [normalizedMetadata] field is a boolean that determines if
176 // the response would include a json string of the NFT Metadata that can be decoded
177 // and used within the wallet
178
179 try {
180 isImportNFTLoading = true;
181
182 if (appStore.wallet!.type == WalletType.solana) {
183 final result = await getSolanaNFTDetails(tokenAddress, chainName);
184
185 solanaNftAssetModels.add(result);
186 } else {
187 final uri = Uri.https(
188 'deep-index.moralis.io',
189 '/api/v2.2/nft/$tokenAddress/$tokenId',
190 {
191 "chain": chainName,
192 "format": "decimal",
193 "media_items": "false",
194 "normalizeMetadata": "true",
195 },
196 );
197 final response = await ProxyWrapper().get(
198 clearnetUri: uri,
199 headers: {
200 "Accept": "application/json",
201 "X-API-Key": secrets.moralisApiKey,
202 },
203 );
204
205 final decodedResponse = jsonDecode(response.body) as Map<String, dynamic>;
206
207 final nftAsset = NFTAssetModel.fromJson(decodedResponse);
208
209 nftAssetByWalletModels.add(nftAsset);
210 }
211 } catch (e) {
212 bottomSheetService.queueBottomSheet(
213 isModalDismissible: true,
214 widget: BottomSheetMessageDisplayWidget(
215 message: e.toString(),
216 ),
217 );
218 } finally {
219 isImportNFTLoading = false;
220 }
221 }
222 }