Cw 1329 implement near intents dex (#2742)

* add Near Intents exchange provider integration

* refactor for improved trade handling

* add explorer link for trades

provider.

* refactor currency parsing in near Intents exchange

* fix: address type for estimating send all transactions in swaps

* add token and app fee config

* Update near_Intents_exchange_provider.dart

* add Near Intents app fee secrets and update usage

* add NEAR intents app fee secrets to workflows

* Update lib/view_model/exchange/exchange_view_model.dart [skip ci]

* making priceUpdatedAt nullable [skip ci]

---------

Co-authored-by: Serhii-Borodenko <17529954+Serhii-Borodenko@users.noreply.github.com>
Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
Serhii
2026-01-09 19:32:56 +01:00
committed by GitHub
co-authored by Serhii-Borodenko Omar Hatem
parent ff88fa8dc8
commit 9b1a62441c
11 changed files with 681 additions and 4 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

+1 -1
View File
@@ -214,7 +214,7 @@ class CWBitcoin extends Bitcoin {
return estimatedTx.amount;
}
final p2shAddr = sk.getPublic().toP2pkhInP2sh();
final p2shAddr = sk.getPublic().toP2pkhAddress();
final estimatedTx = await electrumWallet.estimateSendAllTx(
[BitcoinOutput(address: p2shAddr, value: BigInt.zero)],
getFeeRate(
+3
View File
@@ -1,4 +1,5 @@
import 'dart:async';
import 'package:cake_wallet/exchange/provider/near_Intents_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/swapsxyz_exchange_provider.dart';
import 'package:cake_wallet/exchange/trade.dart';
@@ -65,6 +66,8 @@ class TradeMonitor {
return XOSwapExchangeProvider();
case ExchangeProviderDescription.swapsXyz:
return SwapsXyzExchangeProvider();
case ExchangeProviderDescription.nearIntents:
return NearIntentsExchangeProvider();
}
return null;
}
@@ -38,6 +38,8 @@ class ExchangeProviderDescription extends EnumerableItem<int> with Serializable<
ExchangeProviderDescription(title: 'XOSwap', raw: 13, image: 'assets/images/xoswap.svg');
static const swapsXyz =
ExchangeProviderDescription(title: 'Swaps.XYZ', raw: 14, image: 'assets/images/swaps_xyz.svg');
static const nearIntents =
ExchangeProviderDescription(title: 'Near Intents', raw: 15, image: 'assets/images/near.png');
static ExchangeProviderDescription deserialize({required int raw}) {
switch (raw) {
@@ -71,6 +73,8 @@ class ExchangeProviderDescription extends EnumerableItem<int> with Serializable<
return xoSwap;
case 14:
return swapsXyz;
case 15:
return nearIntents;
default:
throw Exception('Unexpected token: $raw for ExchangeProviderDescription deserialize');
}
@@ -0,0 +1,640 @@
import 'dart:convert';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/core/utilities.dart';
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_not_created_exception.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/amount_converter.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cake_wallet/utils/exchange_provider_logger.dart';
class NearIntentsExchangeProvider extends ExchangeProvider {
NearIntentsExchangeProvider()
: super(pairList: supportedPairs(_notSupported));
static const List<CryptoCurrency> _notSupported = [];
static const apiKey = secrets.nearIntentsBearerToken;
static const _baseUrl = '1click.chaindefuser.com';
static const _versionPath = '/v0';
static const _tokenPath = '/tokens';
static const _quotePath = '/quote';
static const _statusPath = '/status';
static const _slippageTolerance = 100; // 1%
static const _appFeesNearIntents = secrets.nearIntentsAppFee;
static const _appFeeRecipientNearIntents = secrets.nearIntentsAppFeeRecipient;
static const _memoRequiredCurrencies = <CryptoCurrency>[
CryptoCurrency.xrp,
CryptoCurrency.xlm,
CryptoCurrency.ton,
];
/// Use these only for quote/rate testing (dummy data).
static const Map<String, String> kNearDummyAddresses = {
// UTXO
'LTC': 'ltc1qhdwz74m3wuuhppv2mckagqk9e2e49z5j4kucnv',
'BTC': 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080',
'ZZEC': 't1Zcash4hPS2bq8rTQkN9bKpZV1X4W3pZxK',
'DOGE': 'D9t7rGQ9mE3hJ2z1w8pGQxkGmKjYwYc8pQ',
'BCH': 'qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a',
// EVM (same address works for all EVM chains)
'ETH': '0x1111111111111111111111111111111111111111',
'BSC': '0x1111111111111111111111111111111111111111',
'POL': '0x1111111111111111111111111111111111111111',
'AVAXC': '0x1111111111111111111111111111111111111111',
'ARB': '0x1111111111111111111111111111111111111111',
'BASE': '0x1111111111111111111111111111111111111111',
// Others
'SOL': '11111111111111111111111111111111',
'XRP': 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe',
'TRX': 'T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb',
'TON': 'UQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c',
'XLM': 'GA3D5O2W7YQZJQ3H4Y5QW6N7V8X9Z0A1B2C3D4E5F6G7H8I9J0',
'ADA': 'addr1vyc33hdv5vag52f3d8h0qsngu52vm27x28zkzru333jma9gaxd38v',
};
String getNearDummyAddress(CryptoCurrency currency) {
final tag = (currency.tag ?? '').trim().toUpperCase();
final title = currency.title.toUpperCase();
final key = tag.isEmpty ? title : tag;
return kNearDummyAddresses[key] ?? '';
}
static final Map<String, String> _headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': '$apiKey',
};
static final _supportedTokensList = <Token>[];
@override
String get title => 'Near Intents';
@override
bool get isAvailable => true;
@override
bool get isEnabled => true;
@override
bool get supportsFixedRate => true;
@override
ExchangeProviderDescription get description =>
ExchangeProviderDescription.nearIntents;
@override
Future<bool> checkIsAvailable() async => true;
@override
Future<Limits> fetchLimits(
{required CryptoCurrency from,
required CryptoCurrency to,
required bool isFixedRateMode}) async {
final tokens = await _geSupportedTokens();
final originToken = currencyToNearAssetId(from, tokens);
final destinationToken = currencyToNearAssetId(to, tokens);
if (originToken == null || destinationToken == null) {
throw Exception(
'fetchLimits: unsupported currency pair: ${from.title} ${from.tag ?? ''} to ${to.title} ${to.tag ?? ''}');
}
return Limits(min: null, max: null);
}
@override
Future<double> fetchRate({
required CryptoCurrency from,
required CryptoCurrency to,
required double amount,
required bool isFixedRateMode,
required bool isReceiveAmount,
}) async {
final tokens = await _geSupportedTokens();
final originToken = currencyToNearAssetId(from, tokens);
final destinationToken = currencyToNearAssetId(to, tokens);
try {
if (originToken == null || destinationToken == null) {
throw Exception('fetchRate: Unsupported currency pair');
}
final formattedAmount = AmountConverter.toBaseUnits(amount.toString(),
isFixedRateMode ? destinationToken.decimals : originToken.decimals);
final dummyAddrFrom = getNearDummyAddress(from);
final dummyAddrTo = getNearDummyAddress(to);
final depositMode = _memoRequiredCurrencies.contains(from) ? "MEMO" : "SIMPLE";
final quote = await getSwapQuote(
dry: true,
isFixedRateMode: isFixedRateMode,
originAsset: originToken.assetId,
destinationAsset: destinationToken.assetId,
amount: formattedAmount,
depositMode: depositMode,
refundTo: dummyAddrFrom,
recipient: dummyAddrTo,
);
if (quote == null) {
throw Exception('fetchRate: Quote returned null');
}
final q = quote['quote'] as Map<String, dynamic>;
final amountIn =
double.tryParse(q['amountInFormatted']?.toString() ?? '0') ?? 0.0;
final amountOut =
double.tryParse(q['amountOutFormatted']?.toString() ?? '0') ?? 0.0;
if (amountIn == 0) return 0.0;
final rate = amountOut / amountIn;
ExchangeProviderLogger.logSuccess(
provider: description,
function: 'fetchRate',
requestData: {
'from': from.title,
'to': to.title,
'amount': amount,
'formattedAmount': formattedAmount,
'isFixedRateMode': isFixedRateMode,
'isReceiveAmount': isReceiveAmount,
'originAsset': originToken.assetId,
'destinationAsset': destinationToken.assetId,
},
responseData: {
'amountIn': amountIn,
'amountOut': amountOut,
'rate': rate,
},
);
return rate;
} catch (e, s) {
ExchangeProviderLogger.logError(
provider: description,
function: 'fetchRate',
error: e,
stackTrace: s,
requestData: {
'from': from.title,
'to': to.title,
'amount': amount,
'isFixedRateMode': isFixedRateMode,
'isReceiveAmount': isReceiveAmount,
},
);
printV(e.toString());
return 0.0;
}
}
@override
Future<Trade> createTrade({
required TradeRequest request,
required bool isFixedRateMode,
required bool isSendAll,
}) async {
try {
final tokens = await _geSupportedTokens();
final originToken = currencyToNearAssetId(request.fromCurrency, tokens);
final destinationToken =
currencyToNearAssetId(request.toCurrency, tokens);
if (originToken == null || destinationToken == null) {
throw Exception('Unsupported currency pair');
}
final rawAmountStr =
isFixedRateMode ? request.toAmount : request.fromAmount;
final baseAmount = AmountConverter.toBaseUnits(
rawAmountStr,
isFixedRateMode
? request.toCurrency.decimals
: request.fromCurrency.decimals,
);
final depositMode = _memoRequiredCurrencies.contains(request.fromCurrency) ? "MEMO" : "SIMPLE";
final quote = await getSwapQuote(
dry: false,
isFixedRateMode: isFixedRateMode,
originAsset: originToken.assetId,
destinationAsset: destinationToken.assetId,
depositMode: depositMode,
amount: baseAmount,
refundTo: request.refundAddress,
recipient: request.toAddress,
);
if (quote == null) {
throw Exception('Quote request failed');
}
final quoteObj = quote['quote'] as Map<String, dynamic>;
final depositAddress = quoteObj['depositAddress'] as String;
final depositMemo = quoteObj['depositMemo'] as String?;
final quoteRequest = quote['quoteRequest'] as Map<String, dynamic>;
final fromAssetId = quoteRequest['originAsset'] as String;
final toAssetId = quoteRequest['destinationAsset'] as String;
final fromCurrency = _nearAssetIdToCurrency(fromAssetId, tokens);
if (fromCurrency == null) {
throw Exception('Failed to parse from currency from assetId: $fromAssetId');
}
final toCurrency = _nearAssetIdToCurrency(toAssetId, tokens);
if (toCurrency == null) {
throw Exception('Failed to parse to currency from assetId: $toAssetId');
}
final from = CryptoCurrency.safeParseCurrencyFromString(fromCurrency.$1,
tag: fromCurrency.$2);
final to = CryptoCurrency.safeParseCurrencyFromString(toCurrency.$1,
tag: toCurrency.$2);
final trade = Trade(
id: depositAddress,
// Using deposit address as trade ID
from: from,
to: to,
provider: description,
providerName: title,
state: TradeState.created,
createdAt: DateTime.now(),
inputAddress: depositAddress,
payoutAddress: request.toAddress,
refundAddress: request.refundAddress,
amount: request.fromAmount,
receiveAmount: quoteObj['amountOutFormatted']?.toString(),
memo: depositMemo,
isSendAll: isSendAll,
userCurrencyFromRaw:
'${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
userCurrencyToRaw:
'${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
);
ExchangeProviderLogger.logSuccess(
provider: description,
function: 'createTrade',
requestData: {
'from': request.fromCurrency.title,
'to': request.toCurrency.title,
'fromAmount': request.fromAmount,
'toAmount': request.toAmount,
'refundAddress': request.refundAddress,
'recipient': request.toAddress,
'isFixedRateMode': isFixedRateMode,
'isSendAll': isSendAll,
'originAsset': originToken.assetId,
'destinationAsset': destinationToken.assetId,
},
responseData: {
'correlationId': quote['correlationId'],
'depositAddress': depositAddress,
'depositMemo': depositMemo,
'quote': quoteObj,
},
);
return trade;
} catch (e, s) {
ExchangeProviderLogger.logError(
provider: description,
function: 'createTrade',
error: e,
stackTrace: s,
requestData: {
'from': request.fromCurrency.title,
'to': request.toCurrency.title,
'fromAmount': request.fromAmount,
'toAmount': request.toAmount,
'refundAddress': request.refundAddress,
'recipient': request.toAddress,
'isFixedRateMode': isFixedRateMode,
'isSendAll': isSendAll,
},
);
throw TradeNotCreatedException(description, description: e.toString());
}
}
@override
Future<Trade> findTradeById({required String id}) async {
final param = {'depositAddress': id};
final uri = Uri.https(_baseUrl, '$_versionPath$_statusPath', param);
final response =
await ProxyWrapper().get(clearnetUri: uri, headers: _headers);
if (response.statusCode != 200) {
throw Exception(
'Near Intents fetch trade failed: ${response.statusCode} ${response.body}',
);
}
final data = jsonDecode(response.body) as Map<String, dynamic>;
final statusRaw = (data['status'] as String?) ?? 'UNKNOWN';
final quoteResponse = data['quoteResponse'] as Map<String, dynamic>? ?? {};
final quoteRequest =
quoteResponse['quoteRequest'] as Map<String, dynamic>? ?? {};
final refundTo = quoteRequest['refundTo'] as String? ?? '';
final recipient = quoteRequest['recipient'] as String? ?? '';
// Parsing 'from' currency
final originAssetId = quoteRequest['originAsset'] as String? ?? '';
final from =
_nearAssetIdToCurrency(originAssetId, await _geSupportedTokens());
CryptoCurrency? coinFrom;
CryptoCurrency? coinTo;
if (from != null) {
coinFrom = CryptoCurrency.safeParseCurrencyFromString(from.$1, tag: from.$2);
}
// Parsing 'to' currency
final destinationAssetId =
quoteRequest['destinationAsset'] as String? ?? '';
final to =
_nearAssetIdToCurrency(destinationAssetId, await _geSupportedTokens());
if (to != null) {
coinTo = CryptoCurrency.safeParseCurrencyFromString(to.$1, tag: to.$2);
}
final quote = quoteResponse['quote'] as Map<String, dynamic>? ?? {};
final swap = data['swapDetails'] as Map<String, dynamic>? ?? {};
final depositAddress = quote['depositAddress'] as String?;
final depositMemo = quote['depositMemo'] as String?;
final depositAmount = swap['amountInFormatted']?.toString() ??
quote['amountInFormatted']?.toString() ??
'0';
final receiveAmount = swap['amountOutFormatted']?.toString() ??
quote['amountOutFormatted']?.toString();
final originTxHash = (swap['originChainTxHashes'] as List?)
?.firstOrNull?['hash']
?.toString();
return Trade(
id: id,
from: coinFrom,
to: coinTo,
provider: description,
inputAddress: depositAddress,
payoutAddress: recipient,
refundAddress: refundTo,
amount: depositAmount,
receiveAmount: receiveAmount,
state: _normalizeStatusToTradeState(statusRaw),
txId: originTxHash,
extraId: depositMemo,
isRefund: statusRaw == 'REFUNDED',
userCurrencyFromRaw: '${from?.$1.toUpperCase()}' + '_' + '${from?.$2?.toUpperCase() ?? ''}',
userCurrencyToRaw: '${to?.$1.toUpperCase()}' + '_' + '${to?.$2?.toUpperCase() ?? ''}',
);
}
// Load & cache supported tokens
Future<List<Token>> _geSupportedTokens() async {
if (_supportedTokensList.isNotEmpty) return _supportedTokensList;
try {
final uri = Uri.https(_baseUrl, '$_versionPath$_tokenPath');
final response =
await ProxyWrapper().get(clearnetUri: uri, headers: _headers);
if (response.statusCode != 200) return [];
final data = json.decode(response.body) as List<dynamic>;
_supportedTokensList
..clear()
..addAll(data.map((e) => Token.fromJson(e as Map<String, dynamic>)));
return _supportedTokensList;
} catch (e) {
printV(e);
return [];
}
}
Future<Map<String, dynamic>?> getSwapQuote({
required bool dry,
required bool isFixedRateMode,
required String originAsset,
required String destinationAsset,
required String amount,
required String refundTo,
required String recipient,
required String depositMode,
List<String>? connectedWallets,
String? sessionId,
String? virtualChainRecipient,
String? virtualChainRefundRecipient,
String? customRecipientMsg,
String? deadline,
String? referral,
int? quoteWaitingTimeMs,
}) async {
final swapType = isFixedRateMode ? 'EXACT_OUTPUT' : 'EXACT_INPUT';
final _isoUtcDeadline = _buildDeadline();
final appFees = [
{
"recipient": _appFeeRecipientNearIntents,
"fee": _appFeesNearIntents,
}
];
final uri = Uri.https(_baseUrl, "$_versionPath$_quotePath");
final payload = {
"dry": dry,
"depositMode": depositMode,
"swapType": swapType,
"slippageTolerance": _slippageTolerance,
"originAsset": originAsset,
"depositType": 'ORIGIN_CHAIN',
"destinationAsset": destinationAsset,
"amount": amount,
"refundTo": refundTo,
"refundType": 'ORIGIN_CHAIN',
"recipient": recipient,
"recipientType": 'DESTINATION_CHAIN',
"deadline": _isoUtcDeadline,
if (connectedWallets != null) "connectedWallets": connectedWallets,
if (sessionId != null) "sessionId": sessionId,
if (virtualChainRecipient != null)
"virtualChainRecipient": virtualChainRecipient,
if (virtualChainRefundRecipient != null)
"virtualChainRefundRecipient": virtualChainRefundRecipient,
if (customRecipientMsg != null) "customRecipientMsg": customRecipientMsg,
if (deadline != null) "deadline": deadline,
if (referral != null) "referral": referral,
if (quoteWaitingTimeMs != null) "quoteWaitingTimeMs": quoteWaitingTimeMs,
"appFees": appFees,
};
try {
final response = await ProxyWrapper().post(
clearnetUri: uri,
headers: _headers,
body: jsonEncode(payload),
);
if (response.statusCode != 201) {
printV("Quote request failed with status: ${response.statusCode}");
return null;
}
return jsonDecode(response.body) as Map<String, dynamic>;
} catch (e) {
printV("Quote error: $e");
return null;
}
}
TradeState _normalizeStatusToTradeState(String status) {
return switch (status.toUpperCase()) {
'PENDING_DEPOSIT' => TradeState.pending,
'PROCESSING' => TradeState.processing,
'SUCCESS' => TradeState.success,
'INCOMPLETE_DEPOSIT' => TradeState.underpaid,
'REFUNDED' => TradeState.refunded,
'FAILED' => TradeState.failed,
_ => TradeState.notFound,
};
}
String _normalizeTitleToNearSymbol(CryptoCurrency currency) {
final title = currency.title.toUpperCase();
return switch (title) {
'TZEC' => 'ZEC',
_ => title,
};
}
String? _normalizeTagToNearBlockchain(String? tag) {
return switch (tag) {
'TRX' => 'tron',
'AVAXC' => 'avax',
_ => tag?.toLowerCase(),
};
}
String? _normalizeNearBlockchainToTag(String? blockchain) {
return switch (blockchain) {
'tron' => 'TRX',
'avax' => 'AVAXC',
_ => blockchain?.toUpperCase(),
};
}
Token? currencyToNearAssetId(CryptoCurrency currency, List<Token> supported) {
if (supported.isEmpty) return null;
final symbol = _normalizeTitleToNearSymbol(currency).toUpperCase();
final blockchain = _normalizeTagToNearBlockchain(currency.tag);
// Native asset (no contract)
final native = supported.firstWhereOrNull((t) =>
t.symbol.toUpperCase() == symbol &&
(blockchain == null || t.blockchain.toLowerCase() == blockchain) &&
t.contractAddress == null);
if (native != null) {
return native;
}
final token = supported.firstWhereOrNull((t) =>
t.symbol.toUpperCase() == symbol &&
(blockchain == null || t.blockchain.toLowerCase() == blockchain));
return token;
}
(String, String?)? _nearAssetIdToCurrency(
String assetId, List<Token> supported) {
if (supported.isEmpty) return null;
final token = supported.firstWhereOrNull((t) => t.assetId == assetId);
if (token == null) return null;
final title = token.symbol;
final normalizedNetwork = _normalizeNearBlockchainToTag(token.blockchain);
final tag =
normalizedNetwork == title.toUpperCase() ? null : normalizedNetwork;
return (title, tag);
}
String _buildDeadline() {
return DateTime.now()
.toUtc()
.add(const Duration(hours: 2))
.toIso8601String()
.replaceFirst(RegExp(r'\.\d+Z$'), 'Z');
}
}
class Token {
final String assetId;
final int decimals;
final String blockchain;
final String symbol;
final double priceUsd;
final String? priceUpdatedAt;
final String? contractAddress;
Token({
required this.assetId,
required this.decimals,
required this.blockchain,
required this.symbol,
required this.priceUsd,
required this.priceUpdatedAt,
required this.contractAddress,
});
factory Token.fromJson(Map<String, dynamic> json) {
final decimals = json['decimals'] as int?;
if (decimals == null) {
throw Exception('Token decimals is null for assetId: ${json['assetId']}');
}
return Token(
assetId: json['assetId'] as String,
decimals: json['decimals'] as int,
blockchain: json['blockchain'] as String,
symbol: json['symbol'] as String,
priceUsd: (json['price'] as num?)?.toDouble() ?? 0.0,
priceUpdatedAt: json['priceUpdatedAt'] as String?,
contractAddress: json['contractAddress'] as String?,
);
}
}
+13 -3
View File
@@ -22,7 +22,8 @@ abstract class TradeFilterStoreBase with Store {
displayStealthEx = true,
displayXOSwap = true,
displaySwapTrade = true,
displaySwapXyz = true;
displaySwapXyz = true,
displayNearIntents = true;
@observable
bool displayXMRTO;
@@ -65,6 +66,8 @@ abstract class TradeFilterStoreBase with Store {
@observable
bool displaySwapXyz;
@observable
bool displayNearIntents;
@computed
bool get displayAllTrades =>
@@ -79,7 +82,8 @@ abstract class TradeFilterStoreBase with Store {
displayStealthEx &&
displayXOSwap &&
displaySwapTrade &&
displaySwapXyz;
displaySwapXyz &&
displayNearIntents;
@action
void toggleDisplayExchange(ExchangeProviderDescription provider) {
@@ -126,6 +130,9 @@ abstract class TradeFilterStoreBase with Store {
case ExchangeProviderDescription.swapsXyz:
displaySwapXyz = !displaySwapXyz;
break;
case ExchangeProviderDescription.nearIntents:
displayNearIntents = !displayNearIntents;
break;
case ExchangeProviderDescription.all:
if (displayAllTrades) {
displayChangeNow = false;
@@ -142,6 +149,7 @@ abstract class TradeFilterStoreBase with Store {
displayXOSwap = false;
displaySwapTrade = false;
displaySwapXyz = false;
displayNearIntents = false;
} else {
displayChangeNow = true;
displaySideShift = true;
@@ -157,6 +165,7 @@ abstract class TradeFilterStoreBase with Store {
displayXOSwap = true;
displaySwapTrade = true;
displaySwapXyz = true;
displayNearIntents = true;
}
break;
}
@@ -192,7 +201,8 @@ abstract class TradeFilterStoreBase with Store {
(displayXOSwap && item.trade.provider == ExchangeProviderDescription.xoSwap) ||
(displaySwapTrade && item.trade.provider == ExchangeProviderDescription.swapTrade) ||
(displaySwapXyz &&
item.trade.provider == ExchangeProviderDescription.swapsXyz))
item.trade.provider == ExchangeProviderDescription.swapsXyz) ||
(displayNearIntents && item.trade.provider == ExchangeProviderDescription.nearIntents))
.toList()
: _trades;
}
@@ -187,6 +187,11 @@ abstract class DashboardViewModelBase with Store {
caption: ExchangeProviderDescription.swapsXyz.title,
onChanged: () =>
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.swapsXyz)),
FilterItem(
value: () => tradeFilterStore.displayNearIntents,
caption: ExchangeProviderDescription.nearIntents.title,
onChanged: () =>
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.nearIntents)),
]
},
subname = '',
@@ -8,6 +8,7 @@ import 'package:cake_wallet/exchange/provider/chainflip_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/changenow_exchange_provider.dart';
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/near_Intents_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/swapsxyz_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/swaptrade_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
@@ -86,6 +87,9 @@ abstract class ExchangeTradeViewModelBase with Store {
case ExchangeProviderDescription.swapsXyz:
_provider = SwapsXyzExchangeProvider();
break;
case ExchangeProviderDescription.nearIntents:
_provider = NearIntentsExchangeProvider();
break;
}
_updateItems();
@@ -25,6 +25,7 @@ import 'package:cake_wallet/exchange/provider/letsexchange_exchange_provider.dar
import 'package:cake_wallet/exchange/provider/changenow_exchange_provider.dart';
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/near_Intents_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/stealth_ex_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/swapsxyz_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/swaptrade_exchange_provider.dart';
@@ -218,6 +219,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
StealthExExchangeProvider(),
XOSwapExchangeProvider(),
// SwapsXyzExchangeProvider(),
NearIntentsExchangeProvider(),
TrocadorExchangeProvider(
useTorOnly: _useTorOnly, providerStates: _settingsStore.trocadorProviderStates),
];
@@ -6,6 +6,7 @@ import 'package:cake_wallet/exchange/provider/changenow_exchange_provider.dart';
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/letsexchange_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/near_Intents_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/swapsxyz_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/swaptrade_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
@@ -81,6 +82,9 @@ abstract class TradeDetailsViewModelBase with Store {
case ExchangeProviderDescription.swapsXyz:
_provider = SwapsXyzExchangeProvider();
break;
case ExchangeProviderDescription.nearIntents:
_provider = NearIntentsExchangeProvider();
break;
}
_updateItems();
@@ -115,6 +119,8 @@ abstract class TradeDetailsViewModelBase with Store {
return 'https://scan.chainflip.io/channels/${trade.id}';
case ExchangeProviderDescription.xoSwap:
return 'https://orders.xoswap.com/${trade.id}';
case ExchangeProviderDescription.nearIntents:
return 'https://explorer.near-intents.org/transactions/${trade.id}';
}
return null;
}
+3
View File
@@ -85,6 +85,9 @@ class SecretKey {
SecretKey('kryptonimApiKey', () => ''),
SecretKey('walletGroupSalt', () => hex.encode(encrypt.Key.fromSecureRandom(16).bytes)),
SecretKey('swapsXyzApiKey', () => ''),
SecretKey('nearIntentsBearerToken', () => ''),
SecretKey('nearIntentsAppFee', () => ''),
SecretKey('nearIntentsAppFeeRecipient', () => ''),
];
static final evmChainsSecrets = [