diff --git a/lib/shared/utils/utils.dart b/lib/shared/utils/utils.dart index 3895c6c733..0edf13b274 100644 --- a/lib/shared/utils/utils.dart +++ b/lib/shared/utils/utils.dart @@ -190,16 +190,22 @@ Map? rat2fract(Rational? rat, [bool toLog = true]) { String getTxExplorerUrl(Coin coin, String txHash) { final String explorerUrl = coin.explorerUrl; final String explorerTxUrl = coin.explorerTxUrl; - if (explorerUrl.isEmpty || explorerTxUrl.isEmpty) return ''; + if (explorerUrl.isEmpty) return ''; final hash = coin.type == CoinType.tendermint || coin.type == CoinType.tendermintToken ? txHash.toUpperCase() : txHash; - return coin.need0xPrefixForTxHash && !hash.startsWith('0x') - ? '$explorerUrl${explorerTxUrl}0x$hash' - : '$explorerUrl$explorerTxUrl$hash'; + final normalizedHash = coin.need0xPrefixForTxHash && !hash.startsWith('0x') + ? '0x$hash' + : hash; + + if (explorerTxUrl.isEmpty) { + return '$explorerUrl$normalizedHash'; + } + + return '$explorerUrl$explorerTxUrl$normalizedHash'; } String getAddressExplorerUrl(Coin coin, String address) { diff --git a/test_units/main.dart b/test_units/main.dart index 4989a34d84..abf24bb69a 100644 --- a/test_units/main.dart +++ b/test_units/main.dart @@ -31,6 +31,7 @@ import 'tests/balance_utils/compute_wallet_total_usd_test.dart'; import 'tests/utils/convert_double_to_string_test.dart'; import 'tests/utils/convert_fract_rat_test.dart'; import 'tests/utils/double_to_string_test.dart'; +import 'tests/utils/explorer_url_test.dart'; import 'tests/utils/get_fiat_amount_tests.dart'; import 'tests/utils/get_usd_balance_test.dart'; import 'tests/utils/ipfs_gateway_manager_test.dart'; @@ -69,6 +70,7 @@ void main() { testUsdBalanceFormatter(); testGetFiatAmount(); testCustomDoubleToString(); + testExplorerUrlHelpers(); testRatToFracAndViseVersa(); testDoubleToString(); diff --git a/test_units/tests/utils/explorer_url_test.dart b/test_units/tests/utils/explorer_url_test.dart new file mode 100644 index 0000000000..1a371dbedf --- /dev/null +++ b/test_units/tests/utils/explorer_url_test.dart @@ -0,0 +1,78 @@ +import 'package:komodo_defi_types/komodo_defi_types.dart'; +import 'package:test/test.dart'; +import 'package:web_dex/model/coin.dart'; +import 'package:web_dex/model/coin_type.dart'; +import 'package:web_dex/shared/utils/utils.dart'; + +void testExplorerUrlHelpers() { + test( + 'getTxExplorerUrl falls back to base explorer when tx pattern is empty', + () { + final coin = _coinWithExplorer( + explorerUrl: 'https://explorer.example/tx/', + explorerTxUrl: '', + ); + + expect( + getTxExplorerUrl(coin, 'abc123'), + 'https://explorer.example/tx/abc123', + ); + }, + ); + + test( + 'getTxExplorerUrl keeps 0x prefix behavior on base explorer fallback', + () { + final coin = _coinWithExplorer( + explorerUrl: 'https://etherscan.io/tx/', + explorerTxUrl: '', + protocolType: 'ERC20', + type: CoinType.erc20, + ); + + expect( + getTxExplorerUrl(coin, 'deadbeef'), + 'https://etherscan.io/tx/0xdeadbeef', + ); + }, + ); +} + +Coin _coinWithExplorer({ + required String explorerUrl, + required String explorerTxUrl, + String protocolType = 'UTXO', + CoinType type = CoinType.utxo, +}) { + final assetId = AssetId( + id: 'TEST', + name: 'Test Coin', + parentId: null, + symbol: AssetSymbol(assetConfigId: 'TEST'), + derivationPath: null, + chainId: AssetChainId(chainId: 0), + subClass: CoinSubClass.utxo, + ); + + return Coin( + type: type, + abbr: 'TEST', + id: assetId, + name: 'Test Coin', + logoImageUrl: null, + isCustomCoin: false, + explorerUrl: explorerUrl, + explorerTxUrl: explorerTxUrl, + explorerAddressUrl: '', + protocolType: protocolType, + protocolData: ProtocolData(platform: '', contractAddress: ''), + isTestCoin: false, + coingeckoId: null, + fallbackSwapContract: null, + priority: 0, + state: CoinState.inactive, + walletOnly: false, + mode: CoinMode.standard, + swapContractAddress: null, + ); +}