mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-23 03:59:23 +00:00
fetching thorChain traid info
This commit is contained in:
parent
a941014fc2
commit
571c9fd82a
8 changed files with 67 additions and 47 deletions
|
@ -1,7 +1,6 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:bitbox/bitbox.dart' as bitbox;
|
import 'package:bitbox/bitbox.dart' as bitbox;
|
||||||
import 'package:bitbox/src/utils/opcodes.dart' as bitboxOPCodes;
|
|
||||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||||
|
@ -258,7 +257,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
||||||
txb.addOutput(changeAddress, changeValue);
|
txb.addOutput(changeAddress, changeValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opReturnMemo != null) txb.addOutput(createOpReturnScript(opReturnMemo), 0);
|
if (opReturnMemo != null) txb.addOutputData(opReturnMemo);
|
||||||
|
|
||||||
for (var i = 0; i < inputs.length; i++) {
|
for (var i = 0; i < inputs.length; i++) {
|
||||||
final input = inputs[i];
|
final input = inputs[i];
|
||||||
|
@ -333,11 +332,4 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
||||||
final HD = index == null ? hd : hd.derive(index);
|
final HD = index == null ? hd : hd.derive(index);
|
||||||
return base64Encode(HD.signMessage(message));
|
return base64Encode(HD.signMessage(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint8List createOpReturnScript(String data) {
|
|
||||||
List<int> script = [];
|
|
||||||
script.add(bitboxOPCodes.Opcodes.OP_RETURN);
|
|
||||||
script.addAll(utf8.encode(data));
|
|
||||||
return Uint8List.fromList(script);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,9 @@ class ThorChainExchangeProvider extends ExchangeProvider {
|
||||||
|
|
||||||
static const _baseURL = 'https://thornode.ninerealms.com';
|
static const _baseURL = 'https://thornode.ninerealms.com';
|
||||||
static const _quotePath = '/thorchain/quote/swap';
|
static const _quotePath = '/thorchain/quote/swap';
|
||||||
|
static const _txInfoPath = '/thorchain/tx/';
|
||||||
static const _affiliateName = 'cakewallet';
|
static const _affiliateName = 'cakewallet';
|
||||||
static const _affiliateBps = '10';
|
static const _affiliateBps = '0';
|
||||||
|
|
||||||
final Box<Trade> tradesStore;
|
final Box<Trade> tradesStore;
|
||||||
|
|
||||||
|
@ -68,6 +69,8 @@ class ThorChainExchangeProvider extends ExchangeProvider {
|
||||||
'from_asset': _normalizeCurrency(from),
|
'from_asset': _normalizeCurrency(from),
|
||||||
'to_asset': _normalizeCurrency(to),
|
'to_asset': _normalizeCurrency(to),
|
||||||
'amount': _doubleToThorChainString(amount),
|
'amount': _doubleToThorChainString(amount),
|
||||||
|
'affiliate': _affiliateName,
|
||||||
|
'affiliate_bps': _affiliateBps
|
||||||
};
|
};
|
||||||
|
|
||||||
final responseJSON = await _getSwapQuote(params);
|
final responseJSON = await _getSwapQuote(params);
|
||||||
|
@ -95,7 +98,7 @@ class ThorChainExchangeProvider extends ExchangeProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
final responseJSON = await _getSwapQuote(params);
|
final responseJSON = await _getSwapQuote(params);
|
||||||
final minAmountIn = responseJSON['recommended_min_amount_in'] as String?;
|
final minAmountIn = responseJSON['recommended_min_amount_in'] as String? ?? '0.0';
|
||||||
|
|
||||||
return Limits(min: _thorChainAmountToDouble(minAmountIn));
|
return Limits(min: _thorChainAmountToDouble(minAmountIn));
|
||||||
}
|
}
|
||||||
|
@ -121,27 +124,58 @@ class ThorChainExchangeProvider extends ExchangeProvider {
|
||||||
|
|
||||||
final inputAddress = responseJSON['inbound_address'] as String?;
|
final inputAddress = responseJSON['inbound_address'] as String?;
|
||||||
final memo = responseJSON['memo'] as String?;
|
final memo = responseJSON['memo'] as String?;
|
||||||
final tradeId = await getNextTradeCounter();
|
|
||||||
|
|
||||||
return Trade(
|
return Trade(
|
||||||
id: tradeId.toString(),
|
id: '',
|
||||||
from: request.fromCurrency,
|
from: request.fromCurrency,
|
||||||
to: request.toCurrency,
|
to: request.toCurrency,
|
||||||
provider: description,
|
provider: description,
|
||||||
inputAddress: inputAddress,
|
inputAddress: inputAddress,
|
||||||
createdAt: DateTime.now(),
|
createdAt: DateTime.now(),
|
||||||
amount: request.fromAmount,
|
amount: request.fromAmount,
|
||||||
state: TradeState.created,
|
state: TradeState.pending,
|
||||||
payoutAddress: request.toAddress,
|
payoutAddress: request.toAddress,
|
||||||
memo: memo);
|
memo: memo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
Future<Trade> findTradeById({required String id}) async {
|
Future<Trade> findTradeById({required String id}) async {
|
||||||
final foundTrade = tradesStore.values.firstWhereOrNull((element) => element.id == id);
|
if (id.isEmpty) throw Exception('Trade id is empty');
|
||||||
if (foundTrade == null) {
|
final uri = Uri.parse('$_baseURL$_txInfoPath$id');
|
||||||
throw Exception('Trade with id $id not found');
|
final response = await http.get(uri);
|
||||||
|
|
||||||
|
if (response.statusCode == 404) {
|
||||||
|
throw Exception('Trade not found for id: $id');
|
||||||
|
} else if (response.statusCode != 200) {
|
||||||
|
throw Exception('Unexpected HTTP status: ${response.statusCode}');
|
||||||
}
|
}
|
||||||
return foundTrade;
|
|
||||||
|
final responseJSON = json.decode(response.body);
|
||||||
|
final observedTx = responseJSON['observed_tx'];
|
||||||
|
if (observedTx == null) {
|
||||||
|
throw Exception('No observed transaction found for id: $id');
|
||||||
|
}
|
||||||
|
|
||||||
|
final tx = observedTx['tx'];
|
||||||
|
final String fromAddress = tx['from_address'] as String? ?? '';
|
||||||
|
final String toAddress = tx['to_address'] as String? ?? '';
|
||||||
|
final List<dynamic> coins = tx['coins'] as List<dynamic>;
|
||||||
|
final String? memo = tx['memo'] as String?;
|
||||||
|
final String toAsset = memo != null ? (memo.split(':')[1]).split('.')[0] : '';
|
||||||
|
final status = observedTx['status'] as String?;
|
||||||
|
final formattedStatus = status ?? 'pending';
|
||||||
|
|
||||||
|
return Trade(
|
||||||
|
id: id,
|
||||||
|
from: CryptoCurrency.fromString(tx['chain'] as String? ?? ''),
|
||||||
|
to: CryptoCurrency.fromString(toAsset),
|
||||||
|
provider: description,
|
||||||
|
inputAddress: fromAddress,
|
||||||
|
payoutAddress: toAddress,
|
||||||
|
amount: coins.first['amount'] as String? ?? '0.0',
|
||||||
|
state: TradeState.deserialize(raw: formattedStatus),
|
||||||
|
memo: memo,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> _getSwapQuote(Map<String, String> params) async {
|
Future<Map<String, dynamic>> _getSwapQuote(Map<String, String> params) async {
|
||||||
|
@ -160,31 +194,9 @@ class ThorChainExchangeProvider extends ExchangeProvider {
|
||||||
return json.decode(response.body) as Map<String, dynamic>;
|
return json.decode(response.body) as Map<String, dynamic>;
|
||||||
}
|
}
|
||||||
|
|
||||||
String _normalizeCurrency(CryptoCurrency currency) {
|
String _normalizeCurrency(CryptoCurrency currency) => '${currency.title}.${currency.title}';
|
||||||
switch (currency) {
|
|
||||||
case CryptoCurrency.btc:
|
|
||||||
return 'BTC.BTC';
|
|
||||||
case CryptoCurrency.eth:
|
|
||||||
return 'ETH.ETH';
|
|
||||||
case CryptoCurrency.ltc:
|
|
||||||
return 'LTC.LTC';
|
|
||||||
case CryptoCurrency.bch:
|
|
||||||
return 'BCH.BCH';
|
|
||||||
default:
|
|
||||||
return currency.title.toLowerCase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _doubleToThorChainString(double amount) => (amount * 1e8).toInt().toString();
|
String _doubleToThorChainString(double amount) => (amount * 1e8).toInt().toString();
|
||||||
|
|
||||||
double _thorChainAmountToDouble(String? amount) =>
|
double _thorChainAmountToDouble(String amount) => double.parse(amount) / 1e8;
|
||||||
amount == null ? 0.0 : double.parse(amount) / 1e8;
|
|
||||||
|
|
||||||
Future<int> getNextTradeCounter() async {
|
|
||||||
final prefs = await SharedPreferences.getInstance();
|
|
||||||
int currentCounter = prefs.getInt(PreferencesKey.thorChainTradeCounter) ?? 0;
|
|
||||||
currentCounter++;
|
|
||||||
await prefs.setInt(PreferencesKey.thorChainTradeCounter, currentCounter);
|
|
||||||
return currentCounter;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ class Trade extends HiveObject {
|
||||||
this.providerName,
|
this.providerName,
|
||||||
this.fromWalletAddress,
|
this.fromWalletAddress,
|
||||||
this.memo,
|
this.memo,
|
||||||
|
this.txId
|
||||||
}) {
|
}) {
|
||||||
if (provider != null) providerRaw = provider.raw;
|
if (provider != null) providerRaw = provider.raw;
|
||||||
|
|
||||||
|
@ -109,6 +110,9 @@ class Trade extends HiveObject {
|
||||||
@HiveField(18)
|
@HiveField(18)
|
||||||
String? memo;
|
String? memo;
|
||||||
|
|
||||||
|
@HiveField(19)
|
||||||
|
String? txId;
|
||||||
|
|
||||||
static Trade fromMap(Map<String, Object?> map) {
|
static Trade fromMap(Map<String, Object?> map) {
|
||||||
return Trade(
|
return Trade(
|
||||||
id: map['id'] as String,
|
id: map['id'] as String,
|
||||||
|
@ -120,7 +124,8 @@ class Trade extends HiveObject {
|
||||||
amount: map['amount'] as String,
|
amount: map['amount'] as String,
|
||||||
walletId: map['wallet_id'] as String,
|
walletId: map['wallet_id'] as String,
|
||||||
fromWalletAddress: map['from_wallet_address'] as String?,
|
fromWalletAddress: map['from_wallet_address'] as String?,
|
||||||
memo: map['memo'] as String?
|
memo: map['memo'] as String?,
|
||||||
|
txId: map['tx_id'] as String?
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +139,8 @@ class Trade extends HiveObject {
|
||||||
'amount': amount,
|
'amount': amount,
|
||||||
'wallet_id': walletId,
|
'wallet_id': walletId,
|
||||||
'from_wallet_address': fromWalletAddress,
|
'from_wallet_address': fromWalletAddress,
|
||||||
'memo': memo
|
'memo': memo,
|
||||||
|
'tx_id': txId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,7 @@ class TradeState extends EnumerableItem<String> with Serializable<String> {
|
||||||
case 'sending':
|
case 'sending':
|
||||||
return sending;
|
return sending;
|
||||||
case 'success':
|
case 'success':
|
||||||
|
case 'done':
|
||||||
return success;
|
return success;
|
||||||
default:
|
default:
|
||||||
throw Exception('Unexpected token: $raw in TradeState deserialize');
|
throw Exception('Unexpected token: $raw in TradeState deserialize');
|
||||||
|
|
|
@ -20,6 +20,7 @@ class SyncIndicatorIcon extends StatelessWidget {
|
||||||
static const String created = 'created';
|
static const String created = 'created';
|
||||||
static const String fetching = 'fetching';
|
static const String fetching = 'fetching';
|
||||||
static const String finished = 'finished';
|
static const String finished = 'finished';
|
||||||
|
static const String success = 'success';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -45,6 +46,7 @@ class SyncIndicatorIcon extends StatelessWidget {
|
||||||
indicatorColor = Colors.red;
|
indicatorColor = Colors.red;
|
||||||
break;
|
break;
|
||||||
case finished:
|
case finished:
|
||||||
|
case success:
|
||||||
indicatorColor = PaletteDark.brightGreen;
|
indicatorColor = PaletteDark.brightGreen;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -106,7 +106,11 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||||
output.setCryptoAmount(trade.amount);
|
output.setCryptoAmount(trade.amount);
|
||||||
if (_provider is ThorChainExchangeProvider) output.memo = trade.memo;
|
if (_provider is ThorChainExchangeProvider) output.memo = trade.memo;
|
||||||
sendViewModel.selectedCryptoCurrency = trade.from;
|
sendViewModel.selectedCryptoCurrency = trade.from;
|
||||||
await sendViewModel.createTransaction();
|
final pendingTransaction = await sendViewModel.createTransaction();
|
||||||
|
if (_provider is ThorChainExchangeProvider) {
|
||||||
|
trade.id = pendingTransaction?.id ?? '';
|
||||||
|
trades.add(trade);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
|
|
@ -9,6 +9,7 @@ import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||||
import 'package:cake_wallet/entities/wallet_contact.dart';
|
import 'package:cake_wallet/entities/wallet_contact.dart';
|
||||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||||
|
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||||
import 'package:cake_wallet/exchange/exchange_template.dart';
|
import 'package:cake_wallet/exchange/exchange_template.dart';
|
||||||
import 'package:cake_wallet/exchange/exchange_trade_state.dart';
|
import 'package:cake_wallet/exchange/exchange_trade_state.dart';
|
||||||
import 'package:cake_wallet/exchange/limits.dart';
|
import 'package:cake_wallet/exchange/limits.dart';
|
||||||
|
@ -495,7 +496,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||||
trade.walletId = wallet.id;
|
trade.walletId = wallet.id;
|
||||||
trade.fromWalletAddress = wallet.walletAddresses.address;
|
trade.fromWalletAddress = wallet.walletAddresses.address;
|
||||||
tradesStore.setTrade(trade);
|
tradesStore.setTrade(trade);
|
||||||
await trades.add(trade);
|
if(trade.provider != ExchangeProviderDescription.thorChain) await trades.add(trade);
|
||||||
tradeState = TradeIsCreatedSuccessfully(trade: trade);
|
tradeState = TradeIsCreatedSuccessfully(trade: trade);
|
||||||
|
|
||||||
/// return after the first successful trade
|
/// return after the first successful trade
|
||||||
|
|
|
@ -291,13 +291,15 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> createTransaction() async {
|
Future<PendingTransaction?> createTransaction() async {
|
||||||
try {
|
try {
|
||||||
state = IsExecutingState();
|
state = IsExecutingState();
|
||||||
pendingTransaction = await wallet.createTransaction(_credentials());
|
pendingTransaction = await wallet.createTransaction(_credentials());
|
||||||
state = ExecutedSuccessfullyState();
|
state = ExecutedSuccessfullyState();
|
||||||
|
return pendingTransaction;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
state = FailureState(e.toString());
|
state = FailureState(e.toString());
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue