Skip to content

Commit

Permalink
feat(app-tokens): Add app token dataloader selector (Zapper-fi#1093)
Browse files Browse the repository at this point in the history
  • Loading branch information
JForsaken committed Aug 8, 2022
1 parent f0767da commit 920d0f1
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 10 deletions.
3 changes: 3 additions & 0 deletions src/app-toolkit/app-toolkit.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ethers } from 'ethers';
import { AppDefinition } from '~app/app.definition';
import { IContractFactory } from '~contract/contracts';
import { IMulticallWrapper } from '~multicall/multicall.interface';
import { AppTokenSelector, CreateAppTokenSelectorOptions } from '~position/app-token-selector.interface';
import { DefaultDataProps } from '~position/display.interface';
import { AppTokenPosition, ContractPosition, NonFungibleToken } from '~position/position.interface';
import { AppGroupsDefinition } from '~position/position.service';
Expand Down Expand Up @@ -40,6 +41,8 @@ export interface IAppToolkit {

// Positions

getAppTokenSelector(opts?: CreateAppTokenSelectorOptions): AppTokenSelector;

getAppTokenPositions<T = DefaultDataProps>(
...appTokenDefinition: AppGroupsDefinition[]
): Promise<AppTokenPosition<T>[]>;
Expand Down
9 changes: 7 additions & 2 deletions src/app-toolkit/app-toolkit.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import { AppService } from '~app/app.service';
import { ContractFactory } from '~contract';
import { MulticallService } from '~multicall/multicall.service';
import { NetworkProviderService } from '~network-provider/network-provider.service';
import { CreateAppTokenSelectorOptions } from '~position/app-token-selector.interface';
import { AppTokenSelectorService } from '~position/app-token-selector.service';
import { DefaultDataProps } from '~position/display.interface';
import { PositionKeyService } from '~position/position-key.service';
import { AppTokenPosition, ContractPosition, NonFungibleToken } from '~position/position.interface';
import { AppGroupsDefinition, PositionService } from '~position/position.service';
import { BaseToken } from '~position/token.interface';
import { PriceSelectorService } from '~token/price-selector.service';
import { CreatePriceSelectorOptions } from '~token/token-price-selector.interface';
import { TokenService } from '~token/token.service';
import { Network } from '~types/network.interface';

import { AppToolkitHelperRegistry } from './app-toolkit.helpers';
Expand All @@ -30,8 +31,8 @@ export class AppToolkit implements IAppToolkit {
@Inject(NetworkProviderService) private readonly networkProviderService: NetworkProviderService,
@Inject(PositionService) private readonly positionService: PositionService,
@Inject(PositionKeyService) private readonly positionKeyService: PositionKeyService,
@Inject(TokenService) private readonly tokenService: TokenService,
@Inject(PriceSelectorService) private readonly priceSelectorService: PriceSelectorService,
@Inject(AppTokenSelectorService) private readonly appTokenSelectorService: AppTokenSelectorService,
@Inject(MulticallService) private readonly multicallService: MulticallService,
@Inject(CACHE_MANAGER) private readonly cacheManager: Cache,
) {
Expand Down Expand Up @@ -78,6 +79,10 @@ export class AppToolkit implements IAppToolkit {

// Positions

getAppTokenSelector(opts: CreateAppTokenSelectorOptions = {}) {
return this.appTokenSelectorService.create(opts);
}

getAppTokenPositions<T = DefaultDataProps>(...appTokenDefinitions: AppGroupsDefinition[]) {
return this.positionService.getAppTokenPositions<T>(...appTokenDefinitions);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Inject } from '@nestjs/common';


import { Register } from '~app-toolkit/decorators';
import { PositionFetcher } from '~position/position-fetcher.interface';
import { AppTokenPosition } from '~position/position.interface';
Expand Down
3 changes: 2 additions & 1 deletion src/apps/openleverage/contracts/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Injectable, Inject } from '@nestjs/common';

import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface';
import { ContractFactory } from '~contract/contracts';
import { Network } from '~types/network.interface';

import { OpenleverageFactory__factory } from './ethers';
import { OpenleverageLpool__factory } from './ethers';
import { ContractFactory } from '~contract/contracts';
// eslint-disable-next-line
type ContractOpts = { address: string; network: Network };

Expand Down
2 changes: 1 addition & 1 deletion src/apps/openleverage/openleverage.definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const OPENLEVERAGE_DEFINITION = appDefinition({
url: 'https://openleverage.finance/',
groups: {
pool: { id: 'pool', type: GroupType.TOKEN, label: 'Pools' },
trade: { id: 'trade', type: GroupType.POSITION, label: 'Trade' }
trade: { id: 'trade', type: GroupType.POSITION, label: 'Trade' },
},
tags: [AppTag.DECENTRALIZED_EXCHANGE],
keywords: [],
Expand Down
2 changes: 1 addition & 1 deletion src/apps/qi-dao/optimism/qi-dao.balance-fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class OptimismQiDaoBalanceFetcher implements BalanceFetcher {
@Inject(QiDaoVaultPositionBalanceHelper)
private readonly qiDaoVaultTokenBalanceHelper: QiDaoVaultPositionBalanceHelper,
@Inject(QiDaoContractFactory) private readonly contractFactory: QiDaoContractFactory,
) { }
) {}

async getVaultTokenBalances(address: string) {
return this.qiDaoVaultTokenBalanceHelper.getPositionBalances({
Expand Down
2 changes: 1 addition & 1 deletion src/apps/qi-dao/optimism/qi-dao.vault.position-fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const network = Network.OPTIMISM_MAINNET;
export class OptimismQiDaoVaultPositionFetcher
implements PositionFetcher<ContractPosition<QiDaoVaultPositionDataProps>>
{
constructor(@Inject(QiDaoVaultPositionHelper) private readonly qiDaoVaultPositionHelper: QiDaoVaultPositionHelper) { }
constructor(@Inject(QiDaoVaultPositionHelper) private readonly qiDaoVaultPositionHelper: QiDaoVaultPositionHelper) {}

getPositions() {
return this.qiDaoVaultPositionHelper.getPositions({
Expand Down
19 changes: 19 additions & 0 deletions src/position/app-token-selector.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Network } from '~types';

import { AppTokenPosition } from './position.interface';

export type LoggingTags = { network?: Network; context?: string };
export type AppTokenSelectorKey = { network: Network; address: string };
export type GetOne = (opts: AppTokenSelectorKey) => Promise<AppTokenPosition | null>;
export type GetMany = (opts: AppTokenSelectorKey[]) => Promise<(AppTokenPosition | null)[]>;

export interface AppTokenSelector {
getOne: GetOne;
getMany: GetMany;
}

export type CreateAppTokenSelectorOptions = { tags?: LoggingTags };

export interface AppTokenSelectorFactory {
create: (opts: CreateAppTokenSelectorOptions) => AppTokenSelector;
}
39 changes: 39 additions & 0 deletions src/position/app-token-selector.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Inject, Injectable } from '@nestjs/common';
import DataLoader from 'dataloader';
import { Mutable } from 'type-fest';

import { AppTokenPosition } from '~position/position.interface';

import {
AppTokenSelector,
AppTokenSelectorFactory,
AppTokenSelectorKey,
CreateAppTokenSelectorOptions,
GetMany,
GetOne,
} from './app-token-selector.interface';
import { ApiPositionSource } from './position-source/position-source.api';

@Injectable()
export class AppTokenSelectorService implements AppTokenSelectorFactory {
constructor(@Inject(ApiPositionSource) private readonly positionAPIClient: ApiPositionSource) {}

create(_opts: CreateAppTokenSelectorOptions): AppTokenSelector {
const tokenDataLoader = new DataLoader<AppTokenSelectorKey, AppTokenPosition | null>(
keys => this.positionAPIClient.getAppTokenBatch(keys as Mutable<AppTokenSelectorKey[]>),
{ maxBatchSize: 100 },
);

return {
getOne: ({ network, address }: Parameters<GetOne>[0]) => tokenDataLoader.load({ network, address }),
getMany: async (queries: Parameters<GetMany>[0]) => {
const docs = await tokenDataLoader.loadMany(queries);

return docs.map(doc => {
if (doc instanceof Error) return null;
return doc;
});
},
};
}
}
16 changes: 16 additions & 0 deletions src/position/position-source/position-source.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import Axios, { AxiosInstance } from 'axios';
import DataLoader from 'dataloader';
import qs from 'qs';

import { AppTokenSelectorKey } from '~position/app-token-selector.interface';
import { ContractType } from '~position/contract.interface';
import { AbstractPosition, AppTokenPosition, ContractPosition } from '~position/position.interface';
import { AppGroupsDefinition } from '~position/position.service';
import { Network } from '~types';

import { PositionSource } from './position-source.interface';

Expand Down Expand Up @@ -62,4 +64,18 @@ export class ApiPositionSource implements PositionSource {
const results = await Promise.all(definitions.map(v => loader.load(v))).then(v => v.flat());
return results as any as T[];
}

async getAppTokenBatch(queries: AppTokenSelectorKey[]) {
const addresses: string[] = [];
const networks: Network[] = [];

for (const q of queries) {
addresses.push(q.address);
networks.push(q.network);
}

const query = qs.stringify({ addresses, networks });
const { data } = await this.axios.get<(AppTokenPosition | null)[]>(`/v2/app-tokens/batch?${query}`);
return data;
}
}
14 changes: 11 additions & 3 deletions src/position/position.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { DiscoveryModule } from '@nestjs/core';

import { CacheModule } from '~cache/cache.module';

import { AppTokenSelectorService } from './app-token-selector.service';
import { PositionBalanceFetcherRegistry } from './position-balance-fetcher.registry';
import { PositionFetcherRegistry } from './position-fetcher.registry';
import { PositionKeyService } from './position-key.service';
Expand All @@ -14,12 +15,19 @@ import { PositionService } from './position.service';
imports: [DiscoveryModule, CacheModule],
providers: [
...PositionSources,
PositionService,
PositionFetcherRegistry,
AppTokenSelectorService,
PositionBalanceFetcherRegistry,
PositionFetcherRegistry,
PositionKeyService,
PositionService,
],
controllers: [PositionController],
exports: [PositionService, PositionFetcherRegistry, PositionBalanceFetcherRegistry, PositionKeyService],
exports: [
AppTokenSelectorService,
PositionBalanceFetcherRegistry,
PositionFetcherRegistry,
PositionKeyService,
PositionService,
],
})
export class PositionModule {}

0 comments on commit 920d0f1

Please sign in to comment.