dev
dart 205 lines 6.68 KB
Raw
1 import 'dart:convert';
2 import 'package:cake_wallet/anonpay/anonpay_donation_link_info.dart';
3 import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
4 import 'package:cake_wallet/anonpay/anonpay_request.dart';
5 import 'package:cake_wallet/anonpay/anonpay_status_response.dart';
6 import 'package:cake_wallet/core/fiat_conversion_service.dart';
7 import 'package:cake_wallet/entities/fiat_currency.dart';
8 import 'package:cake_wallet/exchange/limits.dart';
9 import 'package:cake_wallet/wallet_type_utils.dart';
10 import 'package:cw_core/utils/proxy_wrapper.dart';
11 import 'package:cw_core/wallet_base.dart';
12 import 'package:cw_core/crypto_currency.dart';
13 import 'package:cake_wallet/.secrets.g.dart' as secrets;
14
15 class AnonPayApi {
16 const AnonPayApi({
17 this.useTorOnly = false,
18 required this.wallet,
19 });
20 final bool useTorOnly;
21 final WalletBase wallet;
22
23 static const anonpayRef = secrets.anonPayReferralCode;
24 static const clearNetAuthority = 'trocador.app';
25 // static const onionApiAuthority = 'tqzngtf2hybjbexznel6dhgsvbynjzezoybvtv6iofomx7gchqfssgqd.onion';
26 static const onionApiAuthority = clearNetAuthority;
27 static const markup = secrets.trocadorExchangeMarkup;
28 static const anonPayPath = '/anonpay';
29 static const anonPayStatus = '/anonpay/status';
30 static const coinPath = 'api/coin';
31 static final apiKey = isMoneroOnly ? secrets.trocadorMoneroApiKey : secrets.trocadorApiKey;
32
33 Future<AnonpayStatusResponse> paymentStatus(String id) async {
34 final response = await ProxyWrapper().get(
35 clearnetUri: Uri.https(clearNetAuthority, "$anonPayStatus/$id"),
36 onionUri: Uri.https(onionApiAuthority, "$anonPayStatus/$id"),
37 );
38
39 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
40 final status = responseJSON['Status'] as String;
41 final fiatAmount = responseJSON['Fiat_Amount'] as double?;
42 final fiatEquiv = responseJSON['Fiat_Equiv'] as String?;
43 final amountTo = responseJSON['AmountTo'] as double?;
44 final coinTo = responseJSON['CoinTo'] as String;
45 final address = responseJSON['Address'] as String;
46
47 return AnonpayStatusResponse(
48 status: status,
49 fiatAmount: fiatAmount,
50 amountTo: amountTo,
51 coinTo: coinTo,
52 address: address,
53 fiatEquiv: fiatEquiv,
54 );
55 }
56
57 Future<AnonpayInvoiceInfo> createInvoice(AnonPayRequest request) async {
58 final description = Uri.encodeComponent(request.description);
59 final body = <String, dynamic>{
60 'ticker_to': request.cryptoCurrency.title.toLowerCase(),
61 'network_to': _networkFor(request.cryptoCurrency),
62 'address': request.address,
63 'name': request.name,
64 'description': description,
65 'email': request.email,
66 'ref': anonpayRef,
67 'markup': markup,
68 'direct': 'False',
69 };
70
71 if (request.amount != null) {
72 body['amount'] = request.amount;
73 }
74 if (request.fiatEquivalent != null) {
75 body['fiat_equiv'] = request.fiatEquivalent;
76 }
77 final response = await ProxyWrapper().get(
78 clearnetUri: Uri.https(clearNetAuthority, anonPayPath, body),
79 onionUri: Uri.https(onionApiAuthority, anonPayPath, body),
80 );
81
82 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
83 final id = responseJSON['ID'] as String;
84 final url = responseJSON['url'] as String;
85 final urlOnion = responseJSON['url_onion'] as String;
86 final statusUrl = responseJSON['status_url'] as String;
87 final statusUrlOnion = responseJSON['status_url_onion'] as String;
88
89 final statusInfo = await paymentStatus(id);
90
91 return AnonpayInvoiceInfo(
92 invoiceId: id,
93 clearnetUrl: url,
94 onionUrl: urlOnion,
95 status: statusInfo.status,
96 fiatAmount: statusInfo.fiatAmount,
97 fiatEquiv: statusInfo.fiatEquiv,
98 amountTo: statusInfo.amountTo,
99 coinTo: statusInfo.coinTo,
100 address: statusInfo.address,
101 clearnetStatusUrl: statusUrl,
102 onionStatusUrl: statusUrlOnion,
103 walletId: wallet.id,
104 createdAt: DateTime.now(),
105 provider: 'Trocador AnonPay invoice',
106 );
107 }
108
109 Future<AnonpayDonationLinkInfo> generateDonationLink(AnonPayRequest request) async {
110 final body = <String, dynamic>{
111 'ticker_to': request.cryptoCurrency.title.toLowerCase(),
112 'network_to': _networkFor(request.cryptoCurrency),
113 'address': request.address,
114 'ref': anonpayRef,
115 'direct': 'True',
116 };
117 if (request.name.isNotEmpty) {
118 body['name'] = request.name;
119 }
120 if (request.description.isNotEmpty) {
121 body['description'] = request.description;
122 }
123 if (request.email.isNotEmpty) {
124 body['email'] = request.email;
125 }
126
127 final clearnetUrl = Uri.https(clearNetAuthority, anonPayPath, body);
128 final onionUrl = Uri.https(onionApiAuthority, anonPayPath, body);
129 return AnonpayDonationLinkInfo(
130 clearnetUrl: clearnetUrl.toString(),
131 onionUrl: onionUrl.toString(),
132 address: request.address,
133 );
134 }
135
136 Future<Limits> fetchLimits({
137 FiatCurrency? fiatCurrency,
138 required CryptoCurrency cryptoCurrency,
139 }) async {
140 double fiatRate = 0.0;
141 if (fiatCurrency != null) {
142 fiatRate = await FiatConversionService.fetchPrice(
143 crypto: cryptoCurrency,
144 fiat: fiatCurrency,
145 torOnly: useTorOnly,
146 );
147 }
148
149 final params = <String, String>{
150 'api_key': apiKey,
151 'ticker': cryptoCurrency.title.toLowerCase(),
152 'name': cryptoCurrency.name,
153 };
154
155 final response = await ProxyWrapper().get(
156 clearnetUri: Uri.https(clearNetAuthority, coinPath, params),
157 onionUri: Uri.https(onionApiAuthority, coinPath, params),
158 );
159
160 final responseJSON = json.decode(response.body) as List<dynamic>;
161 if (response.statusCode != 200) {
162 throw Exception('Unexpected http status: ${response.statusCode}');
163 }
164
165 if (responseJSON.isEmpty) {
166 throw Exception('No data');
167 }
168
169 final coinJson = responseJSON.first as Map<String, dynamic>;
170 final minimum = coinJson['minimum'] as double;
171 final maximum = coinJson['maximum'] as double;
172
173 if (fiatCurrency != null) {
174 return Limits(
175 min: double.tryParse((minimum * fiatRate).toStringAsFixed(2)),
176 max: double.tryParse((maximum * fiatRate).toStringAsFixed(2)),
177 );
178 }
179
180 return Limits(
181 min: minimum,
182 max: maximum,
183 );
184 }
185
186 String _networkFor(CryptoCurrency currency) {
187 switch (currency) {
188 case CryptoCurrency.usdt:
189 return CryptoCurrency.btc.title.toLowerCase();
190 case CryptoCurrency.eth:
191 return 'ERC20';
192 default:
193 return currency.tag != null ? _normalizeTag(currency.tag!) : 'Mainnet';
194 }
195 }
196
197 String _normalizeTag(String tag) {
198 switch (tag) {
199 case 'ETH':
200 return 'ERC20';
201 default:
202 return tag.toLowerCase();
203 }
204 }
205 }