mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 11:39:22 +00:00
Cw 537 integrate thor chain swaps (#1280)
* thorChain btc to eth swap
* eth to btc swap
* update the UI
* update localization
* Update thorchain_exchange.provider.dart
* minor fixes
* minor fix
* fix min amount bug
* revert amount_converter changes
* fetching thorChain traid info
* resolve evm related merge conflicts
* minor fix
* Fix eth transaction hash for Thorchain Integration
* add new status endpoint and refund address for eth
* Adjust affiliate fee
* Fix conflicts with main
* review comments + transaction filter item
* taproot addresses check
* added 10 outputs check
* Update thorchain_exchange.provider.dart
* minor fixes
* update thorchain title
* fix fetching rate for thorchain
* Revert "fix fetching rate for thorchain"
This reverts commit 3aa1386ecf
.
* fix thorchain exchange rate
---------
Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>
This commit is contained in:
parent
b9e803f3bd
commit
cdf081edfd
55 changed files with 534 additions and 102 deletions
BIN
assets/images/thorchain.png
Normal file
BIN
assets/images/thorchain.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
|
@ -195,7 +195,8 @@ abstract class ElectrumWalletBase
|
|||
List<BitcoinOutput> outputs,
|
||||
int? feeRate,
|
||||
BitcoinTransactionPriority? priority,
|
||||
{int? inputsCount}) async {
|
||||
{int? inputsCount,
|
||||
String? memo}) async {
|
||||
final utxos = <UtxoWithAddress>[];
|
||||
List<ECPrivate> privateKeys = [];
|
||||
|
||||
|
@ -253,7 +254,11 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
|
||||
final estimatedSize = BitcoinTransactionBuilder.estimateTransactionSize(
|
||||
utxos: utxos, outputs: outputs, network: network);
|
||||
utxos: utxos,
|
||||
outputs: outputs,
|
||||
network: network,
|
||||
memo: memo,
|
||||
);
|
||||
|
||||
int fee = feeRate != null
|
||||
? feeAmountWithFeeRate(feeRate, 0, 0, size: estimatedSize)
|
||||
|
@ -300,7 +305,13 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
}
|
||||
|
||||
return EstimatedTxResult(utxos: utxos, privateKeys: privateKeys, fee: fee, amount: amount);
|
||||
return EstimatedTxResult(
|
||||
utxos: utxos,
|
||||
privateKeys: privateKeys,
|
||||
fee: fee,
|
||||
amount: amount,
|
||||
memo: memo,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -348,13 +359,17 @@ abstract class ElectrumWalletBase
|
|||
outputs,
|
||||
transactionCredentials.feeRate,
|
||||
transactionCredentials.priority,
|
||||
memo: transactionCredentials.outputs.first.memo,
|
||||
);
|
||||
|
||||
final txb = BitcoinTransactionBuilder(
|
||||
utxos: estimatedTx.utxos,
|
||||
outputs: outputs,
|
||||
fee: BigInt.from(estimatedTx.fee),
|
||||
network: network);
|
||||
network: network,
|
||||
memo: estimatedTx.memo,
|
||||
outputOrdering: BitcoinOrdering.none,
|
||||
);
|
||||
|
||||
final transaction = txb.buildTransaction((txDigest, utxo, publicKey, sighash) {
|
||||
final key = estimatedTx.privateKeys
|
||||
|
@ -888,13 +903,19 @@ class EstimateTxParams {
|
|||
}
|
||||
|
||||
class EstimatedTxResult {
|
||||
EstimatedTxResult(
|
||||
{required this.utxos, required this.privateKeys, required this.fee, required this.amount});
|
||||
EstimatedTxResult({
|
||||
required this.utxos,
|
||||
required this.privateKeys,
|
||||
required this.fee,
|
||||
required this.amount,
|
||||
this.memo,
|
||||
});
|
||||
|
||||
final List<UtxoWithAddress> utxos;
|
||||
final List<ECPrivate> privateKeys;
|
||||
final int fee;
|
||||
final int amount;
|
||||
final String? memo;
|
||||
}
|
||||
|
||||
BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network) {
|
||||
|
|
|
@ -31,6 +31,9 @@ class PendingBitcoinTransaction with PendingTransaction {
|
|||
@override
|
||||
String get feeFormatted => bitcoinAmountToString(amount: fee);
|
||||
|
||||
@override
|
||||
int? get outputCount => _tx.outputs.length;
|
||||
|
||||
final List<void Function(ElectrumTransactionInfo transaction)> _listeners;
|
||||
|
||||
@override
|
||||
|
|
|
@ -140,6 +140,8 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
|||
|
||||
var allInputsAmount = 0;
|
||||
|
||||
final String? opReturnMemo = outputs.first.memo;
|
||||
|
||||
if (unspentCoins.isEmpty) await updateUnspent();
|
||||
|
||||
for (final utx in unspentCoins) {
|
||||
|
@ -282,6 +284,8 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
|||
txb.addOutput(changeAddress, changeValue);
|
||||
}
|
||||
|
||||
if (opReturnMemo != null) txb.addOutputData(opReturnMemo);
|
||||
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
final input = inputs[i];
|
||||
final keyPair = generateKeyPair(
|
||||
|
@ -290,7 +294,6 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
|||
txb.sign(i, keyPair, input.value);
|
||||
}
|
||||
|
||||
// Build the transaction
|
||||
final tx = txb.build();
|
||||
|
||||
return PendingBitcoinCashTransaction(tx, type,
|
||||
|
|
|
@ -7,7 +7,8 @@ class OutputInfo {
|
|||
this.formattedCryptoAmount,
|
||||
this.fiatAmount,
|
||||
this.note,
|
||||
this.extractedAddress,});
|
||||
this.extractedAddress,
|
||||
this.memo});
|
||||
|
||||
final String? fiatAmount;
|
||||
final String? cryptoAmount;
|
||||
|
@ -17,4 +18,5 @@ class OutputInfo {
|
|||
final bool sendAll;
|
||||
final bool isParsedAddress;
|
||||
final int? formattedCryptoAmount;
|
||||
final String? memo;
|
||||
}
|
|
@ -3,6 +3,7 @@ mixin PendingTransaction {
|
|||
String get amountFormatted;
|
||||
String get feeFormatted;
|
||||
String get hex;
|
||||
int? get outputCount => null;
|
||||
|
||||
Future<void> commit();
|
||||
}
|
|
@ -14,6 +14,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:http/http.dart';
|
||||
import 'package:erc20/erc20.dart';
|
||||
import 'package:web3dart/web3dart.dart';
|
||||
import 'package:hex/hex.dart' as hex;
|
||||
|
||||
abstract class EVMChainClient {
|
||||
final httpClient = Client();
|
||||
|
@ -85,6 +86,7 @@ abstract class EVMChainClient {
|
|||
required CryptoCurrency currency,
|
||||
required int exponent,
|
||||
String? contractAddress,
|
||||
String? data,
|
||||
}) async {
|
||||
assert(currency == CryptoCurrency.eth ||
|
||||
currency == CryptoCurrency.maticpoly ||
|
||||
|
@ -100,6 +102,7 @@ abstract class EVMChainClient {
|
|||
to: EthereumAddress.fromHex(toAddress),
|
||||
maxPriorityFeePerGas: EtherAmount.fromInt(EtherUnit.gwei, priority.tip),
|
||||
amount: isEVMCompatibleChain ? EtherAmount.inWei(BigInt.parse(amount)) : EtherAmount.zero(),
|
||||
data: data != null ? hexToBytes(data) : null,
|
||||
);
|
||||
|
||||
final signedTransaction =
|
||||
|
@ -140,12 +143,14 @@ abstract class EVMChainClient {
|
|||
required EthereumAddress to,
|
||||
required EtherAmount amount,
|
||||
EtherAmount? maxPriorityFeePerGas,
|
||||
Uint8List? data,
|
||||
}) {
|
||||
return Transaction(
|
||||
from: from,
|
||||
to: to,
|
||||
maxPriorityFeePerGas: maxPriorityFeePerGas,
|
||||
value: amount,
|
||||
data: data,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -222,6 +227,10 @@ abstract class EVMChainClient {
|
|||
}
|
||||
}
|
||||
|
||||
Uint8List hexToBytes(String hexString) {
|
||||
return Uint8List.fromList(hex.HEX.decode(hexString.startsWith('0x') ? hexString.substring(2) : hexString));
|
||||
}
|
||||
|
||||
void stop() {
|
||||
_client?.dispose();
|
||||
}
|
||||
|
|
|
@ -224,6 +224,13 @@ abstract class EVMChainWalletBase
|
|||
final outputs = _credentials.outputs;
|
||||
final hasMultiDestination = outputs.length > 1;
|
||||
|
||||
final String? opReturnMemo = outputs.first.memo;
|
||||
|
||||
String? hexOpReturnMemo;
|
||||
if (opReturnMemo != null) {
|
||||
hexOpReturnMemo = '0x${opReturnMemo.codeUnits.map((char) => char.toRadixString(16).padLeft(2, '0')).join()}';
|
||||
}
|
||||
|
||||
final CryptoCurrency transactionCurrency =
|
||||
balance.keys.firstWhere((element) => element.title == _credentials.currency.title);
|
||||
|
||||
|
@ -279,6 +286,7 @@ abstract class EVMChainWalletBase
|
|||
exponent: exponent,
|
||||
contractAddress:
|
||||
transactionCurrency is Erc20Token ? transactionCurrency.contractAddress : null,
|
||||
data: hexOpReturnMemo,
|
||||
);
|
||||
|
||||
return pendingEVMChainTransaction;
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:typed_data';
|
|||
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:web3dart/crypto.dart';
|
||||
import 'package:hex/hex.dart' as Hex;
|
||||
|
||||
class PendingEVMChainTransaction with PendingTransaction {
|
||||
final Function sendTransaction;
|
||||
|
@ -38,5 +39,12 @@ class PendingEVMChainTransaction with PendingTransaction {
|
|||
String get hex => bytesToHex(signedTransaction, include0x: true);
|
||||
|
||||
@override
|
||||
String get id => '';
|
||||
String get id {
|
||||
final String eip1559Hex = '0x02${hex.substring(2)}';
|
||||
final Uint8List bytes = Uint8List.fromList(Hex.HEX.decode(eip1559Hex.substring(2)));
|
||||
|
||||
var txid = keccak256(bytes);
|
||||
|
||||
return '0x${Hex.HEX.encode(txid)}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ class PolygonClient extends EVMChainClient {
|
|||
required EthereumAddress to,
|
||||
required EtherAmount amount,
|
||||
EtherAmount? maxPriorityFeePerGas,
|
||||
Uint8List? data,
|
||||
|
||||
}) {
|
||||
return Transaction(
|
||||
from: from,
|
||||
|
|
|
@ -85,7 +85,8 @@ class CWBitcoin extends Bitcoin {
|
|||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
formattedCryptoAmount: out.formattedCryptoAmount))
|
||||
formattedCryptoAmount: out.formattedCryptoAmount,
|
||||
memo: out.memo))
|
||||
.toList(),
|
||||
priority: priority as BitcoinTransactionPriority,
|
||||
feeRate: feeRate);
|
||||
|
|
|
@ -76,7 +76,8 @@ class CWEthereum extends Ethereum {
|
|||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
formattedCryptoAmount: out.formattedCryptoAmount))
|
||||
formattedCryptoAmount: out.formattedCryptoAmount,
|
||||
memo: out.memo))
|
||||
.toList(),
|
||||
priority: priority as EVMChainTransactionPriority,
|
||||
currency: currency,
|
||||
|
|
|
@ -22,6 +22,8 @@ class ExchangeProviderDescription extends EnumerableItem<int> with Serializable<
|
|||
ExchangeProviderDescription(title: 'Trocador', raw: 5, image: 'assets/images/trocador.png');
|
||||
static const exolix =
|
||||
ExchangeProviderDescription(title: 'Exolix', raw: 6, image: 'assets/images/exolix.png');
|
||||
static const thorChain =
|
||||
ExchangeProviderDescription(title: 'ThorChain' , raw: 8, image: 'assets/images/thorchain.png');
|
||||
|
||||
static const all = ExchangeProviderDescription(title: 'All trades', raw: 7, image: '');
|
||||
|
||||
|
@ -41,6 +43,8 @@ class ExchangeProviderDescription extends EnumerableItem<int> with Serializable<
|
|||
return trocador;
|
||||
case 6:
|
||||
return exolix;
|
||||
case 8:
|
||||
return thorChain;
|
||||
case 7:
|
||||
return all;
|
||||
default:
|
||||
|
|
248
lib/exchange/provider/thorchain_exchange.provider.dart
Normal file
248
lib/exchange/provider/thorchain_exchange.provider.dart
Normal file
|
@ -0,0 +1,248 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/limits.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
class ThorChainExchangeProvider extends ExchangeProvider {
|
||||
ThorChainExchangeProvider({required this.tradesStore})
|
||||
: super(pairList: supportedPairs(_notSupported));
|
||||
|
||||
static final List<CryptoCurrency> _notSupported = [
|
||||
...(CryptoCurrency.all
|
||||
.where((element) => ![
|
||||
CryptoCurrency.btc,
|
||||
CryptoCurrency.eth,
|
||||
CryptoCurrency.ltc,
|
||||
CryptoCurrency.bch,
|
||||
CryptoCurrency.aave,
|
||||
CryptoCurrency.dai,
|
||||
CryptoCurrency.gusd,
|
||||
CryptoCurrency.usdc,
|
||||
CryptoCurrency.usdterc20,
|
||||
CryptoCurrency.wbtc,
|
||||
].contains(element))
|
||||
.toList())
|
||||
];
|
||||
|
||||
static final isRefundAddressSupported = [CryptoCurrency.eth];
|
||||
|
||||
static const _baseURL = 'thornode.ninerealms.com';
|
||||
static const _quotePath = '/thorchain/quote/swap';
|
||||
static const _txInfoPath = '/thorchain/tx/status/';
|
||||
static const _affiliateName = 'cakewallet';
|
||||
static const _affiliateBps = '175';
|
||||
|
||||
final Box<Trade> tradesStore;
|
||||
|
||||
@override
|
||||
String get title => 'THORChain';
|
||||
|
||||
@override
|
||||
bool get isAvailable => true;
|
||||
|
||||
@override
|
||||
bool get isEnabled => true;
|
||||
|
||||
@override
|
||||
bool get supportsFixedRate => false;
|
||||
|
||||
@override
|
||||
ExchangeProviderDescription get description => ExchangeProviderDescription.thorChain;
|
||||
|
||||
@override
|
||||
Future<bool> checkIsAvailable() async => true;
|
||||
|
||||
@override
|
||||
Future<double> fetchRate(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required double amount,
|
||||
required bool isFixedRateMode,
|
||||
required bool isReceiveAmount}) async {
|
||||
try {
|
||||
if (amount == 0) return 0.0;
|
||||
|
||||
final params = {
|
||||
'from_asset': _normalizeCurrency(from),
|
||||
'to_asset': _normalizeCurrency(to),
|
||||
'amount': _doubleToThorChainString(amount),
|
||||
'affiliate': _affiliateName,
|
||||
'affiliate_bps': _affiliateBps
|
||||
};
|
||||
|
||||
final responseJSON = await _getSwapQuote(params);
|
||||
|
||||
final expectedAmountOut = responseJSON['expected_amount_out'] as String? ?? '0.0';
|
||||
|
||||
return _thorChainAmountToDouble(expectedAmountOut) / amount;
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Limits> fetchLimits(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required bool isFixedRateMode}) async {
|
||||
final params = {
|
||||
'from_asset': _normalizeCurrency(from),
|
||||
'to_asset': _normalizeCurrency(to),
|
||||
'amount': _doubleToThorChainString(1),
|
||||
'affiliate': _affiliateName,
|
||||
'affiliate_bps': _affiliateBps
|
||||
};
|
||||
|
||||
final responseJSON = await _getSwapQuote(params);
|
||||
final minAmountIn = responseJSON['recommended_min_amount_in'] as String? ?? '0.0';
|
||||
|
||||
return Limits(min: _thorChainAmountToDouble(minAmountIn));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode}) async {
|
||||
String formattedToAddress = request.toAddress.startsWith('bitcoincash:')
|
||||
? request.toAddress.replaceFirst('bitcoincash:', '')
|
||||
: request.toAddress;
|
||||
|
||||
final formattedFromAmount = double.parse(request.fromAmount);
|
||||
|
||||
final params = {
|
||||
'from_asset': _normalizeCurrency(request.fromCurrency),
|
||||
'to_asset': _normalizeCurrency(request.toCurrency),
|
||||
'amount': _doubleToThorChainString(formattedFromAmount),
|
||||
'destination': formattedToAddress,
|
||||
'affiliate': _affiliateName,
|
||||
'affiliate_bps': _affiliateBps,
|
||||
'refund_address':
|
||||
isRefundAddressSupported.contains(request.fromCurrency) ? request.refundAddress : '',
|
||||
};
|
||||
|
||||
final responseJSON = await _getSwapQuote(params);
|
||||
|
||||
final inputAddress = responseJSON['inbound_address'] as String?;
|
||||
final memo = responseJSON['memo'] as String?;
|
||||
|
||||
return Trade(
|
||||
id: '',
|
||||
from: request.fromCurrency,
|
||||
to: request.toCurrency,
|
||||
provider: description,
|
||||
inputAddress: inputAddress,
|
||||
createdAt: DateTime.now(),
|
||||
amount: request.fromAmount,
|
||||
state: TradeState.notFound,
|
||||
payoutAddress: request.toAddress,
|
||||
memo: memo);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> findTradeById({required String id}) async {
|
||||
if (id.isEmpty) throw Exception('Trade id is empty');
|
||||
final formattedId = id.startsWith('0x') ? id.substring(2) : id;
|
||||
final uri = Uri.https(_baseURL, '$_txInfoPath$formattedId');
|
||||
final response = await http.get(uri);
|
||||
|
||||
if (response.statusCode == 404) {
|
||||
throw Exception('Trade not found for id: $formattedId');
|
||||
} else if (response.statusCode != 200) {
|
||||
throw Exception('Unexpected HTTP status: ${response.statusCode}');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body);
|
||||
final Map<String, dynamic> stagesJson = responseJSON['stages'] as Map<String, dynamic>;
|
||||
|
||||
final inboundObservedStarted = stagesJson['inbound_observed']?['started'] as bool? ?? true;
|
||||
if (!inboundObservedStarted) {
|
||||
throw Exception('Trade has not started for id: $formattedId');
|
||||
}
|
||||
|
||||
final currentState = _updateStateBasedOnStages(stagesJson) ?? TradeState.notFound;
|
||||
|
||||
final tx = responseJSON['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 parts = memo?.split(':') ?? [];
|
||||
|
||||
final String toChain = parts.length > 1 ? parts[1].split('.')[0] : '';
|
||||
final String toAsset = parts.length > 1 && parts[1].split('.').length > 1 ? parts[1].split('.')[1].split('-')[0] : '';
|
||||
|
||||
final formattedToChain = CryptoCurrency.fromString(toChain);
|
||||
final toAssetWithChain = CryptoCurrency.fromString(toAsset, walletCurrency:formattedToChain);
|
||||
|
||||
final plannedOutTxs = responseJSON['planned_out_txs'] as List<dynamic>?;
|
||||
final isRefund = plannedOutTxs?.any((tx) => tx['refund'] == true) ?? false;
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
from: CryptoCurrency.fromString(tx['chain'] as String? ?? ''),
|
||||
to: toAssetWithChain,
|
||||
provider: description,
|
||||
inputAddress: fromAddress,
|
||||
payoutAddress: toAddress,
|
||||
amount: coins.first['amount'] as String? ?? '0.0',
|
||||
state: currentState,
|
||||
memo: memo,
|
||||
isRefund: isRefund,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> _getSwapQuote(Map<String, String> params) async {
|
||||
Uri uri = Uri.https(_baseURL, _quotePath, params);
|
||||
|
||||
final response = await http.get(uri);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception('Unexpected HTTP status: ${response.statusCode}');
|
||||
}
|
||||
|
||||
if (response.body.contains('error')) {
|
||||
throw Exception('Unexpected response: ${response.body}');
|
||||
}
|
||||
|
||||
return json.decode(response.body) as Map<String, dynamic>;
|
||||
}
|
||||
|
||||
String _normalizeCurrency(CryptoCurrency currency) {
|
||||
final networkTitle = currency.tag == 'ETH' ? 'ETH' : currency.title;
|
||||
return '$networkTitle.${currency.title}';
|
||||
}
|
||||
|
||||
String _doubleToThorChainString(double amount) => (amount * 1e8).toInt().toString();
|
||||
|
||||
double _thorChainAmountToDouble(String amount) => double.parse(amount) / 1e8;
|
||||
|
||||
TradeState? _updateStateBasedOnStages(Map<String, dynamic> stages) {
|
||||
TradeState? currentState;
|
||||
|
||||
if (stages['inbound_observed']['completed'] as bool? ?? false) {
|
||||
currentState = TradeState.confirmation;
|
||||
}
|
||||
if (stages['inbound_confirmation_counted']['completed'] as bool? ?? false) {
|
||||
currentState = TradeState.confirmed;
|
||||
}
|
||||
if (stages['inbound_finalised']['completed'] as bool? ?? false) {
|
||||
currentState = TradeState.processing;
|
||||
}
|
||||
if (stages['swap_finalised']['completed'] as bool? ?? false) {
|
||||
currentState = TradeState.traded;
|
||||
}
|
||||
if (stages['outbound_signed']['completed'] as bool? ?? false) {
|
||||
currentState = TradeState.success;
|
||||
}
|
||||
|
||||
return currentState;
|
||||
}
|
||||
}
|
|
@ -27,7 +27,10 @@ class Trade extends HiveObject {
|
|||
this.password,
|
||||
this.providerId,
|
||||
this.providerName,
|
||||
this.fromWalletAddress
|
||||
this.fromWalletAddress,
|
||||
this.memo,
|
||||
this.txId,
|
||||
this.isRefund,
|
||||
}) {
|
||||
if (provider != null) providerRaw = provider.raw;
|
||||
|
||||
|
@ -105,6 +108,15 @@ class Trade extends HiveObject {
|
|||
@HiveField(17)
|
||||
String? fromWalletAddress;
|
||||
|
||||
@HiveField(18)
|
||||
String? memo;
|
||||
|
||||
@HiveField(19)
|
||||
String? txId;
|
||||
|
||||
@HiveField(20)
|
||||
bool? isRefund;
|
||||
|
||||
static Trade fromMap(Map<String, Object?> map) {
|
||||
return Trade(
|
||||
id: map['id'] as String,
|
||||
|
@ -115,7 +127,10 @@ class Trade extends HiveObject {
|
|||
map['date'] != null ? DateTime.fromMillisecondsSinceEpoch(map['date'] as int) : null,
|
||||
amount: map['amount'] 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?,
|
||||
txId: map['tx_id'] as String?,
|
||||
isRefund: map['isRefund'] as bool?
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -128,7 +143,10 @@ class Trade extends HiveObject {
|
|||
'date': createdAt != null ? createdAt!.millisecondsSinceEpoch : null,
|
||||
'amount': amount,
|
||||
'wallet_id': walletId,
|
||||
'from_wallet_address': fromWalletAddress
|
||||
'from_wallet_address': fromWalletAddress,
|
||||
'memo': memo,
|
||||
'tx_id': txId,
|
||||
'isRefund': isRefund
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,8 @@ class TradeState extends EnumerableItem<String> with Serializable<String> {
|
|||
static const success = TradeState(raw: 'success', title: 'Success');
|
||||
static TradeState deserialize({required String raw}) {
|
||||
switch (raw) {
|
||||
case 'NOT_FOUND':
|
||||
return notFound;
|
||||
case 'pending':
|
||||
return pending;
|
||||
case 'confirming':
|
||||
|
@ -98,6 +100,7 @@ class TradeState extends EnumerableItem<String> with Serializable<String> {
|
|||
case 'sending':
|
||||
return sending;
|
||||
case 'success':
|
||||
case 'done':
|
||||
return success;
|
||||
default:
|
||||
throw Exception('Unexpected token: $raw in TradeState deserialize');
|
||||
|
|
|
@ -9,7 +9,7 @@ class FilterTile extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 24.0),
|
||||
padding: EdgeInsets.symmetric(vertical: 6.0, horizontal: 24.0),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ class SyncIndicatorIcon extends StatelessWidget {
|
|||
static const String created = 'created';
|
||||
static const String fetching = 'fetching';
|
||||
static const String finished = 'finished';
|
||||
static const String success = 'success';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -45,6 +46,7 @@ class SyncIndicatorIcon extends StatelessWidget {
|
|||
indicatorColor = Colors.red;
|
||||
break;
|
||||
case finished:
|
||||
case success:
|
||||
indicatorColor = PaletteDark.brightGreen;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -34,7 +34,9 @@ class TradeRow extends StatelessWidget {
|
|||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_getPoweredImage(provider)!,
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
child: Image.asset(provider.image, width: 36, height: 36)),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
|
@ -69,38 +71,4 @@ class TradeRow extends StatelessWidget {
|
|||
),
|
||||
));
|
||||
}
|
||||
|
||||
Widget? _getPoweredImage(ExchangeProviderDescription provider) {
|
||||
Widget? image;
|
||||
|
||||
switch (provider) {
|
||||
case ExchangeProviderDescription.xmrto:
|
||||
image = Image.asset('assets/images/xmrto.png', height: 36, width: 36);
|
||||
break;
|
||||
case ExchangeProviderDescription.changeNow:
|
||||
image = Image.asset('assets/images/changenow.png', height: 36, width: 36);
|
||||
break;
|
||||
case ExchangeProviderDescription.morphToken:
|
||||
image = Image.asset('assets/images/morph.png', height: 36, width: 36);
|
||||
break;
|
||||
case ExchangeProviderDescription.sideShift:
|
||||
image = Image.asset('assets/images/sideshift.png', width: 36, height: 36);
|
||||
break;
|
||||
case ExchangeProviderDescription.simpleSwap:
|
||||
image = Image.asset('assets/images/simpleSwap.png', width: 36, height: 36);
|
||||
break;
|
||||
case ExchangeProviderDescription.trocador:
|
||||
image = ClipRRect(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
child: Image.asset('assets/images/trocador.png', width: 36, height: 36));
|
||||
break;
|
||||
case ExchangeProviderDescription.exolix:
|
||||
image = Image.asset('assets/images/exolix.png', width: 36, height: 36);
|
||||
break;
|
||||
default:
|
||||
image = null;
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
|
||||
import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
|
@ -60,7 +62,7 @@ class ExchangePage extends BasePage {
|
|||
final _receiveAmountFocus = FocusNode();
|
||||
final _receiveAddressFocus = FocusNode();
|
||||
final _receiveAmountDebounce = Debounce(Duration(milliseconds: 500));
|
||||
final _depositAmountDebounce = Debounce(Duration(milliseconds: 500));
|
||||
Debounce _depositAmountDebounce = Debounce(Duration(milliseconds: 500));
|
||||
var _isReactionsSet = false;
|
||||
|
||||
final arrowBottomPurple = Image.asset(
|
||||
|
@ -431,7 +433,9 @@ class ExchangePage extends BasePage {
|
|||
}
|
||||
if (state is TradeIsCreatedSuccessfully) {
|
||||
exchangeViewModel.reset();
|
||||
Navigator.of(context).pushNamed(Routes.exchangeConfirm);
|
||||
(exchangeViewModel.tradesStore.trade?.provider == ExchangeProviderDescription.thorChain)
|
||||
? Navigator.of(context).pushReplacementNamed(Routes.exchangeTrade)
|
||||
: Navigator.of(context).pushReplacementNamed(Routes.exchangeConfirm);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -470,6 +474,13 @@ class ExchangePage extends BasePage {
|
|||
if (depositAmountController.text != exchangeViewModel.depositAmount &&
|
||||
depositAmountController.text != S.of(context).all) {
|
||||
exchangeViewModel.isSendAllEnabled = false;
|
||||
final isThorChain = exchangeViewModel.selectedProviders
|
||||
.any((provider) => provider is ThorChainExchangeProvider);
|
||||
|
||||
_depositAmountDebounce = isThorChain
|
||||
? Debounce(Duration(milliseconds: 1000))
|
||||
: Debounce(Duration(milliseconds: 500));
|
||||
|
||||
_depositAmountDebounce.run(() {
|
||||
exchangeViewModel.changeDepositAmount(amount: depositAmountController.text);
|
||||
exchangeViewModel.isReceiveAmountEntered = false;
|
||||
|
|
|
@ -8,13 +8,15 @@ part'trade_filter_store.g.dart';
|
|||
class TradeFilterStore = TradeFilterStoreBase with _$TradeFilterStore;
|
||||
|
||||
abstract class TradeFilterStoreBase with Store {
|
||||
TradeFilterStoreBase() : displayXMRTO = true,
|
||||
TradeFilterStoreBase()
|
||||
: displayXMRTO = true,
|
||||
displayChangeNow = true,
|
||||
displaySideShift = true,
|
||||
displayMorphToken = true,
|
||||
displaySimpleSwap = true,
|
||||
displayTrocador = true,
|
||||
displayExolix = true;
|
||||
displayExolix = true,
|
||||
displayThorChain = true;
|
||||
|
||||
@observable
|
||||
bool displayXMRTO;
|
||||
|
@ -37,8 +39,17 @@ abstract class TradeFilterStoreBase with Store {
|
|||
@observable
|
||||
bool displayExolix;
|
||||
|
||||
@observable
|
||||
bool displayThorChain;
|
||||
|
||||
@computed
|
||||
bool get displayAllTrades => displayChangeNow && displaySideShift && displaySimpleSwap && displayTrocador && displayExolix;
|
||||
bool get displayAllTrades =>
|
||||
displayChangeNow &&
|
||||
displaySideShift &&
|
||||
displaySimpleSwap &&
|
||||
displayTrocador &&
|
||||
displayExolix &&
|
||||
displayThorChain;
|
||||
|
||||
@action
|
||||
void toggleDisplayExchange(ExchangeProviderDescription provider) {
|
||||
|
@ -64,6 +75,9 @@ abstract class TradeFilterStoreBase with Store {
|
|||
case ExchangeProviderDescription.exolix:
|
||||
displayExolix = !displayExolix;
|
||||
break;
|
||||
case ExchangeProviderDescription.thorChain:
|
||||
displayThorChain = !displayThorChain;
|
||||
break;
|
||||
case ExchangeProviderDescription.all:
|
||||
if (displayAllTrades) {
|
||||
displayChangeNow = false;
|
||||
|
@ -73,6 +87,7 @@ abstract class TradeFilterStoreBase with Store {
|
|||
displaySimpleSwap = false;
|
||||
displayTrocador = false;
|
||||
displayExolix = false;
|
||||
displayThorChain = false;
|
||||
} else {
|
||||
displayChangeNow = true;
|
||||
displaySideShift = true;
|
||||
|
@ -81,6 +96,7 @@ abstract class TradeFilterStoreBase with Store {
|
|||
displaySimpleSwap = true;
|
||||
displayTrocador = true;
|
||||
displayExolix = true;
|
||||
displayThorChain = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -96,16 +112,13 @@ abstract class TradeFilterStoreBase with Store {
|
|||
? _trades
|
||||
.where((item) =>
|
||||
(displayXMRTO && item.trade.provider == ExchangeProviderDescription.xmrto) ||
|
||||
(displaySideShift &&
|
||||
item.trade.provider == ExchangeProviderDescription.sideShift) ||
|
||||
(displayChangeNow &&
|
||||
item.trade.provider == ExchangeProviderDescription.changeNow) ||
|
||||
(displayMorphToken &&
|
||||
item.trade.provider == ExchangeProviderDescription.morphToken) ||
|
||||
(displaySimpleSwap &&
|
||||
item.trade.provider == ExchangeProviderDescription.simpleSwap) ||
|
||||
(displaySideShift && item.trade.provider == ExchangeProviderDescription.sideShift) ||
|
||||
(displayChangeNow && item.trade.provider == ExchangeProviderDescription.changeNow) ||
|
||||
(displayMorphToken && item.trade.provider == ExchangeProviderDescription.morphToken) ||
|
||||
(displaySimpleSwap && item.trade.provider == ExchangeProviderDescription.simpleSwap) ||
|
||||
(displayTrocador && item.trade.provider == ExchangeProviderDescription.trocador) ||
|
||||
(displayExolix && item.trade.provider == ExchangeProviderDescription.exolix))
|
||||
(displayExolix && item.trade.provider == ExchangeProviderDescription.exolix) ||
|
||||
(displayThorChain && item.trade.provider == ExchangeProviderDescription.thorChain))
|
||||
.toList()
|
||||
: _trades;
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ abstract class AnonpayDetailsViewModelBase with Store {
|
|||
]);
|
||||
|
||||
items.add(TrackTradeListItem(
|
||||
title: 'Track',
|
||||
title: S.current.track,
|
||||
value: invoiceDetail.clearnetStatusUrl,
|
||||
onTap: () => launchUrlString(invoiceDetail.clearnetStatusUrl)));
|
||||
}
|
||||
|
|
|
@ -120,6 +120,11 @@ abstract class DashboardViewModelBase with Store {
|
|||
caption: ExchangeProviderDescription.exolix.title,
|
||||
onChanged: () =>
|
||||
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.exolix)),
|
||||
FilterItem(
|
||||
value: () => tradeFilterStore.displayThorChain,
|
||||
caption: ExchangeProviderDescription.thorChain.title,
|
||||
onChanged: () =>
|
||||
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.thorChain)),
|
||||
]
|
||||
},
|
||||
subname = '',
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
|||
import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
@ -47,6 +48,9 @@ abstract class ExchangeTradeViewModelBase with Store {
|
|||
case ExchangeProviderDescription.exolix:
|
||||
_provider = ExolixExchangeProvider();
|
||||
break;
|
||||
case ExchangeProviderDescription.thorChain:
|
||||
_provider = ThorChainExchangeProvider(tradesStore: trades);
|
||||
break;
|
||||
}
|
||||
|
||||
_updateItems();
|
||||
|
@ -100,8 +104,13 @@ abstract class ExchangeTradeViewModelBase with Store {
|
|||
final output = sendViewModel.outputs.first;
|
||||
output.address = trade.inputAddress ?? '';
|
||||
output.setCryptoAmount(trade.amount);
|
||||
if (_provider is ThorChainExchangeProvider) output.memo = trade.memo;
|
||||
sendViewModel.selectedCryptoCurrency = trade.from;
|
||||
await sendViewModel.createTransaction();
|
||||
final pendingTransaction = await sendViewModel.createTransaction(provider: _provider);
|
||||
if (_provider is ThorChainExchangeProvider) {
|
||||
trade.id = pendingTransaction?.id ?? '';
|
||||
trades.add(trade);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -127,6 +136,8 @@ abstract class ExchangeTradeViewModelBase with Store {
|
|||
tradesStore.trade!.from.tag != null ? '${tradesStore.trade!.from.tag}' + ' ' : '';
|
||||
final tagTo = tradesStore.trade!.to.tag != null ? '${tradesStore.trade!.to.tag}' + ' ' : '';
|
||||
items.clear();
|
||||
|
||||
if(trade.provider != ExchangeProviderDescription.thorChain)
|
||||
items.add(ExchangeTradeItem(
|
||||
title: "${trade.provider.title} ${S.current.id}", data: '${trade.id}', isCopied: true));
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||
import 'dart:collection';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
|
||||
|
@ -9,6 +10,7 @@ import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
|||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||
import 'package:cake_wallet/entities/wallet_contact.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_trade_state.dart';
|
||||
import 'package:cake_wallet/exchange/limits.dart';
|
||||
|
@ -18,6 +20,7 @@ import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
|||
import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
|
@ -96,7 +99,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
|||
|
||||
/// if the provider is not in the user settings (user's first time or newly added provider)
|
||||
/// then use its default value decided by us
|
||||
selectedProviders = ObservableList.of(providersForCurrentPair()
|
||||
selectedProviders = ObservableList.of(providerList
|
||||
.where((element) => exchangeProvidersSelection[element.title] == null
|
||||
? element.isEnabled
|
||||
: (exchangeProvidersSelection[element.title] as bool))
|
||||
|
@ -148,6 +151,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
|||
SimpleSwapExchangeProvider(),
|
||||
TrocadorExchangeProvider(
|
||||
useTorOnly: _useTorOnly, providerStates: _settingsStore.trocadorProviderStates),
|
||||
ThorChainExchangeProvider(tradesStore: trades),
|
||||
if (FeatureFlag.isExolixEnabled) ExolixExchangeProvider(),
|
||||
];
|
||||
|
||||
|
@ -496,8 +500,16 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
|||
await provider.createTrade(request: request, isFixedRateMode: isFixedRateMode);
|
||||
trade.walletId = wallet.id;
|
||||
trade.fromWalletAddress = wallet.walletAddresses.address;
|
||||
|
||||
if (!isCanCreateTrade(trade)) {
|
||||
tradeState = TradeIsCreatedFailure(
|
||||
title: S.current.trade_not_created,
|
||||
error: S.current.thorchain_taproot_address_not_supported);
|
||||
return;
|
||||
}
|
||||
|
||||
tradesStore.setTrade(trade);
|
||||
await trades.add(trade);
|
||||
if (trade.provider != ExchangeProviderDescription.thorChain) await trades.add(trade);
|
||||
tradeState = TradeIsCreatedSuccessfully(trade: trade);
|
||||
|
||||
/// return after the first successful trade
|
||||
|
@ -749,4 +761,17 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
|||
int get depositMaxDigits => depositCurrency.decimals;
|
||||
|
||||
int get receiveMaxDigits => receiveCurrency.decimals;
|
||||
|
||||
bool isCanCreateTrade(Trade trade) {
|
||||
if (trade.provider == ExchangeProviderDescription.thorChain) {
|
||||
final payoutAddress = trade.payoutAddress ?? '';
|
||||
final fromWalletAddress = trade.fromWalletAddress ?? '';
|
||||
final tapRootPattern = RegExp(P2trAddress.regex.pattern);
|
||||
|
||||
if (tapRootPattern.hasMatch(payoutAddress) || tapRootPattern.hasMatch(fromWalletAddress)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ abstract class OrderDetailsViewModelBase with Store {
|
|||
final buildURL = trackUrl + '${order.transferId}';
|
||||
items.add(
|
||||
TrackTradeListItem(
|
||||
title: 'Track',
|
||||
title: S.current.track,
|
||||
value: buildURL,
|
||||
onTap: () {
|
||||
try {
|
||||
|
|
|
@ -66,6 +66,8 @@ abstract class OutputBase with Store {
|
|||
@observable
|
||||
String extractedAddress;
|
||||
|
||||
String? memo;
|
||||
|
||||
@computed
|
||||
bool get isParsedAddress =>
|
||||
parsedAddress.parseFrom != ParseFrom.notParsed && parsedAddress.name.isNotEmpty;
|
||||
|
@ -175,6 +177,7 @@ abstract class OutputBase with Store {
|
|||
fiatAmount = '';
|
||||
address = '';
|
||||
note = '';
|
||||
memo = null;
|
||||
resetParsedAddress();
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@ import 'package:cake_wallet/entities/contact.dart';
|
|||
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
|
||||
import 'package:cake_wallet/entities/transaction_description.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
|
||||
import 'package:cake_wallet/nano/nano.dart';
|
||||
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
|
||||
import 'package:cake_wallet/entities/contact_record.dart';
|
||||
|
@ -296,14 +298,20 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
|||
}
|
||||
|
||||
@action
|
||||
Future<void> createTransaction() async {
|
||||
Future<PendingTransaction?> createTransaction({ExchangeProvider? provider}) async {
|
||||
try {
|
||||
state = IsExecutingState();
|
||||
pendingTransaction = await wallet.createTransaction(_credentials());
|
||||
if (provider is ThorChainExchangeProvider) {
|
||||
final outputCount = pendingTransaction?.outputCount ?? 0;
|
||||
if (outputCount > 10) throw Exception("ThorChain does not support more than 10 outputs");
|
||||
}
|
||||
state = ExecutedSuccessfullyState();
|
||||
return pendingTransaction;
|
||||
} catch (e) {
|
||||
print('Failed with ${e.toString()}');
|
||||
state = FailureState(e.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
|||
import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
@ -52,6 +53,9 @@ abstract class TradeDetailsViewModelBase with Store {
|
|||
case ExchangeProviderDescription.exolix:
|
||||
_provider = ExolixExchangeProvider();
|
||||
break;
|
||||
case ExchangeProviderDescription.thorChain:
|
||||
_provider = ThorChainExchangeProvider(tradesStore: trades);
|
||||
break;
|
||||
}
|
||||
|
||||
_updateItems();
|
||||
|
@ -62,6 +66,24 @@ abstract class TradeDetailsViewModelBase with Store {
|
|||
}
|
||||
}
|
||||
|
||||
static String? getTrackUrl(ExchangeProviderDescription provider, Trade trade) {
|
||||
switch (provider) {
|
||||
case ExchangeProviderDescription.changeNow:
|
||||
return 'https://changenow.io/exchange/txs/${trade.id}';
|
||||
case ExchangeProviderDescription.sideShift:
|
||||
return 'https://sideshift.ai/orders/${trade.id}';
|
||||
case ExchangeProviderDescription.simpleSwap:
|
||||
return 'https://simpleswap.io/exchange?id=${trade.id}';
|
||||
case ExchangeProviderDescription.trocador:
|
||||
return 'https://trocador.app/en/checkout/${trade.id}';
|
||||
case ExchangeProviderDescription.exolix:
|
||||
return 'https://exolix.com/transaction/${trade.id}';
|
||||
case ExchangeProviderDescription.thorChain:
|
||||
return 'https://track.ninerealms.com/${trade.id}';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
final Box<Trade> trades;
|
||||
|
||||
@observable
|
||||
|
@ -125,46 +147,26 @@ abstract class TradeDetailsViewModelBase with Store {
|
|||
items.add(StandartListItem(
|
||||
title: S.current.trade_details_provider, value: trade.provider.toString()));
|
||||
|
||||
if (trade.provider == ExchangeProviderDescription.changeNow) {
|
||||
final buildURL = 'https://changenow.io/exchange/txs/${trade.id.toString()}';
|
||||
final trackUrl = TradeDetailsViewModelBase.getTrackUrl(trade.provider, trade);
|
||||
if (trackUrl != null) {
|
||||
items.add(TrackTradeListItem(
|
||||
title: 'Track',
|
||||
value: buildURL,
|
||||
onTap: () {
|
||||
_launchUrl(buildURL);
|
||||
}));
|
||||
title: S.current.track, value: trackUrl, onTap: () => _launchUrl(trackUrl)));
|
||||
}
|
||||
|
||||
if (trade.provider == ExchangeProviderDescription.sideShift) {
|
||||
final buildURL = 'https://sideshift.ai/orders/${trade.id.toString()}';
|
||||
items.add(
|
||||
TrackTradeListItem(title: 'Track', value: buildURL, onTap: () => _launchUrl(buildURL)));
|
||||
}
|
||||
|
||||
if (trade.provider == ExchangeProviderDescription.simpleSwap) {
|
||||
final buildURL = 'https://simpleswap.io/exchange?id=${trade.id.toString()}';
|
||||
items.add(
|
||||
TrackTradeListItem(title: 'Track', value: buildURL, onTap: () => _launchUrl(buildURL)));
|
||||
if (trade.isRefund == true) {
|
||||
items.add(StandartListItem(
|
||||
title: 'Refund', value: trade.refundAddress ?? ''));
|
||||
}
|
||||
|
||||
if (trade.provider == ExchangeProviderDescription.trocador) {
|
||||
final buildURL = 'https://trocador.app/en/checkout/${trade.id.toString()}';
|
||||
items.add(
|
||||
TrackTradeListItem(title: 'Track', value: buildURL, onTap: () => _launchUrl(buildURL)));
|
||||
|
||||
items.add(StandartListItem(
|
||||
title: '${trade.providerName} ${S.current.id.toUpperCase()}',
|
||||
value: trade.providerId ?? ''));
|
||||
|
||||
if (trade.password != null && trade.password!.isNotEmpty)
|
||||
if (trade.password != null && trade.password!.isNotEmpty) {
|
||||
items.add(StandartListItem(
|
||||
title: '${trade.providerName} ${S.current.password}', value: trade.password ?? ''));
|
||||
}
|
||||
|
||||
if (trade.provider == ExchangeProviderDescription.exolix) {
|
||||
final buildURL = 'https://exolix.com/transaction/${trade.id.toString()}';
|
||||
items.add(
|
||||
TrackTradeListItem(title: 'Track', value: buildURL, onTap: () => _launchUrl(buildURL)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -643,6 +643,7 @@
|
|||
"template_name": "اسم القالب",
|
||||
"third_intro_content": "يعيش Yats خارج Cake Wallet أيضًا. يمكن استبدال أي عنوان محفظة على وجه الأرض بـ Yat!",
|
||||
"third_intro_title": "يتماشي Yat بلطف مع الآخرين",
|
||||
"thorchain_taproot_address_not_supported": "لا يدعم مزود Thorchain عناوين Taproot. يرجى تغيير العنوان أو تحديد مزود مختلف.",
|
||||
"time": "${minutes}د ${seconds}س",
|
||||
"tip": "بقشيش:",
|
||||
"today": "اليوم",
|
||||
|
@ -660,6 +661,7 @@
|
|||
"totp_code": "كود TOTP",
|
||||
"totp_secret_code": "كود TOTP السري",
|
||||
"totp_verification_success": "تم التحقق بنجاح!",
|
||||
"track": " ﺭﺎﺴﻣ",
|
||||
"trade_details_copied": "تم نسخ ${title} إلى الحافظة",
|
||||
"trade_details_created_at": "أنشئت في",
|
||||
"trade_details_fetching": "جار الجلب",
|
||||
|
|
|
@ -643,6 +643,7 @@
|
|||
"template_name": "Име на шаблон",
|
||||
"third_intro_content": "Yats също живее извън Cake Wallet. Всеки адрес на портфейл може да бъде заменен с Yat!",
|
||||
"third_intro_title": "Yat добре се сработва с други",
|
||||
"thorchain_taproot_address_not_supported": "Доставчикът на Thorchain не поддържа адреси на TapRoot. Моля, променете адреса или изберете друг доставчик.",
|
||||
"time": "${minutes} мин ${seconds} сек",
|
||||
"tip": "Tip:",
|
||||
"today": "Днес",
|
||||
|
@ -660,6 +661,7 @@
|
|||
"totp_code": "TOTP код",
|
||||
"totp_secret_code": "TOTP таен код",
|
||||
"totp_verification_success": "Проверката е успешна!",
|
||||
"track": "Писта",
|
||||
"trade_details_copied": "${title} копирано",
|
||||
"trade_details_created_at": "Създадено",
|
||||
"trade_details_fetching": "Обработка",
|
||||
|
|
|
@ -643,6 +643,7 @@
|
|||
"template_name": "Název šablony",
|
||||
"third_intro_content": "Yat existuje i mimo Cake Wallet. Jakákoliv adresa peněženky na světě může být nahrazena Yatem!",
|
||||
"third_intro_title": "Yat dobře spolupracuje s ostatními",
|
||||
"thorchain_taproot_address_not_supported": "Poskytovatel Thorchain nepodporuje adresy Taproot. Změňte adresu nebo vyberte jiného poskytovatele.",
|
||||
"time": "${minutes}m ${seconds}s",
|
||||
"tip": "Spropitné:",
|
||||
"today": "Dnes",
|
||||
|
@ -660,6 +661,7 @@
|
|||
"totp_code": "Kód TOTP",
|
||||
"totp_secret_code": "Tajný kód TOTP",
|
||||
"totp_verification_success": "Ověření proběhlo úspěšně!",
|
||||
"track": "Dráha",
|
||||
"trade_details_copied": "${title} zkopírováno do schránky",
|
||||
"trade_details_created_at": "Vytvořeno v",
|
||||
"trade_details_fetching": "Získávám",
|
||||
|
|
|
@ -644,6 +644,7 @@
|
|||
"template_name": "Vorlagenname",
|
||||
"third_intro_content": "Yats leben auch außerhalb von Cake Wallet. Jede Wallet-Adresse auf der Welt kann durch ein Yat ersetzt werden!",
|
||||
"third_intro_title": "Yat spielt gut mit anderen",
|
||||
"thorchain_taproot_address_not_supported": "Der Thorchain -Anbieter unterstützt keine Taproot -Adressen. Bitte ändern Sie die Adresse oder wählen Sie einen anderen Anbieter aus.",
|
||||
"time": "${minutes}m ${seconds}s",
|
||||
"tip": "Hinweis:",
|
||||
"today": "Heute",
|
||||
|
@ -661,6 +662,7 @@
|
|||
"totp_code": "TOTP-Code",
|
||||
"totp_secret_code": "TOTP-Geheimcode",
|
||||
"totp_verification_success": "Verifizierung erfolgreich!",
|
||||
"track": "Schiene",
|
||||
"trade_details_copied": "${title} in die Zwischenablage kopiert",
|
||||
"trade_details_created_at": "Erzeugt am",
|
||||
"trade_details_fetching": "Wird ermittelt",
|
||||
|
|
|
@ -643,6 +643,7 @@
|
|||
"template_name": "Template Name",
|
||||
"third_intro_content": "Yats live outside of Cake Wallet, too. Any wallet address on earth can be replaced with a Yat!",
|
||||
"third_intro_title": "Yat plays nicely with others",
|
||||
"thorchain_taproot_address_not_supported": "The ThorChain provider does not support Taproot addresses. Please change the address or select a different provider.",
|
||||
"time": "${minutes}m ${seconds}s",
|
||||
"tip": "Tip:",
|
||||
"today": "Today",
|
||||
|
@ -660,6 +661,7 @@
|
|||
"totp_code": "TOTP Code",
|
||||
"totp_secret_code": "TOTP Secret Code",
|
||||
"totp_verification_success": "Verification Successful!",
|
||||
"track": "Track",
|
||||
"trade_details_copied": "${title} copied to Clipboard",
|
||||
"trade_details_created_at": "Created at",
|
||||
"trade_details_fetching": "Fetching",
|
||||
|
|
|
@ -644,6 +644,7 @@
|
|||
"template_name": "Nombre de la plantilla",
|
||||
"third_intro_content": "Los Yats también viven fuera de Cake Wallet. Cualquier dirección de billetera en la tierra se puede reemplazar con un Yat!",
|
||||
"third_intro_title": "Yat juega muy bien con otras",
|
||||
"thorchain_taproot_address_not_supported": "El proveedor de Thorchain no admite las direcciones de Taproot. Cambie la dirección o seleccione un proveedor diferente.",
|
||||
"time": "${minutes}m ${seconds}s",
|
||||
"tip": "Consejo:",
|
||||
"today": "Hoy",
|
||||
|
@ -661,6 +662,7 @@
|
|||
"totp_code": "Código TOTP",
|
||||
"totp_secret_code": "Código secreto TOTP",
|
||||
"totp_verification_success": "¡Verificación exitosa!",
|
||||
"track": "Pista",
|
||||
"trade_details_copied": "${title} Copiado al portapapeles",
|
||||
"trade_details_created_at": "Creado en",
|
||||
"trade_details_fetching": "Cargando",
|
||||
|
|
|
@ -643,6 +643,7 @@
|
|||
"template_name": "Nom du modèle",
|
||||
"third_intro_content": "Les Yats existent aussi en dehors de Cake Wallet. Toute adresse sur terre peut être remplacée par un Yat !",
|
||||
"third_intro_title": "Yat est universel",
|
||||
"thorchain_taproot_address_not_supported": "Le fournisseur de Thorchain ne prend pas en charge les adresses de tapoot. Veuillez modifier l'adresse ou sélectionner un autre fournisseur.",
|
||||
"time": "${minutes}m ${seconds}s",
|
||||
"tip": "Pourboire :",
|
||||
"today": "Aujourd'hui",
|
||||
|
@ -660,6 +661,7 @@
|
|||
"totp_code": "Code TOTP",
|
||||
"totp_secret_code": "Secret TOTP",
|
||||
"totp_verification_success": "Vérification réussie !",
|
||||
"track": "Piste",
|
||||
"trade_details_copied": "${title} copié vers le presse-papier",
|
||||
"trade_details_created_at": "Créé le",
|
||||
"trade_details_fetching": "Récupération",
|
||||
|
|
|
@ -645,6 +645,7 @@
|
|||
"template_name": "Sunan Samfura",
|
||||
"third_intro_content": "Yats suna zaune a wajen Kek Wallet, kuma. Ana iya maye gurbin kowane adireshin walat a duniya da Yat!",
|
||||
"third_intro_title": "Yat yana wasa da kyau tare da wasu",
|
||||
"thorchain_taproot_address_not_supported": "Mai ba da tallafi na ThorChain baya goyan bayan adreshin taproot. Da fatan za a canza adireshin ko zaɓi mai bayarwa daban.",
|
||||
"time": "${minutes}m ${seconds}s",
|
||||
"tip": "Tukwici:",
|
||||
"today": "Yau",
|
||||
|
@ -662,6 +663,7 @@
|
|||
"totp_code": "Lambar totp",
|
||||
"totp_secret_code": "Lambar sirri",
|
||||
"totp_verification_success": "Tabbatar cin nasara!",
|
||||
"track": "Waƙa",
|
||||
"trade_details_copied": "${title} an kwafa zuwa cikin kwafin",
|
||||
"trade_details_created_at": "An ƙirƙira a",
|
||||
"trade_details_fetching": "Daukewa",
|
||||
|
|
|
@ -645,6 +645,7 @@
|
|||
"template_name": "टेम्पलेट नाम",
|
||||
"third_intro_content": "Yats Cake Wallet के बाहर भी रहता है। धरती पर किसी भी वॉलेट पते को Yat से बदला जा सकता है!",
|
||||
"third_intro_title": "Yat दूसरों के साथ अच्छा खेलता है",
|
||||
"thorchain_taproot_address_not_supported": "थोरचेन प्रदाता टैपरोट पते का समर्थन नहीं करता है। कृपया पता बदलें या एक अलग प्रदाता का चयन करें।",
|
||||
"time": "${minutes}m ${seconds}s",
|
||||
"tip": "टिप:",
|
||||
"today": "आज",
|
||||
|
@ -662,6 +663,7 @@
|
|||
"totp_code": "टीओटीपी कोड",
|
||||
"totp_secret_code": "टीओटीपी गुप्त कोड",
|
||||
"totp_verification_success": "सत्यापन सफल!",
|
||||
"track": "रास्ता",
|
||||
"trade_details_copied": "${title} क्लिपबोर्ड पर नकल",
|
||||
"trade_details_created_at": "पर बनाया गया",
|
||||
"trade_details_fetching": "ला रहा है",
|
||||
|
|
|
@ -643,6 +643,7 @@
|
|||
"template_name": "Naziv predloška",
|
||||
"third_intro_content": "Yats žive i izvan Cake Wallet -a. Bilo koja adresa novčanika na svijetu može se zamijeniti Yat!",
|
||||
"third_intro_title": "Yat se lijepo igra s drugima",
|
||||
"thorchain_taproot_address_not_supported": "Thorchain pružatelj ne podržava Taproot adrese. Promijenite adresu ili odaberite drugog davatelja usluga.",
|
||||
"time": "${minutes}m ${seconds}s",
|
||||
"tip": "Savjet:",
|
||||
"today": "Danas",
|
||||
|
@ -660,6 +661,7 @@
|
|||
"totp_code": "TOTP kod",
|
||||
"totp_secret_code": "TOTP tajni kod",
|
||||
"totp_verification_success": "Provjera uspješna!",
|
||||
"track": "Staza",
|
||||
"trade_details_copied": "${title} kopiran u međuspremnik",
|
||||
"trade_details_created_at": "Stvoreno u",
|
||||
"trade_details_fetching": "Dohvaćanje",
|
||||
|
|
|
@ -646,6 +646,7 @@
|
|||
"template_name": "Nama Templat",
|
||||
"third_intro_content": "Yats hidup di luar Cake Wallet juga. Setiap alamat dompet di dunia dapat diganti dengan Yat!",
|
||||
"third_intro_title": "Yat bermain baik dengan yang lain",
|
||||
"thorchain_taproot_address_not_supported": "Penyedia Thorchain tidak mendukung alamat Taproot. Harap ubah alamatnya atau pilih penyedia yang berbeda.",
|
||||
"time": "${minutes}m ${seconds}s",
|
||||
"tip": "Tip:",
|
||||
"today": "Hari ini",
|
||||
|
@ -663,6 +664,7 @@
|
|||
"totp_code": "Kode TOTP",
|
||||
"totp_secret_code": "Kode Rahasia TOTP",
|
||||
"totp_verification_success": "Verifikasi Berhasil!",
|
||||
"track": "Melacak",
|
||||
"trade_details_copied": "${title} disalin ke Clipboard",
|
||||
"trade_details_created_at": "Dibuat pada",
|
||||
"trade_details_fetching": "Mengambil",
|
||||
|
|
|
@ -645,6 +645,7 @@
|
|||
"template_name": "Nome modello",
|
||||
"third_intro_content": "Yat può funzionare anche fuori da Cake Wallet. Qualsiasi indirizzo di portafoglio sulla terra può essere sostituito con uno Yat!",
|
||||
"third_intro_title": "Yat gioca bene con gli altri",
|
||||
"thorchain_taproot_address_not_supported": "Il provider di Thorchain non supporta gli indirizzi di TapRoot. Si prega di modificare l'indirizzo o selezionare un fornitore diverso.",
|
||||
"time": "${minutes}m ${seconds}s",
|
||||
"tip": "Suggerimento:",
|
||||
"today": "Oggi",
|
||||
|
@ -662,6 +663,7 @@
|
|||
"totp_code": "Codice TOTP",
|
||||
"totp_secret_code": "TOTP codice segreto",
|
||||
"totp_verification_success": "Verifica riuscita!",
|
||||
"track": "Traccia",
|
||||
"trade_details_copied": "${title} copiati negli Appunti",
|
||||
"trade_details_created_at": "Creato alle",
|
||||
"trade_details_fetching": "Recupero",
|
||||
|
|
|
@ -644,6 +644,7 @@
|
|||
"template_name": "テンプレート名",
|
||||
"third_intro_content": "YatsはCakeWalletの外にも住んでいます。 地球上のどのウォレットアドレスもYatに置き換えることができます!",
|
||||
"third_intro_title": "Yatは他の人とうまく遊ぶ",
|
||||
"thorchain_taproot_address_not_supported": "Thorchainプロバイダーは、TapRootアドレスをサポートしていません。アドレスを変更するか、別のプロバイダーを選択してください。",
|
||||
"time": "${minutes}m ${seconds}s",
|
||||
"tip": "ヒント: ",
|
||||
"today": "今日",
|
||||
|
@ -661,6 +662,7 @@
|
|||
"totp_code": "TOTP コード",
|
||||
"totp_secret_code": "TOTPシークレットコード",
|
||||
"totp_verification_success": "検証成功!",
|
||||
"track": "追跡",
|
||||
"trade_details_copied": "${title} クリップボードにコピーしました",
|
||||
"trade_details_created_at": "で作成",
|
||||
"trade_details_fetching": "フェッチング",
|
||||
|
|
|
@ -644,6 +644,7 @@
|
|||
"template_name": "템플릿 이름",
|
||||
"third_intro_content": "Yats는 Cake Wallet 밖에서도 살고 있습니다. 지구상의 모든 지갑 주소는 Yat!",
|
||||
"third_intro_title": "Yat는 다른 사람들과 잘 놉니다.",
|
||||
"thorchain_taproot_address_not_supported": "Thorchain 제공 업체는 Taproot 주소를 지원하지 않습니다. 주소를 변경하거나 다른 공급자를 선택하십시오.",
|
||||
"time": "${minutes}m ${seconds}s",
|
||||
"tip": "팁:",
|
||||
"today": "오늘",
|
||||
|
@ -661,6 +662,7 @@
|
|||
"totp_code": "TOTP 코드",
|
||||
"totp_secret_code": "TOTP 비밀 코드",
|
||||
"totp_verification_success": "확인 성공!",
|
||||
"track": "길",
|
||||
"trade_details_copied": "${title} 클립 보드에 복사",
|
||||
"trade_details_created_at": "에 작성",
|
||||
"trade_details_fetching": "가져 오는 중",
|
||||
|
|
|
@ -643,6 +643,7 @@
|
|||
"template_name": "နမူနာပုံစံ",
|
||||
"third_intro_content": "Yats သည် Cake Wallet အပြင်ဘက်တွင် နေထိုင်ပါသည်။ ကမ္ဘာပေါ်ရှိ မည်သည့်ပိုက်ဆံအိတ်လိပ်စာကို Yat ဖြင့် အစားထိုးနိုင်ပါသည်။",
|
||||
"third_intro_title": "Yat သည် အခြားသူများနှင့် ကောင်းစွာကစားသည်။",
|
||||
"thorchain_taproot_address_not_supported": "Thorchain Provider သည် Taproot လိပ်စာများကိုမထောက်ခံပါ။ ကျေးဇူးပြု. လိပ်စာကိုပြောင်းပါသို့မဟုတ်အခြားပံ့ပိုးပေးသူကိုရွေးချယ်ပါ။",
|
||||
"time": "${minutes}m ${seconds}s",
|
||||
"tip": "အကြံပြုချက်-",
|
||||
"today": "ဒီနေ့",
|
||||
|
@ -660,6 +661,7 @@
|
|||
"totp_code": "TOTP ကုဒ်",
|
||||
"totp_secret_code": "TOTP လျှို့ဝှက်ကုဒ်",
|
||||
"totp_verification_success": "အတည်ပြုခြင်း အောင်မြင်ပါသည်။",
|
||||
"track": "တစ်ပုဒ်",
|
||||
"trade_details_copied": "${title} ကို Clipboard သို့ ကူးယူထားသည်။",
|
||||
"trade_details_created_at": "တွင်ဖန်တီးခဲ့သည်။",
|
||||
"trade_details_fetching": "ခေါ်ယူခြင်း။",
|
||||
|
|
|
@ -643,6 +643,7 @@
|
|||
"template_name": "Sjabloonnaam",
|
||||
"third_intro_content": "Yats wonen ook buiten Cake Wallet. Elk portemonnee-adres op aarde kan worden vervangen door een Yat!",
|
||||
"third_intro_title": "Yat speelt leuk met anderen",
|
||||
"thorchain_taproot_address_not_supported": "De Thorchain -provider ondersteunt geen Taprooot -adressen. Wijzig het adres of selecteer een andere provider.",
|
||||
"time": "${minutes}m ${seconds}s",
|
||||
"tip": "Tip:",
|
||||
"today": "Vandaag",
|
||||
|
@ -660,6 +661,7 @@
|
|||
"totp_code": "TOTP-code",
|
||||
"totp_secret_code": "TOTP-geheime code",
|
||||
"totp_verification_success": "Verificatie geslaagd!",
|
||||
"track": "Spoor",
|
||||
"trade_details_copied": "${title} gekopieerd naar het klembord",
|
||||
"trade_details_created_at": "Gemaakt bij",
|
||||
"trade_details_fetching": "Ophalen",
|
||||
|
|
|
@ -643,6 +643,7 @@
|
|||
"template_name": "Nazwa szablonu",
|
||||
"third_intro_content": "Yats mieszkają również poza Cake Wallet. Każdy adres portfela na ziemi można zastąpić Yat!",
|
||||
"third_intro_title": "Yat ładnie bawi się z innymi",
|
||||
"thorchain_taproot_address_not_supported": "Dostawca Thorchain nie obsługuje adresów TAPROOT. Zmień adres lub wybierz innego dostawcę.",
|
||||
"time": "${minutes}m ${seconds}s",
|
||||
"tip": "wskazówka:",
|
||||
"today": "Dzisiaj",
|
||||
|
@ -660,6 +661,7 @@
|
|||
"totp_code": "Kod TOTP",
|
||||
"totp_secret_code": "Tajny kod TOTP",
|
||||
"totp_verification_success": "Weryfikacja powiodła się!",
|
||||
"track": "Ścieżka",
|
||||
"trade_details_copied": "${title} skopiowane do schowka",
|
||||
"trade_details_created_at": "Utworzono ",
|
||||
"trade_details_fetching": "Pobieranie",
|
||||
|
|
|
@ -645,6 +645,7 @@
|
|||
"template_name": "Nome do modelo",
|
||||
"third_intro_content": "Yats também mora fora da Cake Wallet. Qualquer endereço de carteira na Terra pode ser substituído por um Yat!",
|
||||
"third_intro_title": "Yat joga bem com os outros",
|
||||
"thorchain_taproot_address_not_supported": "O provedor de Thorchain não suporta endereços de raiz de Tap. Altere o endereço ou selecione um provedor diferente.",
|
||||
"time": "${minutes}m ${seconds}s",
|
||||
"tip": "Dica:",
|
||||
"today": "Hoje",
|
||||
|
@ -662,6 +663,7 @@
|
|||
"totp_code": "Código TOTP",
|
||||
"totp_secret_code": "Código Secreto TOTP",
|
||||
"totp_verification_success": "Verificação bem-sucedida!",
|
||||
"track": "Acompanhar",
|
||||
"trade_details_copied": "${title} copiados para a área de transferência",
|
||||
"trade_details_created_at": "Criada em",
|
||||
"trade_details_fetching": "Buscando",
|
||||
|
|
|
@ -644,6 +644,7 @@
|
|||
"template_name": "Имя Шаблона",
|
||||
"third_intro_content": "Yat находятся за пределами Cake Wallet. Любой адрес кошелька на земле можно заменить на Yat!",
|
||||
"third_intro_title": "Yat хорошо взаимодействует с другими",
|
||||
"thorchain_taproot_address_not_supported": "Поставщик Thorchain не поддерживает адреса taproot. Пожалуйста, измените адрес или выберите другого поставщика.",
|
||||
"time": "${minutes}мин ${seconds}сек",
|
||||
"tip": "Совет:",
|
||||
"today": "Сегодня",
|
||||
|
@ -661,6 +662,7 @@
|
|||
"totp_code": "TOTP-код",
|
||||
"totp_secret_code": "Секретный код ТОТП",
|
||||
"totp_verification_success": "Проверка прошла успешно!",
|
||||
"track": "Отслеживать",
|
||||
"trade_details_copied": "${title} скопировано в буфер обмена",
|
||||
"trade_details_created_at": "Создано",
|
||||
"trade_details_fetching": "Получение",
|
||||
|
|
|
@ -643,6 +643,7 @@
|
|||
"template_name": "ชื่อแม่แบบ",
|
||||
"third_intro_content": "Yat อาศัยอยู่นอก Cake Wallet ด้วย ที่อยู่กระเป๋าใดๆ ทั่วโลกสามารถแทนด้วย Yat ได้อีกด้วย!",
|
||||
"third_intro_title": "Yat ปฏิบัติตนอย่างดีกับผู้อื่น",
|
||||
"thorchain_taproot_address_not_supported": "ผู้ให้บริการ Thorchain ไม่รองรับที่อยู่ taproot โปรดเปลี่ยนที่อยู่หรือเลือกผู้ให้บริการอื่น",
|
||||
"time": "${minutes}m ${seconds}s",
|
||||
"tip": "เพิ่มค่าตอบแทน:",
|
||||
"today": "วันนี้",
|
||||
|
@ -660,6 +661,7 @@
|
|||
"totp_code": "รหัสทีโอพี",
|
||||
"totp_secret_code": "รหัสลับ TOTP",
|
||||
"totp_verification_success": "การยืนยันสำเร็จ!",
|
||||
"track": "ติดตาม",
|
||||
"trade_details_copied": "${title} คัดลอกไปยัง Clipboard",
|
||||
"trade_details_created_at": "สร้างเมื่อ",
|
||||
"trade_details_fetching": "กำลังเรียกข้อมูล",
|
||||
|
|
|
@ -643,6 +643,7 @@
|
|||
"template_name": "Pangalan ng Template",
|
||||
"third_intro_content": "Ang mga yats ay nakatira sa labas ng cake wallet, din. Ang anumang address ng pitaka sa mundo ay maaaring mapalitan ng isang yat!",
|
||||
"third_intro_title": "Si Yat ay mahusay na gumaganap sa iba",
|
||||
"thorchain_taproot_address_not_supported": "Ang Tagabigay ng Thorchain ay hindi sumusuporta sa mga address ng taproot. Mangyaring baguhin ang address o pumili ng ibang provider.",
|
||||
"time": "${minutes} m ${seconds} s",
|
||||
"tip": "Tip:",
|
||||
"today": "Ngayon",
|
||||
|
@ -660,6 +661,7 @@
|
|||
"totp_code": "TOTP code",
|
||||
"totp_secret_code": "TOTP Secret Code",
|
||||
"totp_verification_success": "Matagumpay ang pagpapatunay!",
|
||||
"track": "Subaybayan",
|
||||
"trade_details_copied": "${title} kinopya sa clipboard",
|
||||
"trade_details_created_at": "Nilikha sa",
|
||||
"trade_details_fetching": "Pagkuha",
|
||||
|
|
|
@ -643,6 +643,7 @@
|
|||
"template_name": "şablon adı",
|
||||
"third_intro_content": "Yat'lar Cake Wallet'ın dışında da çalışabilir. Dünya üzerindeki herhangi bir cüzdan adresi Yat ile değiştirilebilir!",
|
||||
"third_intro_title": "Yat diğerleriyle iyi çalışır",
|
||||
"thorchain_taproot_address_not_supported": "Thorchain sağlayıcısı Taproot adreslerini desteklemiyor. Lütfen adresi değiştirin veya farklı bir sağlayıcı seçin.",
|
||||
"time": "${minutes}d ${seconds}s",
|
||||
"tip": "Bahşiş:",
|
||||
"today": "Bugün",
|
||||
|
@ -660,6 +661,7 @@
|
|||
"totp_code": "TOTP Kodu",
|
||||
"totp_secret_code": "TOTP Gizli Kodu",
|
||||
"totp_verification_success": "Doğrulama Başarılı!",
|
||||
"track": "İzlemek",
|
||||
"trade_details_copied": "${title} panoya kopyalandı",
|
||||
"trade_details_created_at": "'da oluşturuldu",
|
||||
"trade_details_fetching": "Getiriliyor",
|
||||
|
|
|
@ -644,6 +644,7 @@
|
|||
"template_name": "Назва шаблону",
|
||||
"third_intro_content": "Yat знаходиться за межами Cake Wallet. Будь-яку адресу гаманця на землі можна замінити на Yat!",
|
||||
"third_intro_title": "Yat добре взаємодіє з іншими",
|
||||
"thorchain_taproot_address_not_supported": "Постачальник Thorchain не підтримує адреси Taproot. Будь ласка, змініть адресу або виберіть іншого постачальника.",
|
||||
"time": "${minutes}хв ${seconds}сек",
|
||||
"tip": "Порада:",
|
||||
"today": "Сьогодні",
|
||||
|
@ -661,6 +662,7 @@
|
|||
"totp_code": "Код TOTP",
|
||||
"totp_secret_code": "Секретний код TOTP",
|
||||
"totp_verification_success": "Перевірка успішна!",
|
||||
"track": "Відслідковувати",
|
||||
"trade_details_copied": "${title} скопійовано в буфер обміну",
|
||||
"trade_details_created_at": "Створено",
|
||||
"trade_details_fetching": "Отримання",
|
||||
|
|
|
@ -645,6 +645,7 @@
|
|||
"template_name": "ٹیمپلیٹ کا نام",
|
||||
"third_intro_content": "Yats بھی Cake والیٹ سے باہر رہتے ہیں۔ زمین پر کسی بھی بٹوے کے پتے کو Yat سے تبدیل کیا جا سکتا ہے!",
|
||||
"third_intro_title": "Yat دوسروں کے ساتھ اچھی طرح کھیلتا ہے۔",
|
||||
"thorchain_taproot_address_not_supported": "تھورچین فراہم کنندہ ٹیپروٹ پتے کی حمایت نہیں کرتا ہے۔ براہ کرم پتہ تبدیل کریں یا ایک مختلف فراہم کنندہ کو منتخب کریں۔",
|
||||
"time": "${minutes}m ${seconds}s",
|
||||
"tip": "ٹپ:",
|
||||
"today": "آج",
|
||||
|
@ -662,6 +663,7 @@
|
|||
"totp_code": "TOTP کوڈ",
|
||||
"totp_secret_code": "TOTP خفیہ کوڈ",
|
||||
"totp_verification_success": "توثیق کامیاب!",
|
||||
"track": " ﮏﯾﺮﭨ",
|
||||
"trade_details_copied": "${title} کو کلپ بورڈ پر کاپی کیا گیا۔",
|
||||
"trade_details_created_at": "پر تخلیق کیا گیا۔",
|
||||
"trade_details_fetching": "لا رہا ہے۔",
|
||||
|
|
|
@ -644,6 +644,7 @@
|
|||
"template_name": "Orukọ Awoṣe",
|
||||
"third_intro_content": "A sì lè lo Yats níta Cake Wallet. A lè rọ́pò Àdírẹ́sì kankan àpamọ́wọ́ fún Yat!",
|
||||
"third_intro_title": "Àlàáfíà ni Yat àti àwọn ìmíìn jọ wà",
|
||||
"thorchain_taproot_address_not_supported": "Olupese Trockchain ko ṣe atilẹyin awọn adirẹsi Taproot. Jọwọ yi adirẹsi pada tabi yan olupese ti o yatọ.",
|
||||
"time": "${minutes}ìṣj ${seconds}ìṣs",
|
||||
"tip": "Owó àfikún:",
|
||||
"today": "Lénìí",
|
||||
|
@ -661,6 +662,7 @@
|
|||
"totp_code": "Koodu TOTP",
|
||||
"totp_secret_code": "Koodu iye TOTP",
|
||||
"totp_verification_success": "Ìbẹrẹ dọkita!",
|
||||
"track": "Orin",
|
||||
"trade_details_copied": "Ti ṣeda ${title} sí àtẹ àkọsílẹ̀",
|
||||
"trade_details_created_at": "Ṣíṣe ní",
|
||||
"trade_details_fetching": "Ń mú wá",
|
||||
|
|
|
@ -643,6 +643,7 @@
|
|||
"template_name": "模板名称",
|
||||
"third_intro_content": "Yats 也住在 Cake Wallet 之外。 地球上任何一個錢包地址都可以用一個Yat來代替!",
|
||||
"third_intro_title": "Yat 和別人玩得很好",
|
||||
"thorchain_taproot_address_not_supported": "Thorchain提供商不支持Taproot地址。请更改地址或选择其他提供商。",
|
||||
"time": "${minutes}m ${seconds}s",
|
||||
"tip": "提示:",
|
||||
"today": "今天",
|
||||
|
@ -660,6 +661,7 @@
|
|||
"totp_code": "TOTP代码",
|
||||
"totp_secret_code": "TOTP密码",
|
||||
"totp_verification_success": "验证成功!",
|
||||
"track": "追踪",
|
||||
"trade_details_copied": "${title} 复制到剪贴板",
|
||||
"trade_details_created_at": "创建于",
|
||||
"trade_details_fetching": "正在获取",
|
||||
|
|
Loading…
Reference in a new issue