From 1b492955535bbc21da33298b84f1627f2d1b11d3 Mon Sep 17 00:00:00 2001 From: Francois Date: Wed, 22 Jan 2025 18:06:22 +0200 Subject: [PATCH 01/15] feat(rpc-methods): add custom token import RPC functions --- .../general/address_format.dart | 91 +++++++++++++++ .../rpc_methods/utility/get_token_info.dart | 104 ++++++++++++++++++ .../rpc_methods/wallet/convert_address.dart | 58 ++++++++++ .../lib/src/rpc_methods_library.dart | 39 +++++++ 4 files changed, 292 insertions(+) create mode 100644 packages/komodo_defi_rpc_methods/lib/src/rpc_methods/utility/get_token_info.dart create mode 100644 packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/convert_address.dart diff --git a/packages/komodo_defi_rpc_methods/lib/src/common_structures/general/address_format.dart b/packages/komodo_defi_rpc_methods/lib/src/common_structures/general/address_format.dart index 74cb1d8d..aeb0cfe3 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/common_structures/general/address_format.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/common_structures/general/address_format.dart @@ -1,9 +1,41 @@ +import 'package:komodo_defi_types/komodo_defi_types.dart'; + class AddressFormat { const AddressFormat({ required this.format, required this.network, }); + factory AddressFormat.fromCoinSubClass( + CoinSubClass subClass, { + bool isBchNetwork = false, + }) { + switch (subClass) { + case CoinSubClass.erc20: + return AddressFormat( + format: AddressFormatFormat.mixedCase.toString(), + network: '', + ); + case CoinSubClass.qrc20: + return AddressFormat( + format: AddressFormatFormat.contract.toString(), + network: '', + ); + case CoinSubClass.utxo: + // The only explicitly defined coins are ETH, UTXO and QTUM, and the + // behaviour previously was to use cashaddress as the default + // unless the coin was ERC20. + // ignore: no_default_cases + default: + return AddressFormat( + format: AddressFormatFormat.cashAddress.toString(), + // Only set network for BCH coins + network: + isBchNetwork ? AddressFormatNetwork.bitcoinCash.toString() : '', + ); + } + } + final String format; final String network; @@ -12,3 +44,62 @@ class AddressFormat { 'network': network, }; } + +/// [AddressFormat] format field options. +enum AddressFormatFormat { + /// Use for ETH, ERC20 coins + mixedCase, + + /// Use [cashAddress] OR [standard] for UTXO coins + cashAddress, + + /// Use [cashAddress] OR [standard] for UTXO coins + standard, + + /// Use [contract] or [wallet] for QTUM and QRC20 coins + contract, + + /// Use [contract] or [wallet] for QTUM and QRC20 coins + wallet; + + @override + String toString() { + switch (this) { + case AddressFormatFormat.mixedCase: + return 'mixedcase'; + case AddressFormatFormat.cashAddress: + return 'cashaddress'; + case AddressFormatFormat.standard: + return 'standard'; + case AddressFormatFormat.contract: + return 'contract'; + case AddressFormatFormat.wallet: + return 'wallet'; + } + } +} + +/// [AddressFormat] network prefix for [AddressFormatFormat.cashAddress] +/// format. Used only for UTXO coins, specifically BCH at the moment. +enum AddressFormatNetwork { + /// BCH main network (mainnet) + bitcoinCash, + + /// BCH test network (testnet) + bchTest, + + /// BCH regtest + bchReg; + + @override + String toString() { + switch (this) { + case AddressFormatNetwork.bitcoinCash: + return 'bitcoincash'; + case AddressFormatNetwork.bchTest: + return 'bchtest'; + case AddressFormatNetwork.bchReg: + return 'bchreg'; + } + } +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/utility/get_token_info.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/utility/get_token_info.dart new file mode 100644 index 00000000..d8a5b65c --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/utility/get_token_info.dart @@ -0,0 +1,104 @@ +import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; +import 'package:komodo_defi_types/komodo_defi_types.dart'; + +/// Request to get the ticker and decimals values required for custom token +/// activation, given a platform and contract as input +class GetTokenInfoRequest + extends BaseRequest + with RequestHandlingMixin { + GetTokenInfoRequest({ + required String rpcPass, + required this.protocolType, + required this.platform, + required this.contractAddress, + }) : super(method: 'get_token_info', rpcPass: rpcPass, mmrpc: null); + + /// Token type - e.g ERC20 for tokens on the Ethereum network + final String protocolType; + + /// The parent coin of the token's platform - e.g MATIC for PLG20 tokens + /// protocol_data.platform + final String platform; + + /// Must be mixed case The identifying hex string for the token's contract. + /// Can be found on sites like EthScan, BscScan & PolygonScan + /// platform_data.contract_address + final String contractAddress; + + @override + Map toJson() { + return super.toJson().deepMerge({ + 'params': { + 'protocol': { + 'type': protocolType, + 'protocol_data': { + 'platform': platform, + 'contract_address': contractAddress, + }, + }, + }, + }); + } + + @override + GetTokenInfoResponse parse(Map json) => + GetTokenInfoResponse.parse(json); +} + +class GetTokenInfoResponse extends BaseResponse { + GetTokenInfoResponse({ + required super.mmrpc, + required this.type, + required this.info, + }); + + factory GetTokenInfoResponse.parse(Map json) { + return GetTokenInfoResponse( + mmrpc: json.valueOrNull('mmrpc'), + type: json.value('type'), + info: TokenInfo.fromJson(json.value>('info')), + ); + } + + /// Token type - e.g PLG20 for tokens on the Polygon network + final String type; + final TokenInfo info; + + @override + Map toJson() { + return { + 'type': type, + 'info': info.toJson(), + }; + } +} + +class TokenInfo { + TokenInfo({ + required this.symbol, + required this.decimals, + }); + + factory TokenInfo.fromJson(Map json) { + return TokenInfo( + symbol: json.value('symbol'), + decimals: json.value('decimals'), + ); + } + + /// The ticker of the token linked to the contract address and + /// network requested + final String symbol; + + /// Defines the number of digits after the decimal point that should be + /// used to display the orderbook amounts, balance, and the value of inputs + /// to be used in the case of order creation or a withdraw transaction. + final int decimals; + + Map toJson() { + return { + 'symbol': symbol, + 'decimals': decimals, + }; + } +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/convert_address.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/convert_address.dart new file mode 100644 index 00000000..59978330 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/convert_address.dart @@ -0,0 +1,58 @@ +import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; +import 'package:komodo_defi_types/komodo_defi_types.dart'; + +class ConvertAddressRequest + extends BaseRequest + with RequestHandlingMixin { + ConvertAddressRequest({ + required String rpcPass, + required this.fromAddress, + required this.coinSubClass, + this.isBchNetwork = false, + }) : super(method: 'convertaddress', rpcPass: rpcPass, mmrpc: null); + + final String fromAddress; + final CoinSubClass coinSubClass; + final bool isBchNetwork; + + @override + Map toJson() { + return super.toJson().deepMerge({ + 'from': fromAddress, + 'coin': coinSubClass, + 'to_address_format': AddressFormat.fromCoinSubClass( + coinSubClass, + isBchNetwork: isBchNetwork, + ).toJson(), + }); + } + + @override + ConvertAddressResponse parse(Map json) { + return ConvertAddressResponse.parse(json); + } +} + +class ConvertAddressResponse extends BaseResponse { + ConvertAddressResponse({ + required super.mmrpc, + required this.address, + }); + + factory ConvertAddressResponse.parse(Map json) { + return ConvertAddressResponse( + mmrpc: json.valueOrNull('mmrpc'), + address: json.value('address'), + ); + } + + final String address; + + @override + Map toJson() { + return { + 'mmrpc': mmrpc, + 'address': address, + }; + } +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods_library.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods_library.dart index 666f3dce..9ebed84a 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods_library.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods_library.dart @@ -4,6 +4,8 @@ // ignore_for_file: unused_field, unused_element import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; +import 'package:komodo_defi_rpc_methods/src/rpc_methods/utility/get_token_info.dart'; +import 'package:komodo_defi_rpc_methods/src/rpc_methods/wallet/convert_address.dart'; import 'package:komodo_defi_types/komodo_defi_types.dart'; /// A class that provides a library of RPC methods used by the Komodo DeFi @@ -43,6 +45,7 @@ class KomodoDefiRpcMethods { // Add other namespaces here, e.g.: // TradeNamespace get trade => TradeNamespace(_client); // UtilityNamespace get utility => UtilityNamespace(_client); + UtilityMethods get utility => UtilityMethods(_client); } class TaskMethods extends BaseRpcMethodNamespace { @@ -71,6 +74,42 @@ class WalletMethods extends BaseRpcMethodNamespace { Future getPublicKeyHash([String? rpcPass]) => execute(GetPublicKeyHashRequest(rpcPass: rpcPass)); + + Future convertAddress({ + required String fromAddress, + required CoinSubClass coinSubClass, + String? rpcPass, + }) => + execute( + ConvertAddressRequest( + rpcPass: rpcPass ?? '', + fromAddress: fromAddress, + coinSubClass: coinSubClass, + ), + ); +} + +/// KDF v2 Utility Methods not specific to any larger feature +/// or namespace (e.g. current MTP, token info for custom token activation). +class UtilityMethods extends BaseRpcMethodNamespace { + UtilityMethods(super.client); + + /// Returns the ticker and decimals values required for activation of a custom + /// token, given a [platform], [protocolType], and [contractAddress]. + Future getTokenInfo({ + required String protocolType, + required String platform, + required String contractAddress, + String? rpcPass, + }) => + execute( + GetTokenInfoRequest( + protocolType: protocolType, + platform: platform, + contractAddress: contractAddress, + rpcPass: rpcPass ?? '', + ), + ); } class GeneralActivationMethods extends BaseRpcMethodNamespace { From def68f445b00d2129b6d0ed5e006e561ff5b111d Mon Sep 17 00:00:00 2001 From: Francois Date: Thu, 23 Jan 2025 10:47:18 +0200 Subject: [PATCH 02/15] feat(rpc-methods): add validate address RPC (legacy API) --- .../rpc_methods/wallet/validate_address.dart | 56 +++++++++++++++++++ .../lib/src/rpc_methods_library.dart | 13 +++++ 2 files changed, 69 insertions(+) create mode 100644 packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/validate_address.dart diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/validate_address.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/validate_address.dart new file mode 100644 index 00000000..f4c19ff4 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/validate_address.dart @@ -0,0 +1,56 @@ +import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; +import 'package:komodo_defi_types/komodo_defi_types.dart'; + +class ValidateAddressRequest + extends BaseRequest + with RequestHandlingMixin { + ValidateAddressRequest({ + required this.coin, + required this.address, + required String rpcPass, + }) : super(method: 'validateaddress', rpcPass: rpcPass, mmrpc: null); + + final String coin; + final String address; + + @override + Map toJson() { + return super.toJson().deepMerge({ + 'coin': coin, + 'address': address, + }); + } + + @override + ValidateAddressResponse parse(Map json) { + return ValidateAddressResponse.parse(json); + } +} + +class ValidateAddressResponse extends BaseResponse { + ValidateAddressResponse({ + required super.mmrpc, + required this.isValid, + this.reason, + }); + + factory ValidateAddressResponse.parse(Map json) { + return ValidateAddressResponse( + mmrpc: json.valueOrNull('mmrpc'), + isValid: json.value('is_valid'), + reason: json.valueOrNull('reason'), + ); + } + + final bool isValid; + final String? reason; + + @override + Map toJson() { + return { + 'mmrpc': mmrpc, + 'is_valid': isValid, + if (reason != null) 'reason': reason, + }; + } +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods_library.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods_library.dart index 9ebed84a..4578e403 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods_library.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods_library.dart @@ -6,6 +6,7 @@ import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; import 'package:komodo_defi_rpc_methods/src/rpc_methods/utility/get_token_info.dart'; import 'package:komodo_defi_rpc_methods/src/rpc_methods/wallet/convert_address.dart'; +import 'package:komodo_defi_rpc_methods/src/rpc_methods/wallet/validate_address.dart'; import 'package:komodo_defi_types/komodo_defi_types.dart'; /// A class that provides a library of RPC methods used by the Komodo DeFi @@ -87,6 +88,18 @@ class WalletMethods extends BaseRpcMethodNamespace { coinSubClass: coinSubClass, ), ); + + Future validateAddress({ + required String assetId, + required String address, + }) => + execute( + ValidateAddressRequest( + coin: assetId, + address: address, + rpcPass: rpcPass ?? '', + ), + ); } /// KDF v2 Utility Methods not specific to any larger feature From 552b9c260c681422cd2efe366ad80871e117bd65 Mon Sep 17 00:00:00 2001 From: Francois Date: Mon, 3 Feb 2025 10:46:34 +0200 Subject: [PATCH 03/15] feat: add custom erc20 token activation strategy --- .../rpc_methods/eth/enable_custom_erc20.dart | 49 ++++++++++ .../rpc_methods/eth/eth_rpc_extensions.dart | 18 ++++ .../src/activation/activation_manager.dart | 5 +- .../activation_strategy_factory.dart | 8 ++ .../custom_erc20_activation_strategy.dart | 89 +++++++++++++++++++ .../lib/src/assets/asset_manager.dart | 7 +- .../lib/src/komodo_defi_sdk.dart | 18 ++++ 7 files changed, 189 insertions(+), 5 deletions(-) create mode 100644 packages/komodo_defi_rpc_methods/lib/src/rpc_methods/eth/enable_custom_erc20.dart create mode 100644 packages/komodo_defi_sdk/lib/src/activation/protocol_strategies/custom_erc20_activation_strategy.dart diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/eth/enable_custom_erc20.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/eth/enable_custom_erc20.dart new file mode 100644 index 00000000..e7632b62 --- /dev/null +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/eth/enable_custom_erc20.dart @@ -0,0 +1,49 @@ +import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; +import 'package:komodo_defi_types/komodo_defi_types.dart'; + +class EnableCustomErc20TokenRequest + extends BaseRequest + with RequestHandlingMixin { + EnableCustomErc20TokenRequest({ + required String rpcPass, + required this.ticker, + required this.activationParams, + required this.platform, + required this.contractAddress, + }) : super(method: 'enable_erc20', rpcPass: rpcPass, mmrpc: '2.0'); + + final String ticker; + final Erc20ActivationParams activationParams; + final String platform; + final String contractAddress; + + @override + Map toJson() { + assert( + platform.isNotEmpty, + 'Platform is required when activating a custom token.', + ); + assert( + contractAddress.isNotEmpty, + 'Contract address is required when activating a custom token.', + ); + + return super.toJson().deepMerge({ + 'params': { + 'ticker': ticker, + 'activation_params': activationParams.toJsonRequestParams(), + 'protocol': { + 'type': 'ERC20', + 'protocol_data': { + 'platform': platform, + 'contract_address': contractAddress, + }, + }, + }, + }); + } + + @override + EnableErc20Response parse(Map json) => + EnableErc20Response.parse(json); +} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/eth/eth_rpc_extensions.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/eth/eth_rpc_extensions.dart index ebbb2cf9..d93d1950 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/eth/eth_rpc_extensions.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/eth/eth_rpc_extensions.dart @@ -1,4 +1,5 @@ import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; +import 'package:komodo_defi_rpc_methods/src/rpc_methods/eth/enable_custom_erc20.dart'; /// Extensions for ETH-related RPC methods // lib/src/rpc_methods/eth/eth_rpc_extensions.dart @@ -32,4 +33,21 @@ class Erc20MethodsNamespace extends BaseRpcMethodNamespace { ), ); } + + Future enableCustomErc20Token({ + required String ticker, + required Erc20ActivationParams activationParams, + required String platform, + required String contractAddress, + }) { + return execute( + EnableCustomErc20TokenRequest( + rpcPass: rpcPass ?? '', + ticker: ticker, + activationParams: activationParams, + platform: platform, + contractAddress: contractAddress, + ), + ); + } } diff --git a/packages/komodo_defi_sdk/lib/src/activation/activation_manager.dart b/packages/komodo_defi_sdk/lib/src/activation/activation_manager.dart index 4d45c35e..7bd6961e 100644 --- a/packages/komodo_defi_sdk/lib/src/activation/activation_manager.dart +++ b/packages/komodo_defi_sdk/lib/src/activation/activation_manager.dart @@ -9,8 +9,9 @@ import 'package:komodo_defi_types/komodo_defi_types.dart'; /// Manages the activation lifecycle of assets class ActivationManager { - ActivationManager(ApiClient client) - : _activator = ActivationStrategyFactory.createStrategy(client); + ActivationManager(ApiClient client, {SmartAssetActivator? activator}) + : _activator = + activator ?? ActivationStrategyFactory.createStrategy(client); final SmartAssetActivator _activator; final Map> _activationCompleters = {}; diff --git a/packages/komodo_defi_sdk/lib/src/activation/base_strategies/activation_strategy_factory.dart b/packages/komodo_defi_sdk/lib/src/activation/base_strategies/activation_strategy_factory.dart index ede1dbdc..4267cb3f 100644 --- a/packages/komodo_defi_sdk/lib/src/activation/base_strategies/activation_strategy_factory.dart +++ b/packages/komodo_defi_sdk/lib/src/activation/base_strategies/activation_strategy_factory.dart @@ -1,4 +1,5 @@ import 'package:komodo_defi_sdk/src/activation/_activation.dart'; +import 'package:komodo_defi_sdk/src/activation/protocol_strategies/custom_erc20_activation_strategy.dart'; import 'package:komodo_defi_types/komodo_defi_types.dart'; /// Factory for creating the complete activation strategy stack @@ -21,4 +22,11 @@ class ActivationStrategyFactory { ), ); } + + static SmartAssetActivator createCustomTokenStrategy(ApiClient client) { + return SmartAssetActivator( + client, + CompositeAssetActivator(client, [CustomErc20ActivationStrategy(client)]), + ); + } } diff --git a/packages/komodo_defi_sdk/lib/src/activation/protocol_strategies/custom_erc20_activation_strategy.dart b/packages/komodo_defi_sdk/lib/src/activation/protocol_strategies/custom_erc20_activation_strategy.dart new file mode 100644 index 00000000..ab742e9d --- /dev/null +++ b/packages/komodo_defi_sdk/lib/src/activation/protocol_strategies/custom_erc20_activation_strategy.dart @@ -0,0 +1,89 @@ +import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; +import 'package:komodo_defi_sdk/src/activation/_activation.dart'; +import 'package:komodo_defi_types/komodo_defi_types.dart'; + +class CustomErc20ActivationStrategy extends ProtocolActivationStrategy { + const CustomErc20ActivationStrategy(super.client); + + @override + Set get supportedProtocols => { + CoinSubClass.erc20, + CoinSubClass.bep20, + CoinSubClass.ftm20, + CoinSubClass.matic, + CoinSubClass.avx20, + CoinSubClass.hrc20, + CoinSubClass.moonbeam, + CoinSubClass.moonriver, + CoinSubClass.ethereumClassic, + CoinSubClass.ubiq, + CoinSubClass.krc20, + CoinSubClass.ewt, + CoinSubClass.hecoChain, + CoinSubClass.rskSmartBitcoin, + CoinSubClass.arbitrum, + }; + + @override + bool get supportsBatchActivation => true; + + @override + Stream activate( + Asset asset, [ + List? children, + ]) async* { + yield ActivationProgress( + status: 'Activating ${asset.id.name}...', + progressDetails: ActivationProgressDetails( + currentStep: 'initialization', + stepCount: 2, + additionalInfo: { + 'assetType': 'token', + 'protocol': asset.protocol.subClass.formatted, + }, + ), + ); + + try { + final protocolData = asset.protocol.config + .valueOrNull('protocol', 'protocol_data'); + if (protocolData == null) { + throw StateError('Protocol data is missing from custom token config'); + } + + await client.rpc.erc20.enableCustomErc20Token( + ticker: asset.id.id, + activationParams: Erc20ActivationParams.fromJsonConfig( + asset.protocol.config, + ), + platform: protocolData.value('platform'), + contractAddress: protocolData.value('contract_address'), + ); + + yield ActivationProgress.success( + details: ActivationProgressDetails( + currentStep: 'complete', + stepCount: 2, + additionalInfo: { + 'activatedChain': asset.id.name, + 'activationTime': DateTime.now().toIso8601String(), + 'childCount': children?.length ?? 0, + }, + ), + ); + } catch (e, stack) { + yield ActivationProgress( + status: 'Activation failed', + errorMessage: e.toString(), + isComplete: true, + progressDetails: ActivationProgressDetails( + currentStep: 'error', + stepCount: 2, + errorCode: 'ERC20_ACTIVATION_ERROR', + errorDetails: e.toString(), + stackTrace: stack.toString(), + ), + ); + } + } +} diff --git a/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart b/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart index 4c138b5a..5754d2b0 100644 --- a/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart +++ b/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart @@ -15,9 +15,10 @@ class AssetManager { AssetManager( this._client, this._auth, - this._config, - ) : _assetHistory = AssetHistoryStorage(), - _activationManager = ActivationManager(_client); + this._config, { + ActivationManager? activationManager, + }) : _assetHistory = AssetHistoryStorage(), + _activationManager = activationManager ?? ActivationManager(_client); final ApiClient _client; final KomodoDefiLocalAuth _auth; diff --git a/packages/komodo_defi_sdk/lib/src/komodo_defi_sdk.dart b/packages/komodo_defi_sdk/lib/src/komodo_defi_sdk.dart index dc345f44..fdb2867e 100644 --- a/packages/komodo_defi_sdk/lib/src/komodo_defi_sdk.dart +++ b/packages/komodo_defi_sdk/lib/src/komodo_defi_sdk.dart @@ -1,6 +1,8 @@ import 'package:flutter/foundation.dart'; import 'package:komodo_defi_framework/komodo_defi_framework.dart'; import 'package:komodo_defi_local_auth/komodo_defi_local_auth.dart'; +import 'package:komodo_defi_sdk/src/activation/activation_manager.dart'; +import 'package:komodo_defi_sdk/src/activation/base_strategies/activation_strategy_factory.dart'; import 'package:komodo_defi_sdk/src/addresses/address_operations.dart'; import 'package:komodo_defi_sdk/src/assets/asset_manager.dart'; import 'package:komodo_defi_sdk/src/pubkeys/pubkey_manager.dart'; @@ -164,6 +166,17 @@ class KomodoDefiSdk with SecureRpcPasswordMixin { ); _assets = AssetManager(_apiClient!, _auth!, _config); + _customTokenAssets = AssetManager( + _apiClient, + _auth!, + _config, + activationManager: ActivationManager( + _apiClient, + activator: ActivationStrategyFactory.createCustomTokenStrategy( + _apiClient, + ), + ), + ); _mnemonicValidator = MnemonicValidator(); @@ -190,6 +203,11 @@ class KomodoDefiSdk with SecureRpcPasswordMixin { late final AssetManager? _assets; AssetManager get assets => _assertSdkInitialized(_assets); + late final AssetManager? _customTokenAssets; + + /// Asset manager for custom tokens activated by the user as + /// wallet only assets. + AssetManager get customAssets => _assertSdkInitialized(_customTokenAssets); TransactionHistoryManager get transactions => _assertSdkInitialized(_transactionHistory); From 705237281237cd549187a2fb9cbedaa8b22b5913 Mon Sep 17 00:00:00 2001 From: Francois Date: Mon, 3 Feb 2025 10:48:04 +0200 Subject: [PATCH 04/15] fix(rpc-methods): custom token import RPC parsing types and parameters --- .../lib/src/common_structures/general/address_format.dart | 3 ++- .../lib/src/rpc_methods/utility/get_token_info.dart | 7 ++++--- .../lib/src/rpc_methods/wallet/convert_address.dart | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/komodo_defi_rpc_methods/lib/src/common_structures/general/address_format.dart b/packages/komodo_defi_rpc_methods/lib/src/common_structures/general/address_format.dart index aeb0cfe3..42181be8 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/common_structures/general/address_format.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/common_structures/general/address_format.dart @@ -12,6 +12,7 @@ class AddressFormat { }) { switch (subClass) { case CoinSubClass.erc20: + case CoinSubClass.ethereumClassic: return AddressFormat( format: AddressFormatFormat.mixedCase.toString(), network: '', @@ -41,7 +42,7 @@ class AddressFormat { Map toJson() => { 'format': format, - 'network': network, + if (network.isNotEmpty) 'network': network, }; } diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/utility/get_token_info.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/utility/get_token_info.dart index d8a5b65c..03239484 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/utility/get_token_info.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/utility/get_token_info.dart @@ -11,7 +11,7 @@ class GetTokenInfoRequest required this.protocolType, required this.platform, required this.contractAddress, - }) : super(method: 'get_token_info', rpcPass: rpcPass, mmrpc: null); + }) : super(method: 'get_token_info', rpcPass: rpcPass, mmrpc: '2.0'); /// Token type - e.g ERC20 for tokens on the Ethereum network final String protocolType; @@ -53,10 +53,11 @@ class GetTokenInfoResponse extends BaseResponse { }); factory GetTokenInfoResponse.parse(Map json) { + final result = json.value('result'); return GetTokenInfoResponse( mmrpc: json.valueOrNull('mmrpc'), - type: json.value('type'), - info: TokenInfo.fromJson(json.value>('info')), + type: result.value('type'), + info: TokenInfo.fromJson(result.value('info')), ); } diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/convert_address.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/convert_address.dart index 59978330..c6b4ccea 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/convert_address.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/convert_address.dart @@ -19,7 +19,7 @@ class ConvertAddressRequest Map toJson() { return super.toJson().deepMerge({ 'from': fromAddress, - 'coin': coinSubClass, + 'coin': coinSubClass.ticker, 'to_address_format': AddressFormat.fromCoinSubClass( coinSubClass, isBchNetwork: isBchNetwork, @@ -42,7 +42,7 @@ class ConvertAddressResponse extends BaseResponse { factory ConvertAddressResponse.parse(Map json) { return ConvertAddressResponse( mmrpc: json.valueOrNull('mmrpc'), - address: json.value('address'), + address: json.value('result', 'address'), ); } From 591204088701b48c1cb07a9d35019e52dd8f36d5 Mon Sep 17 00:00:00 2001 From: Francois Date: Mon, 3 Feb 2025 10:49:35 +0200 Subject: [PATCH 05/15] perf(rpc-methods): remove verbose log debug statement --- .../komodo_defi_rpc_methods/lib/src/models/error_response.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/komodo_defi_rpc_methods/lib/src/models/error_response.dart b/packages/komodo_defi_rpc_methods/lib/src/models/error_response.dart index 7c1a091f..017c163e 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/models/error_response.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/models/error_response.dart @@ -43,8 +43,6 @@ class GeneralErrorResponse extends BaseResponse implements Exception { json.hasNestedKey('error') || json.valueOrNull('result', 'status') == 'Error'; - log('isErrorResponse: $isError, json: $json'); - return isError; } From 9801d875c26f29de00ade3b544132362c266f202 Mon Sep 17 00:00:00 2001 From: Francois Date: Mon, 3 Feb 2025 10:54:10 +0200 Subject: [PATCH 06/15] refactor: integrate custom token activation with existing asset manager add boolean flag to classes rather than creating a new asset manager type that makes the assets interface more cumbersome to use --- .../activation_strategy_base.dart | 12 ++++++++++ .../activation_strategy_factory.dart | 8 +------ .../custom_erc20_activation_strategy.dart | 5 ++++ .../lib/src/komodo_defi_sdk.dart | 18 -------------- .../lib/src/coin_classes/protocol_class.dart | 6 +++++ .../src/protocols/erc20/erc20_protocol.dart | 24 +++++++++++++++++++ 6 files changed, 48 insertions(+), 25 deletions(-) diff --git a/packages/komodo_defi_sdk/lib/src/activation/base_strategies/activation_strategy_base.dart b/packages/komodo_defi_sdk/lib/src/activation/base_strategies/activation_strategy_base.dart index ab602770..8271415f 100644 --- a/packages/komodo_defi_sdk/lib/src/activation/base_strategies/activation_strategy_base.dart +++ b/packages/komodo_defi_sdk/lib/src/activation/base_strategies/activation_strategy_base.dart @@ -14,6 +14,11 @@ abstract class BatchCapableActivator extends AssetActivator { const BatchCapableActivator(super.client); bool get supportsBatchActivation; + + /// Whether the activator supports activation of custom EVM-chain + /// tokens that are not part of the live coins configuration. + /// Defaults to false. + bool get supportsCustomTokenActivation => false; } /// Smart activator that chooses between batch/single methods @@ -129,6 +134,13 @@ abstract class ProtocolActivationStrategy extends BatchCapableActivator { @override bool canHandle(Asset asset) => + // | isCustomToken | supportsCustomTokenActivation | result | + // |---------------|------------------------------|--------| + // | true | true | true | + // | true | false | false | + // | false | true | true | + // | false | false | false | + (!asset.protocol.isCustomToken || supportsCustomTokenActivation) && supportedProtocols.contains(asset.protocol.subClass); Set get supportedProtocols; diff --git a/packages/komodo_defi_sdk/lib/src/activation/base_strategies/activation_strategy_factory.dart b/packages/komodo_defi_sdk/lib/src/activation/base_strategies/activation_strategy_factory.dart index 4267cb3f..2abf991f 100644 --- a/packages/komodo_defi_sdk/lib/src/activation/base_strategies/activation_strategy_factory.dart +++ b/packages/komodo_defi_sdk/lib/src/activation/base_strategies/activation_strategy_factory.dart @@ -18,15 +18,9 @@ class ActivationStrategyFactory { TendermintActivationStrategy(client), QtumActivationStrategy(client), ZhtlcActivationStrategy(client), + CustomErc20ActivationStrategy(client), ], ), ); } - - static SmartAssetActivator createCustomTokenStrategy(ApiClient client) { - return SmartAssetActivator( - client, - CompositeAssetActivator(client, [CustomErc20ActivationStrategy(client)]), - ); - } } diff --git a/packages/komodo_defi_sdk/lib/src/activation/protocol_strategies/custom_erc20_activation_strategy.dart b/packages/komodo_defi_sdk/lib/src/activation/protocol_strategies/custom_erc20_activation_strategy.dart index ab742e9d..5b288831 100644 --- a/packages/komodo_defi_sdk/lib/src/activation/protocol_strategies/custom_erc20_activation_strategy.dart +++ b/packages/komodo_defi_sdk/lib/src/activation/protocol_strategies/custom_erc20_activation_strategy.dart @@ -2,6 +2,8 @@ import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; import 'package:komodo_defi_sdk/src/activation/_activation.dart'; import 'package:komodo_defi_types/komodo_defi_types.dart'; +/// Activation strategy for custom ERC20 tokens. This strategy is used to +/// activate tokens that are not part of the live coins configuration. class CustomErc20ActivationStrategy extends ProtocolActivationStrategy { const CustomErc20ActivationStrategy(super.client); @@ -24,6 +26,9 @@ class CustomErc20ActivationStrategy extends ProtocolActivationStrategy { CoinSubClass.arbitrum, }; + @override + bool get supportsCustomTokenActivation => true; + @override bool get supportsBatchActivation => true; diff --git a/packages/komodo_defi_sdk/lib/src/komodo_defi_sdk.dart b/packages/komodo_defi_sdk/lib/src/komodo_defi_sdk.dart index fdb2867e..dc345f44 100644 --- a/packages/komodo_defi_sdk/lib/src/komodo_defi_sdk.dart +++ b/packages/komodo_defi_sdk/lib/src/komodo_defi_sdk.dart @@ -1,8 +1,6 @@ import 'package:flutter/foundation.dart'; import 'package:komodo_defi_framework/komodo_defi_framework.dart'; import 'package:komodo_defi_local_auth/komodo_defi_local_auth.dart'; -import 'package:komodo_defi_sdk/src/activation/activation_manager.dart'; -import 'package:komodo_defi_sdk/src/activation/base_strategies/activation_strategy_factory.dart'; import 'package:komodo_defi_sdk/src/addresses/address_operations.dart'; import 'package:komodo_defi_sdk/src/assets/asset_manager.dart'; import 'package:komodo_defi_sdk/src/pubkeys/pubkey_manager.dart'; @@ -166,17 +164,6 @@ class KomodoDefiSdk with SecureRpcPasswordMixin { ); _assets = AssetManager(_apiClient!, _auth!, _config); - _customTokenAssets = AssetManager( - _apiClient, - _auth!, - _config, - activationManager: ActivationManager( - _apiClient, - activator: ActivationStrategyFactory.createCustomTokenStrategy( - _apiClient, - ), - ), - ); _mnemonicValidator = MnemonicValidator(); @@ -203,11 +190,6 @@ class KomodoDefiSdk with SecureRpcPasswordMixin { late final AssetManager? _assets; AssetManager get assets => _assertSdkInitialized(_assets); - late final AssetManager? _customTokenAssets; - - /// Asset manager for custom tokens activated by the user as - /// wallet only assets. - AssetManager get customAssets => _assertSdkInitialized(_customTokenAssets); TransactionHistoryManager get transactions => _assertSdkInitialized(_transactionHistory); 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 e8ef1527..1e2c0d48 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 @@ -9,6 +9,7 @@ abstract class ProtocolClass with ExplorerUrlMixin { required this.subClass, required this.config, this.supportedProtocols = const [], + this.isCustomToken = false, }); /// Creates the appropriate protocol class from JSON config @@ -89,6 +90,11 @@ abstract class ProtocolClass with ExplorerUrlMixin { final JsonMap config; final List supportedProtocols; + /// Whether this is a custom token activated by the user. + /// Only EVM tokens are supported (e.g. ETH), and they are activated + /// as wallet-only. + final bool isCustomToken; + /// Whether this protocol supports multiple addresses per wallet bool get supportsMultipleAddresses; diff --git a/packages/komodo_defi_types/lib/src/protocols/erc20/erc20_protocol.dart b/packages/komodo_defi_types/lib/src/protocols/erc20/erc20_protocol.dart index a274b3a5..80655f67 100644 --- a/packages/komodo_defi_types/lib/src/protocols/erc20/erc20_protocol.dart +++ b/packages/komodo_defi_types/lib/src/protocols/erc20/erc20_protocol.dart @@ -6,6 +6,7 @@ class Erc20Protocol extends ProtocolClass { Erc20Protocol._({ required super.subClass, required super.config, + super.isCustomToken = false, }); factory Erc20Protocol.fromJson(JsonMap json) { @@ -67,4 +68,27 @@ class Erc20Protocol extends ProtocolClass { // TODO: Confirm if this is correct, or if it is only for 'ERC20' and 'ETH' // protocols as is in the legacy repository. bool get needs0xPrefix => true; + + Erc20Protocol copyWith({ + int? chainId, + List? nodes, + String? swapContractAddress, + String? fallbackSwapContract, + bool? isCustomToken, + }) { + return Erc20Protocol._( + subClass: subClass, + isCustomToken: isCustomToken ?? this.isCustomToken, + config: JsonMap.from(config) + ..addAll({ + if (chainId != null) 'chain_id': chainId, + if (nodes != null) + 'nodes': nodes.map((node) => node.toJson()).toList(), + if (swapContractAddress != null) + 'swap_contract_address': swapContractAddress, + if (fallbackSwapContract != null) + 'fallback_swap_contract': fallbackSwapContract, + }), + ); + } } From 228b568ac452f949feeeb74db7c500a2f246aa57 Mon Sep 17 00:00:00 2001 From: Francois Date: Mon, 3 Feb 2025 10:56:01 +0200 Subject: [PATCH 07/15] feat: Add custom asset history storage manager Significant changes include: - Updated the commit reference for bundled coins in the build configuration - Added a new class, CustomAssetHistoryStorage, to manage custom token history - Enhanced AssetManager to handle custom tokens by adding them to wallet history and activating them - Extended the Asset class with a method to create an instance from JSON data --- .../lib/src/assets/asset_manager.dart | 20 ++++++-- .../assets/custom_asset_history_storage.dart | 47 +++++++++++++++++++ .../lib/src/assets/asset.dart | 15 ++++-- .../lib/src/coin_classes/protocol_class.dart | 18 ++++++- .../src/protocols/erc20/erc20_protocol.dart | 1 + .../lib/src/transactions/transaction.dart | 29 ++++++++++++ 6 files changed, 121 insertions(+), 9 deletions(-) create mode 100644 packages/komodo_defi_sdk/lib/src/assets/custom_asset_history_storage.dart diff --git a/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart b/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart index 5754d2b0..5c2066cd 100644 --- a/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart +++ b/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart @@ -6,6 +6,7 @@ import 'dart:collection'; import 'package:komodo_coins/komodo_coins.dart'; import 'package:komodo_defi_local_auth/komodo_defi_local_auth.dart'; import 'package:komodo_defi_sdk/src/_internal_exports.dart'; +import 'package:komodo_defi_sdk/src/assets/custom_asset_history_storage.dart'; import 'package:komodo_defi_sdk/src/sdk/sdk_config.dart'; import 'package:komodo_defi_types/komodo_defi_types.dart'; @@ -18,12 +19,14 @@ class AssetManager { this._config, { ActivationManager? activationManager, }) : _assetHistory = AssetHistoryStorage(), + _customTokenHistory = CustomAssetHistoryStorage(), _activationManager = activationManager ?? ActivationManager(_client); final ApiClient _client; final KomodoDefiLocalAuth _auth; final KomodoDefiSdkConfig _config; final AssetHistoryStorage _assetHistory; + final CustomAssetHistoryStorage _customTokenHistory; final ActivationManager _activationManager; final Map> _activationCompleters = {}; @@ -99,6 +102,13 @@ class AssetManager { final user = await _auth.currentUser; if (user != null) { await _assetHistory.addAssetToWallet(user.walletId, asset.id.id); + if (asset.protocol.isCustomToken) { + _orderedCoins[asset.id] = asset; + await _customTokenHistory.addAssetToWallet( + user.walletId, + asset, + ); + } } if (!completer.isCompleted) { completer.complete(); @@ -148,6 +158,10 @@ class AssetManager { .where((asset) => !_activeAssetIds.contains(asset.id)); assetsToActivate.addAll(assets); } + + final customTokens = + await _customTokenHistory.getWalletAssets(user.walletId); + assetsToActivate.addAll(customTokens); } final validAssets = assetsToActivate @@ -194,11 +208,9 @@ class AssetManager { await _handlePreActivation(user); } - // bool isAssetActive(AssetId assetId) => _activeAssetIds.contains(assetId); - + /// Fetches the list of enabled coins from KDF. + /// Note: user must be authenticated to perform this operation. Future> _getEnabledCoins() async { - if (!await _auth.isSignedIn()) return {}; - final enabled = await _client.rpc.generalActivation.getEnabledCoins(); return enabled.result.map((e) => e.ticker).toSet(); } diff --git a/packages/komodo_defi_sdk/lib/src/assets/custom_asset_history_storage.dart b/packages/komodo_defi_sdk/lib/src/assets/custom_asset_history_storage.dart new file mode 100644 index 00000000..a4dab610 --- /dev/null +++ b/packages/komodo_defi_sdk/lib/src/assets/custom_asset_history_storage.dart @@ -0,0 +1,47 @@ +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:komodo_defi_types/komodo_defi_types.dart'; + +/// Custom token asset history storage for tokens not present in the live coins +/// configuration. +class CustomAssetHistoryStorage { + static const _storagePrefix = 'wallet_custom_assets_'; + final _storage = const FlutterSecureStorage(); + + /// Store custom tokens used by a wallet + Future storeWalletAssets( + WalletId walletId, + Set assets, + ) async { + final key = _getStorageKey(walletId); + final assetsJsonArray = assets.map((asset) => asset.toJson()).toList(); + await _storage.write( + key: key, + value: assetsJsonArray.toJsonString(), + ); + } + + /// Add a single asset to wallet's history + Future addAssetToWallet(WalletId walletId, Asset asset) async { + final assets = await getWalletAssets(walletId); + assets.add(asset); + await storeWalletAssets(walletId, assets); + } + + /// Get all assets previously used by a wallet + Future> getWalletAssets(WalletId walletId) async { + final key = _getStorageKey(walletId); + final value = await _storage.read(key: key); + if (value == null || value.isEmpty) return {}; + final assetsJsonArray = jsonListFromString(value); + return assetsJsonArray.map(Asset.fromJson).toSet(); + } + + /// Clear wallet's custom token history + Future clearWalletAssets(WalletId walletId) async { + final key = _getStorageKey(walletId); + await _storage.delete(key: key); + } + + String _getStorageKey(WalletId walletId) => + '$_storagePrefix${walletId.pubkeyHash ?? walletId.name}'; +} diff --git a/packages/komodo_defi_types/lib/src/assets/asset.dart b/packages/komodo_defi_types/lib/src/assets/asset.dart index 81f5c25a..57f0f205 100644 --- a/packages/komodo_defi_types/lib/src/assets/asset.dart +++ b/packages/komodo_defi_types/lib/src/assets/asset.dart @@ -18,6 +18,12 @@ class Asset extends Equatable { return Asset(id: assetId, protocol: protocol); } + factory Asset.fromJson(JsonMap json) { + final assetId = AssetId.parse(json, knownIds: const {}); + final protocol = ProtocolClass.fromJson(json); + return Asset(id: assetId, protocol: protocol); + } + /// Creates a variant of this asset with a different protocol type Asset? createVariant(CoinSubClass protocolType) { if (!protocol.supportsProtocolType(protocolType)) return null; @@ -39,13 +45,14 @@ class Asset extends Equatable { .toSet(); } - // /// Gets the appropriate activation strategy for this asset - // ActivationStrategy get activationStrategy => - // ActivationStrategyFactory.createForAsset(this); - final AssetId id; final ProtocolClass protocol; + JsonMap toJson() => { + ...protocol.toJson(), + ...id.toJson(), + }; + @override List get props => [id, protocol]; 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 1e2c0d48..339ff619 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 @@ -1,10 +1,11 @@ +import 'package:equatable/equatable.dart'; import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; import 'package:komodo_defi_types/komodo_defi_types.dart'; import 'package:komodo_defi_types/src/utils/json_type_utils.dart'; /// Base class for all protocol definitions -abstract class ProtocolClass with ExplorerUrlMixin { +abstract class ProtocolClass with ExplorerUrlMixin implements Equatable { const ProtocolClass({ required this.subClass, required this.config, @@ -113,6 +114,8 @@ abstract class ProtocolClass with ExplorerUrlMixin { /// Convert protocol back to JSON representation JsonMap toJson() => { ...config, + 'sub_class': subClass.toString().split('.').last, + 'is_custom_token': isCustomToken, if (supportedProtocols.isNotEmpty) 'other_types': supportedProtocols .map((p) => p.toString().split('.').last) @@ -136,4 +139,17 @@ abstract class ProtocolClass with ExplorerUrlMixin { ActivationParams defaultActivationParams() => ActivationParams.fromConfigJson(config); + + @override + List get props => [ + subClass, + supportedProtocols, + isCustomToken, + requiresHdWallet, + derivationPath, + isTestnet, + ]; + + @override + bool? get stringify => false; } diff --git a/packages/komodo_defi_types/lib/src/protocols/erc20/erc20_protocol.dart b/packages/komodo_defi_types/lib/src/protocols/erc20/erc20_protocol.dart index 80655f67..a8410c83 100644 --- a/packages/komodo_defi_types/lib/src/protocols/erc20/erc20_protocol.dart +++ b/packages/komodo_defi_types/lib/src/protocols/erc20/erc20_protocol.dart @@ -13,6 +13,7 @@ class Erc20Protocol extends ProtocolClass { _validateErc20Config(json); return Erc20Protocol._( subClass: CoinSubClass.parse(json.value('type')), + isCustomToken: json.valueOrNull('is_custom_token') ?? false, config: json, ); } diff --git a/packages/komodo_defi_types/lib/src/transactions/transaction.dart b/packages/komodo_defi_types/lib/src/transactions/transaction.dart index 974104f3..10c4c19d 100644 --- a/packages/komodo_defi_types/lib/src/transactions/transaction.dart +++ b/packages/komodo_defi_types/lib/src/transactions/transaction.dart @@ -92,6 +92,35 @@ class Transaction extends Equatable { if (fee != null) 'fee': fee!.toJson(), if (memo != null) 'memo': memo, }; + + Transaction copyWith({ + String? id, + String? internalId, + AssetId? assetId, + BalanceChanges? balanceChanges, + DateTime? timestamp, + int? confirmations, + int? blockHeight, + List? from, + List? to, + String? txHash, + FeeInfo? fee, + String? memo, + }) => + Transaction( + id: id ?? this.id, + internalId: internalId ?? this.internalId, + assetId: assetId ?? this.assetId, + balanceChanges: balanceChanges ?? this.balanceChanges, + timestamp: timestamp ?? this.timestamp, + confirmations: confirmations ?? this.confirmations, + blockHeight: blockHeight ?? this.blockHeight, + from: from ?? this.from, + to: to ?? this.to, + txHash: txHash ?? this.txHash, + fee: fee ?? this.fee, + memo: memo ?? this.memo, + ); } extension TransactionInfoExtension on TransactionInfo { From a76f7083e6fa46e17635435e56aef0ab2c16f182 Mon Sep 17 00:00:00 2001 From: Francois Date: Mon, 3 Feb 2025 13:32:38 +0200 Subject: [PATCH 08/15] fix: add missing imports and remove duplicate RPCs --- .../rpc_methods/eth/enable_custom_erc20.dart | 4 +- .../rpc_methods/utility/get_token_info.dart | 2 +- .../rpc_methods/wallet/convert_address.dart | 58 ------------------- .../rpc_methods/wallet/validate_address.dart | 56 ------------------ .../lib/src/rpc_methods_library.dart | 27 --------- .../custom_erc20_activation_strategy.dart | 1 + .../assets/custom_asset_history_storage.dart | 1 + 7 files changed, 5 insertions(+), 144 deletions(-) delete mode 100644 packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/convert_address.dart delete mode 100644 packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/validate_address.dart diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/eth/enable_custom_erc20.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/eth/enable_custom_erc20.dart index e7632b62..c15d116e 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/eth/enable_custom_erc20.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/eth/enable_custom_erc20.dart @@ -1,5 +1,5 @@ import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; -import 'package:komodo_defi_types/komodo_defi_types.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; class EnableCustomErc20TokenRequest extends BaseRequest @@ -31,7 +31,7 @@ class EnableCustomErc20TokenRequest return super.toJson().deepMerge({ 'params': { 'ticker': ticker, - 'activation_params': activationParams.toJsonRequestParams(), + 'activation_params': activationParams.toRpcParams(), 'protocol': { 'type': 'ERC20', 'protocol_data': { diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/utility/get_token_info.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/utility/get_token_info.dart index 03239484..e1c7fcdd 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/utility/get_token_info.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/utility/get_token_info.dart @@ -1,5 +1,5 @@ import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; -import 'package:komodo_defi_types/komodo_defi_types.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; /// Request to get the ticker and decimals values required for custom token /// activation, given a platform and contract as input diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/convert_address.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/convert_address.dart deleted file mode 100644 index c6b4ccea..00000000 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/convert_address.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; -import 'package:komodo_defi_types/komodo_defi_types.dart'; - -class ConvertAddressRequest - extends BaseRequest - with RequestHandlingMixin { - ConvertAddressRequest({ - required String rpcPass, - required this.fromAddress, - required this.coinSubClass, - this.isBchNetwork = false, - }) : super(method: 'convertaddress', rpcPass: rpcPass, mmrpc: null); - - final String fromAddress; - final CoinSubClass coinSubClass; - final bool isBchNetwork; - - @override - Map toJson() { - return super.toJson().deepMerge({ - 'from': fromAddress, - 'coin': coinSubClass.ticker, - 'to_address_format': AddressFormat.fromCoinSubClass( - coinSubClass, - isBchNetwork: isBchNetwork, - ).toJson(), - }); - } - - @override - ConvertAddressResponse parse(Map json) { - return ConvertAddressResponse.parse(json); - } -} - -class ConvertAddressResponse extends BaseResponse { - ConvertAddressResponse({ - required super.mmrpc, - required this.address, - }); - - factory ConvertAddressResponse.parse(Map json) { - return ConvertAddressResponse( - mmrpc: json.valueOrNull('mmrpc'), - address: json.value('result', 'address'), - ); - } - - final String address; - - @override - Map toJson() { - return { - 'mmrpc': mmrpc, - 'address': address, - }; - } -} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/validate_address.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/validate_address.dart deleted file mode 100644 index f4c19ff4..00000000 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/wallet/validate_address.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; -import 'package:komodo_defi_types/komodo_defi_types.dart'; - -class ValidateAddressRequest - extends BaseRequest - with RequestHandlingMixin { - ValidateAddressRequest({ - required this.coin, - required this.address, - required String rpcPass, - }) : super(method: 'validateaddress', rpcPass: rpcPass, mmrpc: null); - - final String coin; - final String address; - - @override - Map toJson() { - return super.toJson().deepMerge({ - 'coin': coin, - 'address': address, - }); - } - - @override - ValidateAddressResponse parse(Map json) { - return ValidateAddressResponse.parse(json); - } -} - -class ValidateAddressResponse extends BaseResponse { - ValidateAddressResponse({ - required super.mmrpc, - required this.isValid, - this.reason, - }); - - factory ValidateAddressResponse.parse(Map json) { - return ValidateAddressResponse( - mmrpc: json.valueOrNull('mmrpc'), - isValid: json.value('is_valid'), - reason: json.valueOrNull('reason'), - ); - } - - final bool isValid; - final String? reason; - - @override - Map toJson() { - return { - 'mmrpc': mmrpc, - 'is_valid': isValid, - if (reason != null) 'reason': reason, - }; - } -} diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods_library.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods_library.dart index 4578e403..6d7d843c 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods_library.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods_library.dart @@ -5,8 +5,6 @@ import 'package:komodo_defi_rpc_methods/src/internal_exports.dart'; import 'package:komodo_defi_rpc_methods/src/rpc_methods/utility/get_token_info.dart'; -import 'package:komodo_defi_rpc_methods/src/rpc_methods/wallet/convert_address.dart'; -import 'package:komodo_defi_rpc_methods/src/rpc_methods/wallet/validate_address.dart'; import 'package:komodo_defi_types/komodo_defi_types.dart'; /// A class that provides a library of RPC methods used by the Komodo DeFi @@ -75,31 +73,6 @@ class WalletMethods extends BaseRpcMethodNamespace { Future getPublicKeyHash([String? rpcPass]) => execute(GetPublicKeyHashRequest(rpcPass: rpcPass)); - - Future convertAddress({ - required String fromAddress, - required CoinSubClass coinSubClass, - String? rpcPass, - }) => - execute( - ConvertAddressRequest( - rpcPass: rpcPass ?? '', - fromAddress: fromAddress, - coinSubClass: coinSubClass, - ), - ); - - Future validateAddress({ - required String assetId, - required String address, - }) => - execute( - ValidateAddressRequest( - coin: assetId, - address: address, - rpcPass: rpcPass ?? '', - ), - ); } /// KDF v2 Utility Methods not specific to any larger feature diff --git a/packages/komodo_defi_sdk/lib/src/activation/protocol_strategies/custom_erc20_activation_strategy.dart b/packages/komodo_defi_sdk/lib/src/activation/protocol_strategies/custom_erc20_activation_strategy.dart index 5b288831..d4c25364 100644 --- a/packages/komodo_defi_sdk/lib/src/activation/protocol_strategies/custom_erc20_activation_strategy.dart +++ b/packages/komodo_defi_sdk/lib/src/activation/protocol_strategies/custom_erc20_activation_strategy.dart @@ -1,5 +1,6 @@ import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'; import 'package:komodo_defi_sdk/src/activation/_activation.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; import 'package:komodo_defi_types/komodo_defi_types.dart'; /// Activation strategy for custom ERC20 tokens. This strategy is used to diff --git a/packages/komodo_defi_sdk/lib/src/assets/custom_asset_history_storage.dart b/packages/komodo_defi_sdk/lib/src/assets/custom_asset_history_storage.dart index a4dab610..cfd58375 100644 --- a/packages/komodo_defi_sdk/lib/src/assets/custom_asset_history_storage.dart +++ b/packages/komodo_defi_sdk/lib/src/assets/custom_asset_history_storage.dart @@ -1,4 +1,5 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; import 'package:komodo_defi_types/komodo_defi_types.dart'; /// Custom token asset history storage for tokens not present in the live coins From 819fc623e0fc24b85f08e9e88a002766d9c05e96 Mon Sep 17 00:00:00 2001 From: Francois Date: Mon, 3 Feb 2025 13:33:21 +0200 Subject: [PATCH 09/15] fix(rpc-methods): change convertaddress RPC to legacy format --- .../src/rpc_methods/address/convertaddress.dart | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/address/convertaddress.dart b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/address/convertaddress.dart index 25d4bc96..ec18a48d 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/address/convertaddress.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/rpc_methods/address/convertaddress.dart @@ -9,10 +9,7 @@ class ConvertAddressRequest required this.coin, required this.fromAddress, required this.toAddressFormat, - }) : super( - method: 'convertaddress', - mmrpc: '2.0', - ); + }) : super(method: 'convertaddress', mmrpc: null); final String coin; final String fromAddress; @@ -21,11 +18,9 @@ class ConvertAddressRequest @override Map toJson() => { ...super.toJson(), - 'params': { - 'coin': coin, - 'from': fromAddress, - 'to_address_format': toAddressFormat.toJson(), - }, + 'coin': coin, + 'from': fromAddress, + 'to_address_format': toAddressFormat.toJson(), }; @override @@ -41,7 +36,7 @@ class ConvertAddressResponse extends BaseResponse { factory ConvertAddressResponse.parse(Map json) { return ConvertAddressResponse( - mmrpc: json.value('mmrpc'), + mmrpc: json.valueOrNull('mmrpc'), address: json.value('result', 'address'), ); } From c4c73373e2d6a0059dfb91c46fded0f01911918f Mon Sep 17 00:00:00 2001 From: Francois Date: Mon, 3 Feb 2025 18:26:59 +0200 Subject: [PATCH 10/15] fix(asset-manager): update indexes with custom tokens --- .../lib/src/assets/asset_manager.dart | 19 +++++++------------ .../assets/custom_asset_history_storage.dart | 4 ++++ .../src/assets/legacy_asset_extensions.dart | 4 ++-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart b/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart index 5c2066cd..80b19290 100644 --- a/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart +++ b/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart @@ -53,7 +53,7 @@ class AssetManager { _orderedCoins.addAll(_coins.all); - initTickerIndex(); + await initTickerIndex(); final currentUser = await _auth.currentUser; await _onAuthStateChanged(currentUser); @@ -104,6 +104,7 @@ class AssetManager { await _assetHistory.addAssetToWallet(user.walletId, asset.id.id); if (asset.protocol.isCustomToken) { _orderedCoins[asset.id] = asset; + await updateIndex(asset); await _customTokenHistory.addAssetToWallet( user.walletId, asset, @@ -162,6 +163,10 @@ class AssetManager { final customTokens = await _customTokenHistory.getWalletAssets(user.walletId); assetsToActivate.addAll(customTokens); + for (final customToken in customTokens) { + _orderedCoins[customToken.id] = customToken; + await updateIndex(customToken); + } } final validAssets = assetsToActivate @@ -208,7 +213,7 @@ class AssetManager { await _handlePreActivation(user); } - /// Fetches the list of enabled coins from KDF. + /// Fetches the list of enabled coins from KDF. /// Note: user must be authenticated to perform this operation. Future> _getEnabledCoins() async { final enabled = await _client.rpc.generalActivation.getEnabledCoins(); @@ -232,13 +237,3 @@ class AssetManager { _authSubscription?.cancel(); } } - -class _AssetGroup { - _AssetGroup({ - required this.primary, - required this.children, - }); - - final Asset primary; - final List children; -} diff --git a/packages/komodo_defi_sdk/lib/src/assets/custom_asset_history_storage.dart b/packages/komodo_defi_sdk/lib/src/assets/custom_asset_history_storage.dart index cfd58375..cf89466b 100644 --- a/packages/komodo_defi_sdk/lib/src/assets/custom_asset_history_storage.dart +++ b/packages/komodo_defi_sdk/lib/src/assets/custom_asset_history_storage.dart @@ -24,6 +24,10 @@ class CustomAssetHistoryStorage { /// Add a single asset to wallet's history Future addAssetToWallet(WalletId walletId, Asset asset) async { final assets = await getWalletAssets(walletId); + // Equatable operators not working as expected, so we need to check manually + if (assets.any((historicalAsset) => historicalAsset.id.id == asset.id.id)) { + return; + } assets.add(asset); await storeWalletAssets(walletId, assets); } diff --git a/packages/komodo_defi_sdk/lib/src/assets/legacy_asset_extensions.dart b/packages/komodo_defi_sdk/lib/src/assets/legacy_asset_extensions.dart index 65658ff0..c179bd30 100644 --- a/packages/komodo_defi_sdk/lib/src/assets/legacy_asset_extensions.dart +++ b/packages/komodo_defi_sdk/lib/src/assets/legacy_asset_extensions.dart @@ -91,9 +91,9 @@ extension AssetTickerIndexExtension on AssetManager { _isInitialized = false; } - // Internal methods for maintaining the index + /// Internal methods for maintaining the index // ignore: unused_element - Future _updateIndex(Asset asset, {bool remove = false}) async { + Future updateIndex(Asset asset, {bool remove = false}) async { if (!_isInitialized) return; await _lock.protect(() async { _updateTickerIndex(asset, remove: remove); From 75b7d5cb6e4053782bef90163f172cf9457a9d9c Mon Sep 17 00:00:00 2001 From: Francois Date: Wed, 5 Feb 2025 11:05:27 +0200 Subject: [PATCH 11/15] fix(asset-manager): add missing ticker index initialization fixes runtime error in KW where wallet page fails to load due to ticker index not being initialized --- packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart b/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart index 3195250a..c35f28dc 100644 --- a/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart +++ b/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart @@ -101,6 +101,8 @@ class AssetManager implements IAssetProvider { _orderedCoins.addAll(_coins.all); + await initTickerIndex(); + final currentUser = await _auth.currentUser; await _onAuthStateChanged(currentUser); From 5ee930561a8a2b3dd9f096a117b71eb30a75138e Mon Sep 17 00:00:00 2001 From: Francois Date: Wed, 5 Feb 2025 11:41:16 +0200 Subject: [PATCH 12/15] fix(asset-manager): use factory to create activation strategy the custom token erc20 activation strategy was placed in the factory which had it's reference removed in the latest changes or merge from dev --- .../lib/src/activation/activation_manager.dart | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/packages/komodo_defi_sdk/lib/src/activation/activation_manager.dart b/packages/komodo_defi_sdk/lib/src/activation/activation_manager.dart index 3c55b3ca..a3b959f4 100644 --- a/packages/komodo_defi_sdk/lib/src/activation/activation_manager.dart +++ b/packages/komodo_defi_sdk/lib/src/activation/activation_manager.dart @@ -16,19 +16,7 @@ class ActivationManager { this._assetHistory, this._customTokenHistory, this._assetLookup, - ) : _activator = SmartAssetActivator( - _client, - CompositeAssetActivator( - _client, - [ - UtxoActivationStrategy(_client), - Erc20ActivationStrategy(_client), - TendermintActivationStrategy(_client), - QtumActivationStrategy(_client), - ZhtlcActivationStrategy(_client), - ], - ), - ); + ) : _activator = ActivationStrategyFactory.createStrategy(_client); final ApiClient _client; final KomodoDefiLocalAuth _auth; From c17034cd303fa6060d41f4abcea93a9789fde3d5 Mon Sep 17 00:00:00 2001 From: Francois Date: Wed, 5 Feb 2025 16:13:29 +0200 Subject: [PATCH 13/15] feat(asset-icon): migrate custom token logic from KW --- .../lib/src/defi/asset/asset_icon.dart | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/packages/komodo_ui/lib/src/defi/asset/asset_icon.dart b/packages/komodo_ui/lib/src/defi/asset/asset_icon.dart index b1b89cff..a96db269 100644 --- a/packages/komodo_ui/lib/src/defi/asset/asset_icon.dart +++ b/packages/komodo_ui/lib/src/defi/asset/asset_icon.dart @@ -58,6 +58,29 @@ class AssetIcon extends StatelessWidget { _AssetIconResolver.clearCaches(); } + /// Registers a custom icon for a given coin abbreviation. + /// + /// The [imageProvider] will be used instead of the default asset or CDN images + /// when displaying the icon for the specified [assetId]. + /// + /// Example: + /// ```dart + /// // Register a custom icon from an asset + /// CoinIcon.registerCustomIcon( + /// 'MYCOIN', + /// AssetImage('assets/my_custom_coin.png'), + /// ); + /// + /// // Register a custom icon from memory + /// CoinIcon.registerCustomIcon( + /// 'MYCOIN', + /// MemoryImage(customIconBytes), + /// ); + /// ``` + static void registerCustomIcon(AssetId assetId, ImageProvider imageProvider) { + _AssetIconResolver.registerCustomIcon(assetId, imageProvider); + } + /// Pre-loads the asset icon image into the cache. /// /// This is useful when you know you'll need an icon soon and want to avoid @@ -93,10 +116,16 @@ class _AssetIconResolver extends StatelessWidget { static final Map _assetExistenceCache = {}; static final Map _cdnExistenceCache = {}; + static final Map _customIconsCache = {}; + + static void registerCustomIcon(AssetId assetId, ImageProvider imageProvider) { + _customIconsCache[assetId.symbol.configSymbol] = imageProvider; + } static void clearCaches() { _assetExistenceCache.clear(); _cdnExistenceCache.clear(); + _customIconsCache.clear(); } String get _sanitizedId => @@ -113,6 +142,23 @@ class _AssetIconResolver extends StatelessWidget { final sanitizedId = resolver._sanitizedId; try { + if (_customIconsCache.containsKey(asset.symbol.configSymbol)) { + if (context.mounted) { + await precacheImage( + _customIconsCache[asset.symbol.configSymbol]!, + context, + onError: (e, stackTrace) { + if (throwExceptions) { + throw Exception( + 'Failed to pre-cache custom image for coin $asset: $e', + ); + } + }, + ); + } + return; + } + bool? assetExists; bool? cdnExists; @@ -159,6 +205,17 @@ class _AssetIconResolver extends StatelessWidget { @override Widget build(BuildContext context) { + if (_customIconsCache.containsKey(_sanitizedId)) { + return Image( + image: _customIconsCache[_sanitizedId]!, + filterQuality: FilterQuality.high, + errorBuilder: (context, error, stackTrace) { + debugPrint('Error loading custom icon for $assetId: $error'); + return Icon(Icons.monetization_on_outlined, size: size); + }, + ); + } + _assetExistenceCache[_imagePath] = true; return Image.asset( _imagePath, From 988da092638234ec9b57f4748517945b70674d92 Mon Sep 17 00:00:00 2001 From: Francois Date: Thu, 6 Feb 2025 15:35:40 +0200 Subject: [PATCH 14/15] feat: add config parameter for custom token pre-activation --- .../lib/src/assets/asset_manager.dart | 18 ++++++++++-------- .../lib/src/sdk/komodo_defi_sdk_config.dart | 7 +++++++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart b/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart index c35f28dc..596770d9 100644 --- a/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart +++ b/packages/komodo_defi_sdk/lib/src/assets/asset_manager.dart @@ -174,6 +174,16 @@ class AssetManager implements IAssetProvider { Future _handlePreActivation(KdfUser user) async { final assetsToActivate = {}; + if (_config.preActivateCustomTokenAssets) { + final customTokens = + await _customAssetHistory.getWalletAssets(user.walletId); + assetsToActivate.addAll(customTokens); + for (final customToken in customTokens) { + _orderedCoins[customToken.id] = customToken; + await updateIndex(customToken); + } + } + if (_config.preActivateDefaultAssets) { for (final ticker in _config.defaultAssets) { final assets = findAssetsByTicker(ticker) @@ -189,14 +199,6 @@ class AssetManager implements IAssetProvider { .where((asset) => !_activeAssetIds.contains(asset.id)); assetsToActivate.addAll(assets); } - - final customTokens = - await _customAssetHistory.getWalletAssets(user.walletId); - assetsToActivate.addAll(customTokens); - for (final customToken in customTokens) { - _orderedCoins[customToken.id] = customToken; - await updateIndex(customToken); - } } final validAssets = assetsToActivate diff --git a/packages/komodo_defi_sdk/lib/src/sdk/komodo_defi_sdk_config.dart b/packages/komodo_defi_sdk/lib/src/sdk/komodo_defi_sdk_config.dart index a7bd4d27..19e0a349 100644 --- a/packages/komodo_defi_sdk/lib/src/sdk/komodo_defi_sdk_config.dart +++ b/packages/komodo_defi_sdk/lib/src/sdk/komodo_defi_sdk_config.dart @@ -4,6 +4,7 @@ class KomodoDefiSdkConfig { this.defaultAssets = const {'KMD', 'BTC', 'ETH', 'DOC', 'MARTY'}, this.preActivateDefaultAssets = true, this.preActivateHistoricalAssets = true, + this.preActivateCustomTokenAssets = true, this.maxPreActivationAttempts = 3, this.activationRetryDelay = const Duration(seconds: 2), }); @@ -17,6 +18,9 @@ class KomodoDefiSdkConfig { /// Whether to automatically activate previously used assets on login final bool preActivateHistoricalAssets; + /// Whether to automatically activate custom tokens on login + final bool preActivateCustomTokenAssets; + /// Maximum number of retry attempts for pre-activation final int maxPreActivationAttempts; @@ -27,6 +31,7 @@ class KomodoDefiSdkConfig { Set? defaultAssets, bool? preActivateDefaultAssets, bool? preActivateHistoricalAssets, + bool? preActivateCustomTokenAssets, int? maxPreActivationAttempts, Duration? activationRetryDelay, }) { @@ -36,6 +41,8 @@ class KomodoDefiSdkConfig { preActivateDefaultAssets ?? this.preActivateDefaultAssets, preActivateHistoricalAssets: preActivateHistoricalAssets ?? this.preActivateHistoricalAssets, + preActivateCustomTokenAssets: + preActivateCustomTokenAssets ?? this.preActivateCustomTokenAssets, maxPreActivationAttempts: maxPreActivationAttempts ?? this.maxPreActivationAttempts, activationRetryDelay: activationRetryDelay ?? this.activationRetryDelay, From f91cf60f5eb5d905f25cac2f144fd851fc808384 Mon Sep 17 00:00:00 2001 From: Francois Date: Mon, 10 Feb 2025 22:43:30 +0200 Subject: [PATCH 15/15] refactor: rename AddressFormatFormat to AddressFormatType --- .../general/address_format.dart | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/komodo_defi_rpc_methods/lib/src/common_structures/general/address_format.dart b/packages/komodo_defi_rpc_methods/lib/src/common_structures/general/address_format.dart index 42181be8..258f71cf 100644 --- a/packages/komodo_defi_rpc_methods/lib/src/common_structures/general/address_format.dart +++ b/packages/komodo_defi_rpc_methods/lib/src/common_structures/general/address_format.dart @@ -14,12 +14,12 @@ class AddressFormat { case CoinSubClass.erc20: case CoinSubClass.ethereumClassic: return AddressFormat( - format: AddressFormatFormat.mixedCase.toString(), + format: AddressFormatType.mixedCase.toString(), network: '', ); case CoinSubClass.qrc20: return AddressFormat( - format: AddressFormatFormat.contract.toString(), + format: AddressFormatType.contract.toString(), network: '', ); case CoinSubClass.utxo: @@ -29,7 +29,7 @@ class AddressFormat { // ignore: no_default_cases default: return AddressFormat( - format: AddressFormatFormat.cashAddress.toString(), + format: AddressFormatType.cashAddress.toString(), // Only set network for BCH coins network: isBchNetwork ? AddressFormatNetwork.bitcoinCash.toString() : '', @@ -46,8 +46,8 @@ class AddressFormat { }; } -/// [AddressFormat] format field options. -enum AddressFormatFormat { +/// The address format to which the input address should be converted. +enum AddressFormatType { /// Use for ETH, ERC20 coins mixedCase, @@ -66,21 +66,21 @@ enum AddressFormatFormat { @override String toString() { switch (this) { - case AddressFormatFormat.mixedCase: + case AddressFormatType.mixedCase: return 'mixedcase'; - case AddressFormatFormat.cashAddress: + case AddressFormatType.cashAddress: return 'cashaddress'; - case AddressFormatFormat.standard: + case AddressFormatType.standard: return 'standard'; - case AddressFormatFormat.contract: + case AddressFormatType.contract: return 'contract'; - case AddressFormatFormat.wallet: + case AddressFormatType.wallet: return 'wallet'; } } } -/// [AddressFormat] network prefix for [AddressFormatFormat.cashAddress] +/// [AddressFormat] network prefix for [AddressFormatType.cashAddress] /// format. Used only for UTXO coins, specifically BCH at the moment. enum AddressFormatNetwork { /// BCH main network (mainnet)