CWA-169 | created MorphToken exchange provider and MorphToken request (also this provider added to exchange provider description, exchange page, exchange store, router)
Oleksandr Sobol committed
Jan 30, 2020 at 20:23 UTC
c1a7ee01649da87218c1263035589803966af6d3
6 files changed
+206
-1
lib/router.dart
+3
-1
@@ -16,6 +16,7 @@ import 'package:cake_wallet/src/domain/services/wallet_service.dart';
16
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
17
import 'package:cake_wallet/src/domain/exchange/changenow/changenow_exchange_provider.dart';
18
import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart';
19
+import 'package:cake_wallet/src/domain/exchange/morphtoken/morphtoken_exchange_provider.dart';
20
import 'package:cake_wallet/src/domain/common/node.dart';
21
import 'package:cake_wallet/src/domain/monero/transaction_description.dart';
22
import 'package:cake_wallet/src/domain/exchange/trade.dart';
@@ -453,7 +454,8 @@ class Router {
454
trades: trades,
455
providerList: [
456
xmrtoprovider,
456
- ChangeNowExchangeProvider()
457
+ ChangeNowExchangeProvider(),
458
+ MorphTokenExchangeProvider()
459
],
460
walletStore: walletStore);
461
}),
lib/src/domain/exchange/exchange_provider_description.dart
+4
@@ -8,6 +8,8 @@ class ExchangeProviderDescription extends EnumerableItem<int>
8
static const xmrto = ExchangeProviderDescription(title: 'XMR.TO', raw: 0);
9
static const changeNow =
10
ExchangeProviderDescription(title: 'ChangeNOW', raw: 1);
11
+ static const morphToken =
12
+ ExchangeProviderDescription(title: 'MorphToken', raw: 2);
13
14
static ExchangeProviderDescription deserialize({int raw}) {
15
switch (raw) {
@@ -15,6 +17,8 @@ class ExchangeProviderDescription extends EnumerableItem<int>
17
return xmrto;
18
case 1:
19
return changeNow;
20
+ case 2:
21
+ return morphToken;
22
default:
23
return null;
24
}
lib/src/domain/exchange/morphtoken/morphtoken_exchange_provider.dart
new
+167
@@ -0,0 +1,167 @@
1
+import 'dart:convert';
2
+import 'package:cake_wallet/src/domain/exchange/trade_not_found_exeption.dart';
3
+import 'package:flutter/foundation.dart';
4
+import 'package:http/http.dart';
5
+import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
6
+import 'package:cake_wallet/src/domain/exchange/exchange_pair.dart';
7
+import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart';
8
+import 'package:cake_wallet/src/domain/exchange/limits.dart';
9
+import 'package:cake_wallet/src/domain/exchange/trade.dart';
10
+import 'package:cake_wallet/src/domain/exchange/trade_request.dart';
11
+import 'package:cake_wallet/src/domain/exchange/trade_state.dart';
12
+import 'package:cake_wallet/src/domain/exchange/morphtoken/morphtoken_request.dart';
13
+import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
14
+import 'package:cake_wallet/src/domain/exchange/trade_not_created_exeption.dart';
15
+
16
+class MorphTokenExchangeProvider extends ExchangeProvider {
17
+ MorphTokenExchangeProvider()
18
+ : super(
19
+ pairList: CryptoCurrency.all
20
+ .map((i) {
21
+ return CryptoCurrency.all.map((k) {
22
+ if (i == CryptoCurrency.btc && k == CryptoCurrency.xmr) {
23
+ return ExchangePair(from: i, to: k, reverse: false);
24
+ }
25
+
26
+ if (i == CryptoCurrency.xmr && k == CryptoCurrency.btc) {
27
+ return null;
28
+ }
29
+
30
+ return ExchangePair(from: i, to: k, reverse: true);
31
+ }).where((c) => c != null);
32
+ })
33
+ .expand((i) => i)
34
+ .toList());
35
+
36
+ static const apiUri = 'https://api.morphtoken.com';
37
+ static const _morphURISuffix = '/morph';
38
+ static const _limitsURISuffix = '/limits';
39
+ static const _ratesURISuffix = '/rates';
40
+ static const weight = 10000;
41
+
42
+ @override
43
+ String get title => 'MorphToken';
44
+
45
+ @override
46
+ ExchangeProviderDescription get description =>
47
+ ExchangeProviderDescription.morphToken;
48
+
49
+ @override
50
+ Future<Limits> fetchLimits({CryptoCurrency from, CryptoCurrency to}) async {
51
+ final url = apiUri + _limitsURISuffix;
52
+ final headers = {'Content-type': 'application/json'};
53
+ final body =
54
+ json.encode({
55
+ "input": {
56
+ "asset": from.toString()
57
+ },
58
+ "output": [{
59
+ "asset": to.toString(),
60
+ "weight": weight
61
+ }]});
62
+ final response =
63
+ await post(url.toString(), headers: headers, body: json.encode(body));
64
+ final responseJSON = json.decode(response.body) as Map<String, dynamic>;
65
+
66
+ final min = responseJSON['input']['limits']['min'] as double;
67
+ final max = responseJSON['input']['limits']['max'] as double;
68
+
69
+ return Limits(min: min, max: max);
70
+ }
71
+
72
+ @override
73
+ Future<Trade> createTrade({TradeRequest request}) async {
74
+ const url = apiUri + _morphURISuffix;
75
+ final _request = request as MorphTokenRequest;
76
+ final body = {
77
+ "input": {
78
+ "asset": _request.from.toString(),
79
+ "refund": _request.refundAddress
80
+ },
81
+ "output": [{
82
+ "asset": _request.to.toString(),
83
+ "weight": weight,
84
+ "address": _request.address
85
+ }]
86
+ };
87
+
88
+ final response = await post(url,
89
+ headers: {'Content-Type': 'application/json'}, body: json.encode(body));
90
+
91
+ if (response.statusCode != 200) {
92
+ if (response.statusCode == 400) {
93
+ final responseJSON = json.decode(response.body) as Map<String, dynamic>;
94
+ final error = responseJSON['description'] as String;
95
+
96
+ throw TradeNotCreatedException(description, description: error);
97
+ }
98
+
99
+ throw TradeNotCreatedException(description);
100
+ }
101
+
102
+ final responseJSON = json.decode(response.body) as Map<String, dynamic>;
103
+ final id = responseJSON['id'] as String;
104
+
105
+ return Trade(
106
+ id: id,
107
+ provider: description,
108
+ from: _request.from,
109
+ to: _request.to,
110
+ state: TradeState.created,
111
+ amount: _request.amount,
112
+ createdAt: DateTime.now());
113
+ }
114
+
115
+ @override
116
+ Future<Trade> findTradeById({@required String id}) async {
117
+ final url = apiUri + _morphURISuffix + '/' + id;
118
+ final response = await get(url);
119
+
120
+ if (response.statusCode != 200) {
121
+ if (response.statusCode == 400) {
122
+ final responseJSON = json.decode(response.body) as Map<String, dynamic>;
123
+ final error = responseJSON['description'] as String;
124
+
125
+ throw TradeNotFoundException(id,
126
+ provider: description, description: error);
127
+ }
128
+
129
+ throw TradeNotFoundException(id, provider: description);
130
+ }
131
+
132
+ final responseJSON = json.decode(response.body) as Map<String, dynamic>;
133
+ final fromCurrency = responseJSON['input']['asset'] as String;
134
+ final from = CryptoCurrency.fromString(fromCurrency);
135
+ final toCurrency = responseJSON['output']['asset'] as String;
136
+ final to = CryptoCurrency.fromString(toCurrency);
137
+ final inputAddress = responseJSON['input']['refund_address'] as String;
138
+ final outputWeight = responseJSON['output']['weight'].toString();
139
+ final status = responseJSON['state'] as String;
140
+ final state = TradeState.deserialize(raw: status);
141
+ final extraId = responseJSON['id'] as String;
142
+ final outputTransaction = responseJSON['deposit_address'] as String;
143
+
144
+ return Trade(
145
+ id: id,
146
+ from: from,
147
+ to: to,
148
+ provider: description,
149
+ inputAddress: inputAddress,
150
+ amount: outputWeight,
151
+ state: state,
152
+ extraId: extraId,
153
+ outputTransaction: outputTransaction);
154
+ }
155
+
156
+ @override
157
+ Future<double> calculateAmount(
158
+ {CryptoCurrency from, CryptoCurrency to, double amount}) async {
159
+ final url = apiUri + _ratesURISuffix;
160
+ final response = await get(url);
161
+ final responseJSON = json.decode(response.body) as Map<String, dynamic>;
162
+ final rate = responseJSON['data'][from.toString()][to.toString()] as String;
163
+ final estimatedAmount = double.parse(rate) * amount;
164
+
165
+ return estimatedAmount;
166
+ }
167
+}
lib/src/domain/exchange/morphtoken/morphtoken_request.dart
new
+18
@@ -0,0 +1,18 @@
1
+import 'package:flutter/foundation.dart';
2
+import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
3
+import 'package:cake_wallet/src/domain/exchange/trade_request.dart';
4
+
5
+class MorphTokenRequest extends TradeRequest {
6
+ MorphTokenRequest(
7
+ {@required this.from,
8
+ @required this.to,
9
+ @required this.address,
10
+ @required this.amount,
11
+ @required this.refundAddress});
12
+
13
+ CryptoCurrency from;
14
+ CryptoCurrency to;
15
+ String address;
16
+ String amount;
17
+ String refundAddress;
18
+}
lib/src/screens/exchange/exchange_page.dart
+3
@@ -278,6 +278,9 @@ class ExchangeFormState extends State<ExchangeForm> {
278
case ExchangeProviderDescription.changeNow:
279
imageSrc = 'assets/images/change_now.png';
280
break;
281
+ case ExchangeProviderDescription.morphToken:
282
+ imageSrc = 'assets/images/morph_icon.png';
283
+ break;
284
}
285
286
return Padding(
lib/src/stores/exchange/exchange_store.dart
+11
@@ -8,6 +8,8 @@ import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart';
8
import 'package:cake_wallet/src/domain/exchange/trade_request.dart';
9
import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart';
10
import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_trade_request.dart';
11
+import 'package:cake_wallet/src/domain/exchange/morphtoken/morphtoken_exchange_provider.dart';
12
+import 'package:cake_wallet/src/domain/exchange/morphtoken/morphtoken_request.dart';
13
import 'package:cake_wallet/src/domain/exchange/trade.dart';
14
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
15
import 'package:cake_wallet/src/stores/exchange/exchange_trade_state.dart';
@@ -161,6 +163,15 @@ abstract class ExchangeStoreBase with Store {
163
address: receiveAddress);
164
}
165
166
+ if (provider is MorphTokenExchangeProvider) {
167
+ request = MorphTokenRequest(
168
+ from: depositCurrency,
169
+ to: receiveCurrency,
170
+ amount: depositAmount,
171
+ refundAddress: depositAddress,
172
+ address: receiveAddress);
173
+ }
174
+
175
try {
176
tradeState = TradeIsCreating();
177
final trade = await provider.createTrade(request: request);