From 5d7a689d9656230ff6e728de2756f670e4fdb01e Mon Sep 17 00:00:00 2001 From: CharlVS <77973576+CharlVS@users.noreply.github.com> Date: Tue, 24 Mar 2026 17:41:53 +0100 Subject: [PATCH 1/2] fix(komodo_defi_types): support TRON explorer URLs --- .../lib/src/coin_classes/protocol_class.dart | 4 ++ .../protocols/base/explorer_url_pattern.dart | 69 +++++++++++++------ .../test/tron_protocol_test.dart | 26 ++++++- 3 files changed, 77 insertions(+), 22 deletions(-) diff --git a/packages/komodo_defi_types/lib/src/coin_classes/protocol_class.dart b/packages/komodo_defi_types/lib/src/coin_classes/protocol_class.dart index d58c1e86a..f693369f4 100644 --- a/packages/komodo_defi_types/lib/src/coin_classes/protocol_class.dart +++ b/packages/komodo_defi_types/lib/src/coin_classes/protocol_class.dart @@ -166,6 +166,10 @@ abstract class ProtocolClass with ExplorerUrlMixin implements Equatable { } bool supportsTxHistoryStreaming({required bool isChildAsset}) { + // TRON does not currently expose KDF-backed tx history. + if (subClass == CoinSubClass.trx || subClass == CoinSubClass.trc20) { + return false; + } // EVM does not support tx history streaming in KDF if (evmCoinSubClasses.contains(subClass)) { return false; diff --git a/packages/komodo_defi_types/lib/src/protocols/base/explorer_url_pattern.dart b/packages/komodo_defi_types/lib/src/protocols/base/explorer_url_pattern.dart index 7c0d77f02..fa12b94ca 100644 --- a/packages/komodo_defi_types/lib/src/protocols/base/explorer_url_pattern.dart +++ b/packages/komodo_defi_types/lib/src/protocols/base/explorer_url_pattern.dart @@ -10,16 +10,24 @@ class ExplorerUrlPattern { }); factory ExplorerUrlPattern.fromJson(JsonMap config) { - final baseUrl = config.valueOrNull('explorer_url'); + final baseUrl = _normalizePatternValue( + config.valueOrNull('explorer_url'), + ); if (baseUrl == null) return const ExplorerUrlPattern(); // Add scheme if missing final urlString = baseUrl.startsWith('http') ? baseUrl : 'https://$baseUrl'; return ExplorerUrlPattern( baseUrl: Uri.tryParse(urlString), - txPattern: config.valueOrNull('explorer_tx_url'), - addressPattern: config.valueOrNull('explorer_address_url'), - blockPattern: config.valueOrNull('explorer_block_url'), + txPattern: _normalizePatternValue( + config.valueOrNull('explorer_tx_url'), + ), + addressPattern: _normalizePatternValue( + config.valueOrNull('explorer_address_url'), + ), + blockPattern: _normalizePatternValue( + config.valueOrNull('explorer_block_url'), + ), ); } @@ -45,14 +53,36 @@ class ExplorerUrlPattern { // If no placeholders were found, append the first param value if (params.isNotEmpty && url == pattern) { - url = '$url/${Uri.encodeComponent(params.values.first)}'; + url = '$url${Uri.encodeComponent(params.values.first)}'; + } + + final resolvedBaseUrl = baseUrl; + if (resolvedBaseUrl == null) return null; + + if (resolvedBaseUrl.fragment.isNotEmpty && !url.startsWith('#')) { + final baseFragment = resolvedBaseUrl.fragment; + final normalizedBaseFragment = baseFragment.endsWith('/') + ? baseFragment + : '$baseFragment/'; + final normalizedUrl = url.startsWith('/') ? url.substring(1) : url; + + return resolvedBaseUrl.replace( + fragment: '$normalizedBaseFragment$normalizedUrl', + ); } - return baseUrl?.resolve(url); + return resolvedBaseUrl.resolve(url); } catch (e) { return null; } } + + static String? _normalizePatternValue(String? value) { + if (value == null) return null; + + final normalized = value.trim(); + return normalized.isEmpty ? null : normalized; + } } /// Mixin to provide explorer URL functionality to Protocol classes @@ -63,30 +93,29 @@ mixin ExplorerUrlMixin { Uri? explorerTxUrl(String txHash) { if (txHash.isEmpty) return null; - final hash = - needs0xPrefix && !txHash.startsWith('0x') ? '0x$txHash' : txHash; + final hash = needs0xPrefix && !txHash.startsWith('0x') + ? '0x$txHash' + : txHash; - return explorerPattern.buildUrl( - explorerPattern.txPattern, - {'HASH': hash, 'TX': hash}, - ); + return explorerPattern.buildUrl(explorerPattern.txPattern, { + 'HASH': hash, + 'TX': hash, + }); } Uri? explorerAddressUrl(String address) { if (address.isEmpty) return null; - return explorerPattern.buildUrl( - explorerPattern.addressPattern, - {'ADDRESS': address}, - ); + return explorerPattern.buildUrl(explorerPattern.addressPattern, { + 'ADDRESS': address, + }); } Uri? explorerBlockUrl(String blockId) { if (blockId.isEmpty) return null; - return explorerPattern.buildUrl( - explorerPattern.blockPattern, - {'BLOCK': blockId}, - ); + return explorerPattern.buildUrl(explorerPattern.blockPattern, { + 'BLOCK': blockId, + }); } } diff --git a/packages/komodo_defi_types/test/tron_protocol_test.dart b/packages/komodo_defi_types/test/tron_protocol_test.dart index 43efa55a6..572d71f09 100644 --- a/packages/komodo_defi_types/test/tron_protocol_test.dart +++ b/packages/komodo_defi_types/test/tron_protocol_test.dart @@ -11,6 +11,9 @@ Map _trxConfig() => { 'decimals': 6, 'required_confirmations': 1, 'derivation_path': "m/44'/195'", + 'explorer_url': 'https://tronscan.org/', + 'explorer_tx_url': '#/transaction/', + 'explorer_address_url': '#/address/', 'protocol': { 'type': 'TRX', 'protocol_data': {'network': 'Mainnet'}, @@ -27,6 +30,9 @@ Map _trc20Config() => { 'mm2': 1, 'decimals': 6, 'derivation_path': "m/44'/195'", + 'explorer_url': 'https://tronscan.org/', + 'explorer_tx_url': '#/transaction/', + 'explorer_address_url': '#/address/', 'protocol': { 'type': 'TRC20', 'protocol_data': { @@ -116,7 +122,15 @@ void main() { expect(protocol.subClass, CoinSubClass.trx); expect((protocol as TrxProtocol).nodes, isEmpty); expect(protocol.network, 'Mainnet'); - expect(protocol.supportsTxHistoryStreaming(isChildAsset: false), isTrue); + expect(protocol.supportsTxHistoryStreaming(isChildAsset: false), isFalse); + expect( + protocol.explorerTxUrl('abc123')?.toString(), + 'https://tronscan.org/#/transaction/abc123', + ); + expect( + protocol.explorerAddressUrl('TAddress123')?.toString(), + 'https://tronscan.org/#/address/TAddress123', + ); }); test('Asset.fromJson links TRC20 child asset to TRX parent', () { @@ -128,7 +142,15 @@ void main() { expect(child.id.parentId, parent.id); expect(parent.id.subClass.canBeParentOf(child.id.subClass), isTrue); expect(child.supportsBalanceStreaming, isTrue); - expect(child.supportsTxHistoryStreaming, isTrue); + expect(child.supportsTxHistoryStreaming, isFalse); + expect( + child.protocol.explorerTxUrl('def456')?.toString(), + 'https://tronscan.org/#/transaction/def456', + ); + expect( + child.protocol.explorerAddressUrl('TTokenAddress456')?.toString(), + 'https://tronscan.org/#/address/TTokenAddress456', + ); }); test('non-TRON platform assets keep top-level subtype precedence', () { From 1d3b377710338a294055f2d3a5dd7bdbe82aa5c6 Mon Sep 17 00:00:00 2001 From: CharlVS <77973576+CharlVS@users.noreply.github.com> Date: Tue, 24 Mar 2026 17:44:34 +0100 Subject: [PATCH 2/2] chore(sdk): commit pending SDK updates --- packages/komodo_defi_framework/app_build/build_config.json | 2 +- .../test/activation/tron_activation_strategy_test.dart | 2 +- .../transaction_history_strategies_test.dart | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/komodo_defi_framework/app_build/build_config.json b/packages/komodo_defi_framework/app_build/build_config.json index 0a7b02172..76a424612 100644 --- a/packages/komodo_defi_framework/app_build/build_config.json +++ b/packages/komodo_defi_framework/app_build/build_config.json @@ -65,7 +65,7 @@ "coins": { "fetch_at_build_enabled": true, "update_commit_on_build": true, - "bundled_coins_repo_commit": "4d9b6b1da8f2e9da4dc35a4f0311c7e8c0746d53", + "bundled_coins_repo_commit": "16496a71d9473bfab1357ab0b71511c7ba106ce4", "coins_repo_api_url": "https://api.github.com/repos/GLEECBTC/coins", "coins_repo_content_url": "https://raw.githubusercontent.com/GLEECBTC/coins", "coins_repo_branch": "feat/add-tron-coins", diff --git a/packages/komodo_defi_sdk/test/activation/tron_activation_strategy_test.dart b/packages/komodo_defi_sdk/test/activation/tron_activation_strategy_test.dart index f8d15410f..181a08124 100644 --- a/packages/komodo_defi_sdk/test/activation/tron_activation_strategy_test.dart +++ b/packages/komodo_defi_sdk/test/activation/tron_activation_strategy_test.dart @@ -1,8 +1,8 @@ +import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; import 'package:komodo_defi_sdk/src/activation/protocol_strategies/custom_erc20_activation_strategy.dart'; import 'package:komodo_defi_sdk/src/activation/protocol_strategies/erc20_activation_strategy.dart'; import 'package:komodo_defi_sdk/src/activation/protocol_strategies/eth_task_activation_strategy.dart'; import 'package:komodo_defi_sdk/src/activation/protocol_strategies/eth_with_tokens_activation_strategy.dart'; -import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; import 'package:komodo_defi_types/komodo_defi_types.dart'; import 'package:test/test.dart'; diff --git a/packages/komodo_defi_sdk/test/transaction_history/transaction_history_strategies_test.dart b/packages/komodo_defi_sdk/test/transaction_history/transaction_history_strategies_test.dart index 80a68d924..12c2b070c 100644 --- a/packages/komodo_defi_sdk/test/transaction_history/transaction_history_strategies_test.dart +++ b/packages/komodo_defi_sdk/test/transaction_history/transaction_history_strategies_test.dart @@ -32,7 +32,7 @@ Asset _createEvmAsset({ } Asset _createZhtlcAsset() { - final protocol = ZhtlcProtocol.fromJson({ + final protocol = ZhtlcProtocol.fromJson(const { 'type': 'ZHTLC', 'electrum_servers': [ {'url': 'lightwalletd.pirate.black', 'port': 9067, 'protocol': 'SSL'},