Skip to content

Commit

Permalink
861, add tokenListBuilder, fetch allChains tokens from backend, refca…
Browse files Browse the repository at this point in the history
…tor tokens update system
  • Loading branch information
PseudoElement committed Dec 27, 2024
1 parent a22255a commit 3b29c4e
Show file tree
Hide file tree
Showing 8 changed files with 319 additions and 179 deletions.
5 changes: 5 additions & 0 deletions src/app/core/services/backend/tokens-api/models/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ export interface BackendToken {
type: Token['type'];
}

export interface BackendTokenForAllChains extends BackendToken {
network: BlockchainName;
network_rank: number;
}

export interface TokensBackendResponse {
readonly count: number;
readonly next: string | null;
Expand Down
11 changes: 11 additions & 0 deletions src/app/core/services/backend/tokens-api/tokens-api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Token } from '@shared/models/tokens/token';
import { catchError, map, tap } from 'rxjs/operators';
import {
BackendToken,
BackendTokenForAllChains,
DEFAULT_PAGE_SIZE,
ENDPOINTS,
FavoriteTokenRequestParams,
Expand Down Expand Up @@ -288,4 +289,14 @@ export class TokensApiService {
})
);
}

public fetchTokensListForAllChains(): Observable<List<Token>> {
return this.httpService
.get<BackendTokenForAllChains[]>(
'',
{},
'https://dev2-api.rubic.exchange/api/v2/tokens/allchains'
)
.pipe(map(backendTokens => TokensApiService.prepareTokens(backendTokens)));
}
}
110 changes: 72 additions & 38 deletions src/app/core/services/tokens/tokens-store.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, combineLatest, forkJoin, from, Observable, of } from 'rxjs';
import { BehaviorSubject, firstValueFrom, forkJoin, from, iif, Observable, of } from 'rxjs';
import { List } from 'immutable';
import { TokenAmount } from '@shared/models/tokens/token-amount';
import { catchError, debounceTime, first, map, switchMap, tap } from 'rxjs/operators';
Expand All @@ -23,6 +23,7 @@ import { StoreService } from '@core/services/store/store.service';
import { isTokenAmount } from '@shared/utils/is-token';
import { StorageToken } from '@core/services/tokens/models/storage-token';
import { AssetType } from '@app/features/trade/models/asset';
import { TokensUpdaterService } from '@app/core/services/tokens/tokens-updater.service';

