diff --git a/src/app-toolkit/app-toolkit.interface.ts b/src/app-toolkit/app-toolkit.interface.ts index 11033ea3a..f2f6484e1 100644 --- a/src/app-toolkit/app-toolkit.interface.ts +++ b/src/app-toolkit/app-toolkit.interface.ts @@ -6,7 +6,7 @@ import { AppDefinition } from '~app/app.definition'; import { IContractFactory } from '~contract/contracts'; import { EthersMulticall } from '~multicall'; import { DefaultDataProps } from '~position/display.interface'; -import { AppTokenPosition, ContractPosition } from '~position/position.interface'; +import { AppTokenPosition, ContractPosition, NonFungibleToken } from '~position/position.interface'; import { AppGroupsDefinition } from '~position/position.service'; import { BaseToken } from '~position/token.interface'; import { Network } from '~types/network.interface'; @@ -44,6 +44,13 @@ export interface IAppToolkit { ...appTokenDefinition: AppGroupsDefinition[] ): Promise[]>; + // Position Key + + getPositionKey( + position: ContractPosition | AppTokenPosition | BaseToken | NonFungibleToken, + pickFields?: string[], + ): string; + // Cache getFromCache(key: string): Promise; diff --git a/src/app-toolkit/app-toolkit.service.ts b/src/app-toolkit/app-toolkit.service.ts index 1725970f0..568bc3e1e 100644 --- a/src/app-toolkit/app-toolkit.service.ts +++ b/src/app-toolkit/app-toolkit.service.ts @@ -8,7 +8,10 @@ import { ContractFactory } from '~contract'; import { MulticallService } from '~multicall/multicall.service'; import { NetworkProviderService } from '~network-provider/network-provider.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 { TokenService } from '~token/token.service'; import { Network } from '~types/network.interface'; @@ -24,6 +27,7 @@ export class AppToolkit implements IAppToolkit { @Inject(AppService) private readonly appService: AppService, @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(MulticallService) private readonly multicallService: MulticallService, @Inject(CACHE_MANAGER) private readonly cacheManager: Cache, @@ -75,6 +79,15 @@ export class AppToolkit implements IAppToolkit { return this.positionService.getAppContractPositions(...appTokenDefinitions); } + // Position Key + + getPositionKey( + position: ContractPosition | AppTokenPosition | BaseToken | NonFungibleToken, + pickFields: string[] = [], + ) { + return this.positionKeyService.getPositionKey(position, pickFields); + } + // Cache async getFromCache(key: string) { diff --git a/src/app-toolkit/helpers/master-chef/master-chef.contract-position-helper.ts b/src/app-toolkit/helpers/master-chef/master-chef.contract-position-helper.ts index 51c12658d..f0f20f17c 100644 --- a/src/app-toolkit/helpers/master-chef/master-chef.contract-position-helper.ts +++ b/src/app-toolkit/helpers/master-chef/master-chef.contract-position-helper.ts @@ -303,6 +303,7 @@ export class MasterChefContractPositionHelper { displayProps, }; + position.key = this.appToolkit.getPositionKey(position, ['poolIndex']); return position; }), ); diff --git a/src/apps/lyra-avalon/optimism/lyra-avalon.options.contract-position-fetcher.ts b/src/apps/lyra-avalon/optimism/lyra-avalon.options.contract-position-fetcher.ts index 37baad336..e120721b3 100644 --- a/src/apps/lyra-avalon/optimism/lyra-avalon.options.contract-position-fetcher.ts +++ b/src/apps/lyra-avalon/optimism/lyra-avalon.options.contract-position-fetcher.ts @@ -54,6 +54,7 @@ export class OptimismLyraAvalonOptionsContractPositionFetcher implements Positio }, dataProps: {}, }; + const positions = _.keys(OPTION_TYPES).map(key => { return { ...position, @@ -73,7 +74,12 @@ export class OptimismLyraAvalonOptionsContractPositionFetcher implements Positio }, } as ContractPosition; }); - return positions; + + const positionsWithKey = positions.map(p => ({ + key: this.appToolkit.getPositionKey(p, ['optionType', 'strikeId']), + ...p, + })); + return positionsWithKey; }); return _.flatten(strikes); }); diff --git a/src/position/position-key.service.ts b/src/position/position-key.service.ts new file mode 100644 index 000000000..bf520d173 --- /dev/null +++ b/src/position/position-key.service.ts @@ -0,0 +1,53 @@ +import { Injectable } from '@nestjs/common'; +import murmur from 'murmurhash-js'; + +import { Network } from '~types/network.interface'; + +import { ContractType } from './contract.interface'; +import { AppTokenPosition, ContractPosition, MetaType, NonFungibleToken } from './position.interface'; +import { BaseToken } from './token.interface'; + +export type AppGroupsDefinition = { + appId: string; + groupIds: string[]; + network: Network; +}; + +@Injectable() +export class PositionKeyService { + generateKey(input: string) { + return murmur.murmur3(input).toString(); + } + + getPositionKey( + position: ContractPosition | AppTokenPosition | BaseToken | NonFungibleToken, + pickFields: string[] = [], + ) { + if ('key' in position) return position.key!; + + switch (position.type) { + case ContractType.POSITION: + return this.generateKey( + [ + position.address, + position.network, + position.appId, + position.tokens.map(token => [token.address, token.network, token.metaType].join(':')), + pickFields.map(v => position.dataProps[v]).join(':'), + ].join(':'), + ); + case ContractType.APP_TOKEN: + return this.generateKey( + [ + position.appId, + position.address, + position.network, + MetaType.SUPPLIED, + pickFields.map(v => position.dataProps[v]).join(':'), + ].join(':'), + ); + default: + return this.generateKey([position.address, position.network, MetaType.SUPPLIED].join(':')); + } + } +} diff --git a/src/position/position.module.ts b/src/position/position.module.ts index 3eda1fb5a..df413bbcc 100644 --- a/src/position/position.module.ts +++ b/src/position/position.module.ts @@ -5,14 +5,21 @@ import { CacheModule } from '~cache/cache.module'; import { PositionBalanceFetcherRegistry } from './position-balance-fetcher.registry'; import { PositionFetcherRegistry } from './position-fetcher.registry'; +import { PositionKeyService } from './position-key.service'; import { PositionSources } from './position-source'; import { PositionController } from './position.controller'; import { PositionService } from './position.service'; @Module({ imports: [DiscoveryModule, CacheModule], - providers: [...PositionSources, PositionService, PositionFetcherRegistry, PositionBalanceFetcherRegistry], + providers: [ + ...PositionSources, + PositionService, + PositionFetcherRegistry, + PositionBalanceFetcherRegistry, + PositionKeyService, + ], controllers: [PositionController], - exports: [PositionService, PositionFetcherRegistry, PositionBalanceFetcherRegistry], + exports: [PositionService, PositionFetcherRegistry, PositionBalanceFetcherRegistry, PositionKeyService], }) export class PositionModule {}