clean up remaining letsexchange integration items
What changed, and why it matters
This commit finishes adding support for a new cryptocurrency exchange partner called LetsExchange to the Stack Wallet app. It adds the exchange's logo, wires up its name in currency lookup and trade-detail screens, adds its API credentials placeholders to build scripts, removes its response bodies from debug logs, and excludes it from a Trocador aggregator filter. There is no security fix or vulnerability visible in the changes.
No security action required. As a routine hygiene measure, verify that the new kLetsExchangeId and kLetsExchangeToken placeholders are populated only via secure means and not committed to source control.
Security signals we found
Debug logging of HTTP response bodies removed for LetsExchange API calls, reducing accidental sensitive-data exposure in logs
No security-relevant code changes otherwise observed
Evidence from the diff
The diff is a feature/integration cleanup for LetsExchange. Changes include: adding SVG exchange icons for three app variants; importing LetsExchangeExchange in currency.dart, trade_details_view.dart, and assets.dart; mapping LetsExchange network names to lower-case in Currency.networkFor(); adding a transaction-status URL in trade_details_view.dart; adding kLetsExchangeId and kLetsExchangeToken template constants in prebuild scripts; removing HTTP response bodies from LetsExchangeApi GET/POST debug logs (leaving only method, URI, and status code); and excluding the ‘letsexchange’ provider from TrocadorExchange’s A/B KYC rating filter. No cryptographic, authentication, or input-validation changes are present.
Changed components
asset_sources/svg/*/exchange_icons/letsexchange.svglib/models/isar/exchange_cache/currency.dartlib/pages/exchange_view/trade_details_view.dartlib/services/exchange/lets_exchange/lets_exchange_api.dartlib/services/exchange/trocador/trocador_exchange.dartlib/utilities/assets.dartscripts/prebuild.ps1scripts/prebuild.shInspect captured patch +20 / −4
diff --git a/asset_sources/svg/campfire/exchange_icons/letsexchange.svg b/asset_sources/svg/campfire/exchange_icons/letsexchange.svg
new file mode 100644
index 0000000..b4438fb
--- /dev/null
+++ b/asset_sources/svg/campfire/exchange_icons/letsexchange.svg
@@ -0,0 +1 @@
+<svg fill="none" height="40" viewBox="0 0 40 40" width="40" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="m20 1.72316c0-.767825.8295-1.249193 1.4961-.868246l7.2539 4.145066 8.1202 4.6401c.3894.22255.6298.63672.6298 1.08532v9.2746l-7.5097-4.2913c-.7685-.4391-1.7121-.4391-2.4806 0l-7.5097 4.2913v-10.00002zm-17.5 18.27684v-9.2746c0-.4486.24036-.86277.62983-1.08532l6.87987-3.93133c.7685-.43919 1.7121-.43919 2.4806 0l7.5097 4.29123zm17.5 18.2768c0 .7678-.8295 1.2492-1.4961.8682l-7.2539-4.145-8.12017-4.6401c-.38947-.2226-.62983-.6368-.62983-1.0853v-9.2746l7.5097 4.2912c.7685.4392 1.7121.4392 2.4806 0l7.5097-4.2912v10zm7.5097-3.9856c.7685.4392 1.7121.4392 2.4806 0l6.8799-3.9313c.3894-.2226.6298-.6368.6298-1.0853v-9.2746l-17.5 10z" fill="#159dff" fill-rule="evenodd"/></svg>
\ No newline at end of file
diff --git a/asset_sources/svg/stack_duo/exchange_icons/letsexchange.svg b/asset_sources/svg/stack_duo/exchange_icons/letsexchange.svg
new file mode 100644
index 0000000..b4438fb
--- /dev/null
+++ b/asset_sources/svg/stack_duo/exchange_icons/letsexchange.svg
@@ -0,0 +1 @@
+<svg fill="none" height="40" viewBox="0 0 40 40" width="40" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="m20 1.72316c0-.767825.8295-1.249193 1.4961-.868246l7.2539 4.145066 8.1202 4.6401c.3894.22255.6298.63672.6298 1.08532v9.2746l-7.5097-4.2913c-.7685-.4391-1.7121-.4391-2.4806 0l-7.5097 4.2913v-10.00002zm-17.5 18.27684v-9.2746c0-.4486.24036-.86277.62983-1.08532l6.87987-3.93133c.7685-.43919 1.7121-.43919 2.4806 0l7.5097 4.29123zm17.5 18.2768c0 .7678-.8295 1.2492-1.4961.8682l-7.2539-4.145-8.12017-4.6401c-.38947-.2226-.62983-.6368-.62983-1.0853v-9.2746l7.5097 4.2912c.7685.4392 1.7121.4392 2.4806 0l7.5097-4.2912v10zm7.5097-3.9856c.7685.4392 1.7121.4392 2.4806 0l6.8799-3.9313c.3894-.2226.6298-.6368.6298-1.0853v-9.2746l-17.5 10z" fill="#159dff" fill-rule="evenodd"/></svg>
\ No newline at end of file
diff --git a/asset_sources/svg/stack_wallet/exchange_icons/letsexchange.svg b/asset_sources/svg/stack_wallet/exchange_icons/letsexchange.svg
new file mode 100644
index 0000000..b4438fb
--- /dev/null
+++ b/asset_sources/svg/stack_wallet/exchange_icons/letsexchange.svg
@@ -0,0 +1 @@
+<svg fill="none" height="40" viewBox="0 0 40 40" width="40" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="m20 1.72316c0-.767825.8295-1.249193 1.4961-.868246l7.2539 4.145066 8.1202 4.6401c.3894.22255.6298.63672.6298 1.08532v9.2746l-7.5097-4.2913c-.7685-.4391-1.7121-.4391-2.4806 0l-7.5097 4.2913v-10.00002zm-17.5 18.27684v-9.2746c0-.4486.24036-.86277.62983-1.08532l6.87987-3.93133c.7685-.43919 1.7121-.43919 2.4806 0l7.5097 4.29123zm17.5 18.2768c0 .7678-.8295 1.2492-1.4961.8682l-7.2539-4.145-8.12017-4.6401c-.38947-.2226-.62983-.6368-.62983-1.0853v-9.2746l7.5097 4.2912c.7685.4392 1.7121.4392 2.4806 0l7.5097-4.2912v10zm7.5097-3.9856c.7685.4392 1.7121.4392 2.4806 0l6.8799-3.9313c.3894-.2226.6298-.6368.6298-1.0853v-9.2746l-17.5 10z" fill="#159dff" fill-rule="evenodd"/></svg>
\ No newline at end of file
diff --git a/lib/models/isar/exchange_cache/currency.dart b/lib/models/isar/exchange_cache/currency.dart
index a5769e7..9036385 100644
--- a/lib/models/isar/exchange_cache/currency.dart
+++ b/lib/models/isar/exchange_cache/currency.dart
@@ -14,6 +14,7 @@ import '../../../app_config.dart';
import '../../../services/exchange/change_now/change_now_exchange.dart';
import '../../../services/exchange/exchange.dart';
import '../../../services/exchange/exolix/exolix_exchange.dart';
+import '../../../services/exchange/lets_exchange/lets_exchange_exchange.dart';
import '../../../services/exchange/nanswap/nanswap_exchange.dart';
import '../../../services/exchange/trocador/trocador_exchange.dart';
import '../../../services/exchange/wizard_swap/wizard_swap_exchange.dart';
@@ -101,6 +102,8 @@ class Currency {
// wizard swap's api sucks
const (WizardSwapExchange) => ticker.toLowerCase(),
+ const (LetsExchangeExchange) => network.toLowerCase(),
+
_ => throw Exception("Unknown exchange: $exchangeName"),
};
}
diff --git a/lib/pages/exchange_view/trade_details_view.dart b/lib/pages/exchange_view/trade_details_view.dart
index a8c201c..b8aef6d 100644
--- a/lib/pages/exchange_view/trade_details_view.dart
+++ b/lib/pages/exchange_view/trade_details_view.dart
@@ -30,6 +30,7 @@ import '../../route_generator.dart';
import '../../services/exchange/change_now/change_now_exchange.dart';
import '../../services/exchange/exchange.dart';
import '../../services/exchange/exolix/exolix_exchange.dart';
+import '../../services/exchange/lets_exchange/lets_exchange_exchange.dart';
import '../../services/exchange/nanswap/nanswap_exchange.dart';
import '../../services/exchange/simpleswap/simpleswap_exchange.dart';
import '../../services/exchange/trocador/trocador_exchange.dart';
@@ -1174,6 +1175,9 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
url =
"https://exolix.com/transaction/${trade.tradeId}";
break;
+ case LetsExchangeExchange.exchangeName:
+ url = "https://letsexchange.io/transaction-status";
+ break;
default:
if (trade.exchangeName.startsWith(
diff --git a/lib/services/exchange/lets_exchange/lets_exchange_api.dart b/lib/services/exchange/lets_exchange/lets_exchange_api.dart
index c2499dd..0a3ed23 100644
--- a/lib/services/exchange/lets_exchange/lets_exchange_api.dart
+++ b/lib/services/exchange/lets_exchange/lets_exchange_api.dart
@@ -85,7 +85,7 @@ abstract final class LetsExchangeApi {
proxyInfo: _resolveProxyInfo(),
);
- Logging.instance.t("GET $uri: ${response.code}: ${response.body}");
+ Logging.instance.t("LetsExchangeApi GET $uri: ${response.code}");
return _decode(response.code, response.body, parse);
}
@@ -102,7 +102,7 @@ abstract final class LetsExchangeApi {
proxyInfo: _resolveProxyInfo(),
);
- Logging.instance.t("POST $uri: ${response.code}: ${response.body}");
+ Logging.instance.t("LetsExchangeApi POST $uri: ${response.code}");
return _decode(response.code, response.body, parse);
}
diff --git a/lib/services/exchange/trocador/trocador_exchange.dart b/lib/services/exchange/trocador/trocador_exchange.dart
index b409387..800f921 100644
--- a/lib/services/exchange/trocador/trocador_exchange.dart
+++ b/lib/services/exchange/trocador/trocador_exchange.dart
@@ -249,6 +249,7 @@ class TrocadorExchange extends Exchange {
final provider = quote.provider.toLowerCase();
if (quote.fixed == fixedRate &&
provider != "changenow" &&
+ provider != "letsexchange" &&
provider != "exolix") {
final rating = quote.kycRating.toLowerCase();
if (rating == "a" || rating == "b") {
diff --git a/lib/utilities/assets.dart b/lib/utilities/assets.dart
index 03fd2ce..cd662ca 100644
--- a/lib/utilities/assets.dart
+++ b/lib/utilities/assets.dart
@@ -12,6 +12,7 @@ import 'package:flutter/material.dart';
import '../services/exchange/change_now/change_now_exchange.dart';
import '../services/exchange/exolix/exolix_exchange.dart';
+import '../services/exchange/lets_exchange/lets_exchange_exchange.dart';
import '../services/exchange/nanswap/nanswap_exchange.dart';
import '../services/exchange/simpleswap/simpleswap_exchange.dart';
import '../services/exchange/trocador/trocador_exchange.dart';
@@ -53,6 +54,8 @@ class _EXCHANGE {
String get exolix => "${_path}exolix.png";
+ String get letsexchange => "${_path}letsexchange.svg";
+
String getIconFor({required String exchangeName}) {
switch (exchangeName) {
case SimpleSwapExchange.exchangeName:
@@ -69,6 +72,8 @@ class _EXCHANGE {
return wizard;
case ExolixExchange.exchangeName:
return exolix;
+ case LetsExchangeExchange.exchangeName:
+ return letsexchange;
default:
throw ArgumentError(
"Invalid exchange name passed to "
diff --git a/scripts/prebuild.ps1 b/scripts/prebuild.ps1
index b9ff36a..1005ceb 100644
--- a/scripts/prebuild.ps1
+++ b/scripts/prebuild.ps1
@@ -2,7 +2,7 @@
$KEYS = "..\lib\external_api_keys.dart"
if (-not (Test-Path $KEYS)) {
Write-Host "prebuild.ps1: creating template lib/external_api_keys.dart file"
- "const kChangeNowApiKey = '';" + "`nconst kSimpleSwapApiKey = '';" + "`nconst kNanswapApiKey = '';" + "`nconst kNanoSwapRpcApiKey = '';" + "`nconst kWizSwapApiKey = '';" + "`nconst kShopInBitAccessKey = '';" + "`nconst kShopInBitPartnerSecret = '';" + "`nconst kCakePayApiToken = '';" + "`nconst kExolixApiKey = '';" | Out-File $KEYS -Encoding UTF8
+ "const kChangeNowApiKey = '';" + "`nconst kSimpleSwapApiKey = '';" + "`nconst kNanswapApiKey = '';" + "`nconst kNanoSwapRpcApiKey = '';" + "`nconst kWizSwapApiKey = '';" + "`nconst kShopInBitAccessKey = '';" + "`nconst kShopInBitPartnerSecret = '';" + "`nconst kCakePayApiToken = '';" + "`nconst kExolixApiKey = '';" + "`nconst kLetsExchangeId = '';" + "`nconst kLetsExchangeToken = '';" | Out-File $KEYS -Encoding UTF8
}
# Create template wallet test parameter files if they don't already exist
diff --git a/scripts/prebuild.sh b/scripts/prebuild.sh
index 6aeaca6..b081bce 100755
--- a/scripts/prebuild.sh
+++ b/scripts/prebuild.sh
@@ -4,7 +4,7 @@
KEYS=../lib/external_api_keys.dart
if ! test -f "$KEYS"; then
echo 'prebuild.sh: creating template lib/external_api_keys.dart file'
- printf 'const kChangeNowApiKey = "";\nconst kSimpleSwapApiKey = "";\nconst kNanswapApiKey = "";\nconst kNanoSwapRpcApiKey = "";\nconst kWizSwapApiKey = "";\nconst kShopInBitAccessKey = "";\nconst kShopInBitPartnerSecret = "";\nconst kCakePayApiToken = "";\nconst kExolixApiKey = "";\n' > $KEYS
+ printf 'const kChangeNowApiKey = "";\nconst kSimpleSwapApiKey = "";\nconst kNanswapApiKey = "";\nconst kNanoSwapRpcApiKey = "";\nconst kWizSwapApiKey = "";\nconst kShopInBitAccessKey = "";\nconst kShopInBitPartnerSecret = "";\nconst kCakePayApiToken = "";\nconst kExolixApiKey = "";\nconst kLetsExchangeId = "";\nconst kLetsExchangeToken = "";\n' > $KEYS
fi
# Create template wallet test parameter files if they don't already exist
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.