Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(nft): add nft v2 and hd wallet (single, swap address) support #2566

Draft
wants to merge 3 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions lib/app_config/app_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ Map<String, int> priorityCoinsAbbrMap = {
'MOVR': 10,
};

/// List of coins that are excluded from the list of coins displayed on the
/// coin lists (e.g. wallet page, coin selection dropdowns, etc.)
const List<String> excludedAssetList = [
'ADEXBSCT',
'ADEXBSC',
Expand All @@ -74,9 +76,8 @@ const List<String> excludedAssetList = [
'RICK',
'MORTY',

// NFT v2 coins: https://github.com/KomodoPlatform/coins/pull/1061
// NFT upgrade is not merged yet, and the coins will likely be used in the
// background, so users do not need to see them.
// NFT v2 coins: https://github.com/KomodoPlatform/coins/pull/1061 will be
// used in the background, so users do not need to see them.
'NFT_ETH',
'NFT_AVAX',
'NFT_BNB',
Expand Down Expand Up @@ -109,6 +110,8 @@ const List<String> appWalletOnlyAssetList = [
'SUPERNET',
];

/// Coins that are enabled by default on restore from seed or registration.
/// This will not affect existing wallets.
List<String> get enabledByDefaultCoins => [
'BTC-segwit',
'KMD',
Expand All @@ -120,6 +123,13 @@ List<String> get enabledByDefaultCoins => [
'FTM',
if (kDebugMode) 'DOC',
if (kDebugMode) 'MARTY',

// NFT v2 methods require the new NFT coins to be enabled by default.
'NFT_ETH',
'NFT_AVAX',
'NFT_BNB',
'NFT_FTM',
'NFT_MATIC',
];

List<String> get enabledByDefaultTrezorCoins => [
Expand Down
7 changes: 2 additions & 5 deletions lib/bloc/app_bloc_root.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ import 'package:web_dex/blocs/trezor_coins_bloc.dart';
import 'package:web_dex/blocs/wallets_repository.dart';
import 'package:web_dex/main.dart';
import 'package:web_dex/mm2/mm2_api/mm2_api.dart';
import 'package:web_dex/model/authorize_mode.dart';
import 'package:web_dex/model/main_menu_value.dart';
import 'package:web_dex/model/stored_settings.dart';
import 'package:web_dex/router/navigators/app_router_delegate.dart';
Expand All @@ -76,7 +75,7 @@ class AppBlocRoot extends StatelessWidget {
final KomodoDefiSdk komodoDefiSdk;

// TODO: Refactor to clean up the bloat in this main file
void _clearCachesIfPerformanceModeChanged(
Future<void> _clearCachesIfPerformanceModeChanged(
PerformanceMode? performanceMode,
ProfitLossRepository profitLossRepo,
PortfolioGrowthRepository portfolioGrowthRepo,
Expand Down Expand Up @@ -265,9 +264,7 @@ class AppBlocRoot extends StatelessWidget {
lazy: false,
create: (context) => NftMainBloc(
repo: context.read<NftsRepo>(),
kdfSdk: komodoDefiSdk,
isLoggedIn:
context.read<AuthBloc>().state.mode == AuthorizeMode.logIn,
sdk: komodoDefiSdk,
),
),
if (isBitrefillIntegrationEnabled)
Expand Down
1 change: 1 addition & 0 deletions lib/bloc/coins_bloc/coins_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:bloc_concurrency/bloc_concurrency.dart';
import 'package:equatable/equatable.dart';
import 'package:komodo_defi_sdk/komodo_defi_sdk.dart';
import 'package:komodo_defi_types/komodo_defi_types.dart';
import 'package:web_dex/app_config/app_config.dart';
import 'package:web_dex/bloc/coins_bloc/coins_repo.dart';
import 'package:web_dex/blocs/trezor_coins_bloc.dart';
import 'package:web_dex/mm2/mm2_api/mm2_api.dart';
Expand Down
9 changes: 7 additions & 2 deletions lib/bloc/coins_bloc/coins_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,14 @@ class CoinsState extends Equatable {
bool? loginActivationFinished,
Map<String, AssetPubkeys>? pubkeys,
}) {
final walletCoinsWithoutExcludedCoins = walletCoins ?? this.walletCoins
..removeWhere((coinId, _) => excludedAssetList.contains(coinId));
final coinsWithoutExcludedCoins = coins ?? this.coins
..removeWhere((coinId, _) => excludedAssetList.contains(coinId));

return CoinsState(
coins: coins ?? this.coins,
walletCoins: walletCoins ?? this.walletCoins,
coins: coinsWithoutExcludedCoins,
walletCoins: walletCoinsWithoutExcludedCoins,
loginActivationFinished:
loginActivationFinished ?? this.loginActivationFinished,
pubkeys: pubkeys ?? this.pubkeys,
Expand Down
27 changes: 15 additions & 12 deletions lib/bloc/nft_transactions/nft_txn_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,22 @@ import 'package:web_dex/model/withdraw_details/fee_details.dart';
import 'package:web_dex/shared/utils/utils.dart';

class NftTxnRepository {
final Mm2ApiNft _api;
final CoinsRepo _coinsRepo;
final Map<String, double?> _abbrToUsdPrices = {};

NftTxnRepository({required Mm2ApiNft api, required CoinsRepo coinsRepo})
: _api = api,
_coinsRepo = coinsRepo;
final Mm2ApiNft _api;
final CoinsRepo _coinsRepo;
final Map<String, double?> _abbrToUsdPrices = {};
Map<String, double?> get abbrToUsdPrices => _abbrToUsdPrices;

Future<NftTxsResponse> getNftTransactions(
[List<NftBlockchains>? chains]) async {
Future<NftTxsResponse> getNftTransactions([
List<NftBlockchains>? chains,
]) async {
final List<String> allChains =
(chains ?? NftBlockchains.values).map((e) => e.toApiRequest()).toList();
await getUsdPricesOfCoins(
(chains ?? NftBlockchains.values).map((e) => e.coinAbbr()));
(chains ?? NftBlockchains.values).map((e) => e.coinAbbr()),
);
final request = NftTransactionsRequest(
chains: allChains,
max: true,
Expand All @@ -34,11 +35,11 @@ class NftTxnRepository {
final json = await _api.getNftTxs(request, false);
if (json['error'] != null) {
log(
json['error'],
json['error'] as String,
path: 'nft_main_repo => getNfts',
isError: true,
);
throw ApiError(message: json['error']);
).ignore();
throw ApiError(message: json['error'] as String);
}

if (json['result'] == null) {
Expand Down Expand Up @@ -68,7 +69,9 @@ class NftTxnRepository {
}) async {
try {
final request = NftTxDetailsRequest(
chain: tx.chain.toApiRequest(), txHash: tx.transactionHash);
chain: tx.chain.toApiRequest(),
txHash: tx.transactionHash,
);
final json = await _api.getNftTxDetails(request);
try {
tx.confirmations = json['confirmations'] ?? 0;
Expand All @@ -94,7 +97,7 @@ class NftTxnRepository {

Future<void> getUsdPricesOfCoins(Iterable<String> coinAbbr) async {
final coins = _coinsRepo.getKnownCoins();
for (var abbr in coinAbbr) {
for (final abbr in coinAbbr) {
final coin = coins.firstWhere((c) => c.abbr == abbr);
_abbrToUsdPrices[abbr] = coin.usdPrice?.price;
}
Expand Down
16 changes: 11 additions & 5 deletions lib/bloc/nft_withdraw/nft_withdraw_repo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ class NftWithdrawRepo {
);
final Map<String, dynamic> json = await _api.nft.withdraw(request);
if (json['error'] != null) {
log(json['error'] ?? 'unknown error',
path: 'nft_main_repo => getNfts', isError: true);
log(
json['error'] as String? ?? 'unknown error',
path: 'nft_main_repo => getNfts',
isError: true,
).ignore();
final BaseError error =
withdrawErrorFactory.getError(json, nft.parentCoin.abbr);
throw ApiError(message: error.message);
Expand All @@ -54,15 +57,18 @@ class NftWithdrawRepo {
}

Future<SendRawTransactionResponse> confirmSend(
String coin, String txHex) async {
String coin,
String txHex,
) async {
try {
final request = SendRawTransactionRequest(coin: coin, txHex: txHex);
final response = await _api.sendRawTransaction(request);
return response;
} catch (e) {
return SendRawTransactionResponse(
txHash: null,
error: TextError(error: LocaleKeys.somethingWrong.tr()));
txHash: null,
error: TextError(error: LocaleKeys.somethingWrong.tr()),
);
}
}

Expand Down
90 changes: 51 additions & 39 deletions lib/bloc/nfts/nft_main_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ part 'nft_main_state.dart';
class NftMainBloc extends Bloc<NftMainEvent, NftMainState> {
NftMainBloc({
required NftsRepo repo,
required KomodoDefiSdk kdfSdk,
required bool isLoggedIn,
required KomodoDefiSdk sdk,
}) : _repo = repo,
_isLoggedIn = isLoggedIn,
_sdk = sdk,
super(NftMainState.initial()) {
on<UpdateChainNftsEvent>(_onUpdateChainNfts);
on<ChangeNftTabEvent>(_onChangeTab);
Expand All @@ -27,10 +26,9 @@ class NftMainBloc extends Bloc<NftMainEvent, NftMainState> {
on<StartUpdateNftsEvent>(_onStartUpdate);
on<StopUpdateNftEvent>(_onStopUpdate);

_authorizationSubscription = kdfSdk.auth.authStateChanges.listen((event) {
_isLoggedIn = event != null;

if (_isLoggedIn) {
_authorizationSubscription = _sdk.auth.authStateChanges.listen((event) {
final isSignedIn = event != null;
if (isSignedIn) {
add(const UpdateChainNftsEvent());
} else {
add(const ResetNftPageEvent());
Expand All @@ -39,28 +37,31 @@ class NftMainBloc extends Bloc<NftMainEvent, NftMainState> {
}

final NftsRepo _repo;
final KomodoDefiSdk _sdk;
late StreamSubscription<KdfUser?> _authorizationSubscription;
Timer? _updateTimer;
bool _isLoggedIn = false;
bool get isLoggedIn => _isLoggedIn;

Future<void> _onChangeTab(
ChangeNftTabEvent event,
Emitter<NftMainState> emit,
) async {
emit(state.copyWith(selectedChain: () => event.chain));
if (!_isLoggedIn || !state.isInitialized) return;
if (!await _sdk.auth.isSignedIn() || !state.isInitialized) {
return;
}

try {
final List<NftToken> nftList = await _repo.getNfts([event.chain]);

final (newNftS, newNftCount) =
_recalculateNftsForChain(nftList, event.chain);
emit(state.copyWith(
nfts: () => newNftS,
nftCount: () => newNftCount,
error: () => null,
));
emit(
state.copyWith(
nfts: () => newNftS,
nftCount: () => newNftCount,
error: () => null,
),
);
} on BaseError catch (e) {
emit(state.copyWith(error: () => e));
} catch (e) {
Expand All @@ -72,22 +73,24 @@ class NftMainBloc extends Bloc<NftMainEvent, NftMainState> {
UpdateChainNftsEvent event,
Emitter<NftMainState> emit,
) async {
if (!_isLoggedIn) {
if (!await _sdk.auth.isSignedIn()) {
return;
}

try {
final Map<NftBlockchains, List<NftToken>> nfts = await _getAllNfts();
var (counts, sortedChains) = _calculateNftCount(nfts);

emit(state.copyWith(
nftCount: () => counts,
nfts: () => nfts,
sortedChains: () => sortedChains,
selectedChain: state.isInitialized ? null : () => sortedChains.first,
isInitialized: () => true,
error: () => null,
));
final (counts, sortedChains) = _calculateNftCount(nfts);

emit(
state.copyWith(
nftCount: () => counts,
nfts: () => nfts,
sortedChains: () => sortedChains,
selectedChain: state.isInitialized ? null : () => sortedChains.first,
isInitialized: () => true,
error: () => null,
),
);
} on BaseError catch (e) {
emit(state.copyWith(error: () => e));
} catch (e) {
Expand All @@ -102,8 +105,13 @@ class NftMainBloc extends Bloc<NftMainEvent, NftMainState> {
}

Future<void> _onRefreshForChain(
RefreshNFTsForChainEvent event, Emitter<NftMainState> emit) async {
if (!_isLoggedIn || !state.isInitialized) return;
RefreshNFTsForChainEvent event,
Emitter<NftMainState> emit,
) async {
if (!await _sdk.auth.isSignedIn() || !state.isInitialized) {
return;
}

final updatingChains = _addUpdatingChains(event.chain);
emit(state.copyWith(updatingChains: () => updatingChains));

Expand All @@ -112,11 +120,13 @@ class NftMainBloc extends Bloc<NftMainEvent, NftMainState> {

final (newNftS, newNftCount) =
_recalculateNftsForChain(nftList, event.chain);
emit(state.copyWith(
nfts: () => newNftS,
nftCount: () => newNftCount,
error: () => null,
));
emit(
state.copyWith(
nfts: () => newNftS,
nftCount: () => newNftCount,
error: () => null,
),
);
} on BaseError catch (e) {
emit(state.copyWith(error: () => e));
} catch (e) {
Expand All @@ -138,17 +148,18 @@ class NftMainBloc extends Bloc<NftMainEvent, NftMainState> {
});
}

Future<Map<NftBlockchains, List<NftToken>>> _getAllNfts() async {
const chains = NftBlockchains.values;
Future<Map<NftBlockchains, List<NftToken>>> _getAllNfts({
List<NftBlockchains> chains = NftBlockchains.values,
}) async {
await _repo.updateNft(chains);
final List<NftToken> list = await _repo.getNfts(chains);

final Map<NftBlockchains, List<NftToken>> nfts =
list.fold<Map<NftBlockchains, List<NftToken>>>(
<NftBlockchains, List<NftToken>>{},
(prev, element) {
List<NftToken> chainList = prev[element.chain] ?? [];
chainList.add(element);
final List<NftToken> chainList = prev[element.chain] ?? []
..add(element);
prev[element.chain] = chainList;

return prev;
Expand All @@ -159,10 +170,11 @@ class NftMainBloc extends Bloc<NftMainEvent, NftMainState> {
}

(Map<NftBlockchains, int>, List<NftBlockchains>) _calculateNftCount(
Map<NftBlockchains, List<NftToken>> nfts) {
Map<NftBlockchains, List<NftToken>> nfts,
) {
final Map<NftBlockchains, int> countMap = {};

for (NftBlockchains chain in NftBlockchains.values) {
for (final NftBlockchains chain in NftBlockchains.values) {
final count = nfts[chain]?.length ?? 0;
countMap[chain] = count;
}
Expand Down
Loading
Loading