@Injectable({
providedIn: 'root'
Expand All @@ -35,13 +36,25 @@ export class TokensStoreService {

public readonly tokens$: Observable<List<TokenAmount>> = this._tokens$.asObservable();

private readonly _allChainsTokens$ = new BehaviorSubject<List<TokenAmount>>(List());

/**
* Tokens shown in seelctor when 'All Chains' selected
*/
public readonly allChainsTokens$: Observable<List<TokenAmount>> =
this._allChainsTokens$.asObservable();

/**
* Current tokens list.
*/
get tokens(): List<TokenAmount> {
public get tokens(): List<TokenAmount> {
return this._tokens$.getValue();
}

public get allChainsTokens(): List<TokenAmount> {
return this._allChainsTokens$.getValue();
}

/**
* Current favorite tokens list state.
*/
Expand Down Expand Up @@ -82,13 +95,15 @@ export class TokensStoreService {
private readonly tokensApiService: TokensApiService,
private readonly authService: AuthService,
private readonly walletConnectorService: WalletConnectorService,
private readonly storeService: StoreService
private readonly storeService: StoreService,
private readonly tokensUpdaterService: TokensUpdaterService
) {
this.setupStorageTokens();
this.setupAllChainsTokensList();
this.setupSubscriptions();
}

public setupStorageTokens(): void {
private setupStorageTokens(): void {
this.storageTokens = this.storeService.getItem('RUBIC_TOKENS') || [];
if (this.storageTokens.length) {
const tokens = this.getDefaultTokenAmounts(
Expand All @@ -99,6 +114,15 @@ export class TokensStoreService {
}
}

private setupAllChainsTokensList(): void {
this.tokensApiService
.fetchTokensListForAllChains()
.pipe(map(tokens => this.getDefaultTokenAmounts(tokens, false)))
.subscribe(tokensWithBalancesNaN => {
this._allChainsTokens$.next(tokensWithBalancesNaN);
});
}

private setupSubscriptions(): void {
this.authService.currentUser$
.pipe(
Expand Down Expand Up @@ -134,30 +158,39 @@ export class TokensStoreService {
).flat();
}

public startBalanceCalculating(blockchain: BlockchainName | 'allChains'): void {
public startBalanceCalculating(blockchain: AssetType): void {
console.log(
'%cSTART_BALANCE for ==> ',
'color: green; font-size: 30px;',
this.isBalanceAlreadyCalculatedForChain
);
if (this.isBalanceAlreadyCalculatedForChain[blockchain]) {
return;
}

combineLatest([
const tokensList$: Observable<List<TokenAmount>> = iif(
() => blockchain === 'allChains',
this.allChainsTokens$,
this.tokens$.pipe(
first(v => Boolean(v)),
map(tokens => tokens.filter(t => blockchain === 'allChains' || t.blockchain === blockchain))
),
this.authService.currentUser$
])
map(tokens => tokens.filter(t => t.blockchain === blockchain))
)
);

forkJoin([firstValueFrom(tokensList$), firstValueFrom(this.authService.currentUser$)])
.pipe(
debounceTime(100),
first(),
debounceTime(500),
switchMap(([tokens, user]) => {
this._isBalanceLoading$[blockchain].next(true);
if (!user) return this.getDefaultTokenAmounts(tokens, false);

return this.getTokensWithBalance(tokens);
}),
catchError(() => of(List()))
)
.subscribe((tokensWithBalances: List<TokenAmount>) => {
this.patchTokensBalances(tokensWithBalances);
this.patchTokensBalances(tokensWithBalances, blockchain === 'allChains');
this.tokensUpdaterService.triggerUpdateTokens();
this._isBalanceLoading$[blockchain].next(false);
this.isBalanceAlreadyCalculatedForChain[blockchain] = true;
});
Expand All @@ -179,25 +212,12 @@ export class TokensStoreService {
tokensList: List<TokenAmount | Token>
): Promise<List<TokenAmount>> {
const tokensByChain: Record<BlockchainName, Token[]> = {} as Record<BlockchainName, Token[]>;
// const tokensByChainWithBalancesMap: Map<
// BlockchainName,
// Record<string, TokenAmount>
// > = new Map() as Map<BlockchainName, Record<string, TokenAmount>>;

for (const token of tokensList) {
const chainTokensList = tokensByChain[token.blockchain];
if (!Array.isArray(chainTokensList)) {
tokensByChain[token.blockchain] = [] as Token[];
}
// if (!tokensByChainWithBalancesMap.has(token.blockchain)) {
// tokensByChainWithBalancesMap.set(token.blockchain, {} as Record<string, TokenAmount>);
// }

// const chainTokensWithBalances = tokensByChainWithBalancesMap.get(token.blockchain);
// chainTokensWithBalances[token.address] =
// this.tokens.find(t => compareAddresses(t.address, token.address)) ||
// this.getDefaultTokenAmounts(List([token]), false).get(0);

if (isTokenAmount(token)) {
tokensByChain[token.blockchain].push(token);
} else {
Expand All @@ -215,15 +235,6 @@ export class TokensStoreService {
// if EVM-address used -> it will fetch only evm address etc.
if (!doesWalletSupportsTokenChain) return tokens.map(() => new BigNumber(NaN));

// @TODO try in perfomance
// if (this.isBalanceAlreadyCalculatedForChain[chain]) {
// return tokens.map(
// token =>
// this.tokens.find(t => compareAddresses(t.address, token.address)).amount ||
// new BigNumber(NaN)
// );
// }

const web3Public = Injector.web3PublicService.getWeb3Public(chain) as Web3Public;
const chainTokensBalances = web3Public
.getTokensBalances(
Expand Down Expand Up @@ -382,8 +393,31 @@ export class TokensStoreService {
this._tokens$.next(tokens);
}

public patchTokensBalances(tokensWithBalances: List<TokenAmount>): void {
const tokens = (this.tokens || List([])).map(token => {
// public patchTokensBalances(tokensWithBalances: List<TokenAmount>): void {
// const tokens = (this.tokens || List([])).map(token => {
// const foundToken = tokensWithBalances.find(tokenWithBalance =>
// compareTokens(token, tokenWithBalance)
// );
// if (!foundToken) {
// return token;
// } else {
// return {
// ...token,
// amount: foundToken.amount
// };
// }
// });
// this._tokens$.next(tokens);
// }

public patchTokensBalances(
tokensWithBalances: List<TokenAmount>,
patchAllChains: boolean = false
): void {
const list = patchAllChains ? this.allChainsTokens : this.tokens;
const _listSubj$ = patchAllChains ? this._allChainsTokens$ : this._tokens$;

const tokens = (list || List([])).map(token => {
const foundToken = tokensWithBalances.find(tokenWithBalance =>
compareTokens(token, tokenWithBalance)
);
Expand All @@ -396,7 +430,7 @@ export class TokensStoreService {
};
}
});
this._tokens$.next(tokens);
_listSubj$.next(tokens);
}

/**
Expand Down
16 changes: 16 additions & 0 deletions src/app/core/services/tokens/tokens-updater.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class TokensUpdaterService {
/**
* Emits events, when list must be updated.
*/
private readonly _updateTokensList$ = new Subject<void>();

public readonly updateTokensList$ = this._updateTokensList$.asObservable();

public triggerUpdateTokens(): void {
this._updateTokensList$.next();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export class AssetsSelectorService {
}

this.selectorListType = 'tokens';
this.tokensStoreService.startBalanceCalculating(this.assetType);
}

private subscribeOnAssetChange(): void {
Expand Down
Loading

0 comments on commit 3b29c4e

Please sign in to comment.