Skip to content

Commit

Permalink
feat: add pool graphs to graph endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
tmrdlt committed Aug 28, 2024
1 parent b2866df commit 7920996
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 51 deletions.
36 changes: 21 additions & 15 deletions src/api/graph/graph.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ApiQuery, ApiResponse } from '@nestjs/swagger';

import { Graph, GraphType, TimeFrame } from '@/api/graph/graph.model';
import { GraphService } from '@/api/graph/graph.service';
import { ContractAddress } from '@/clients/sdk-client.model';

@UseInterceptors(CacheInterceptor)
@Controller('graph')
Expand All @@ -27,28 +28,33 @@ export class GraphController {
description: 'Desired Time Frame (Default: MAX)',
required: false,
})
// @ApiQuery({
// name: 'tokenAddress',
// type: String,
// description: 'TBD',
// required: false,
// })
// @ApiQuery({
// name: 'pairAddress',
// type: String,
// description: 'TBD',
// required: false,
// })
@ApiQuery({
name: 'tokenAddress',
type: String,
description: 'Get graph for specific token',
required: false,
})
@ApiQuery({
name: 'pairAddress',
type: String,
description: 'Get graph for specific pair',
required: false,
})
@ApiResponse({ status: 200, type: Graph })
async get(
@Query('graphType', new ParseEnumPipe(GraphType))
graphType: GraphType,
@Query('timeFrame', new ParseEnumPipe(TimeFrame, { optional: true }))
timeFrame?: TimeFrame,
// @Query('tokenAddress') tokenAddress?: ContractAddress,
// @Query('pairAddress') pairAddress?: ContractAddress,
@Query('tokenAddress') tokenAddress?: ContractAddress,
@Query('pairAddress') pairAddress?: ContractAddress,
): Promise<any> {
return this.graphService.getGraph(graphType, timeFrame);
return this.graphService.getGraph(
graphType,
timeFrame,
tokenAddress,
pairAddress,
);
}

constructor(private readonly graphService: GraphService) {}
Expand Down
2 changes: 2 additions & 0 deletions src/api/graph/graph.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export enum GraphType {
TVL = 'TVL',
Volume = 'Volume',
Price = 'Price',
Price0_1 = 'Price0_1',
Price1_0 = 'Price1_0',
Locked = 'Locked',
Fees = 'Fees',
}
Expand Down
152 changes: 116 additions & 36 deletions src/api/graph/graph.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { PairLiquidityInfoHistoryWithTokens } from '@/api/pair-liquidity-info-hi
import { PairLiquidityInfoHistoryService } from '@/api/pair-liquidity-info-history/pair-liquidity-info-history.service';
import { PairLiquidityInfoHistoryDbService } from '@/database/pair-liquidity-info-history/pair-liquidity-info-history-db.service';
import { OrderQueryEnum } from '@/api/api.model';
import { ContractAddress } from '@/clients/sdk-client.model';

const TIME_FRAMES = {
'1H': 1,
Expand All @@ -31,20 +32,33 @@ export class GraphService {
async getGraph(
graphType: GraphType = GraphType.TVL,
timeFrame: TimeFrame = TimeFrame.MAX,
tokenAddress?: ContractAddress,
pairAddress?: ContractAddress,
): Promise<Graph> {
const history = await this.pairLiquidityInfoHistoryDb.getAll({
limit: 9999999,
offset: 0,
order: OrderQueryEnum.asc,
pairAddress,
tokenAddress,
});
const graphData = this.graphData(history);
const filteredData = this.filteredData(graphData, graphType, timeFrame);
const bucketedGraphData = this.bucketedGraphData(filteredData, graphType);

return bucketedGraphData;
const graphData = this.graphData(
history,
pairAddress,
tokenAddress,
graphType,
);
const filteredData = this.filteredData(graphData, graphType, timeFrame);
return this.bucketedGraphData(filteredData, graphType);
}

private graphData(history: PairLiquidityInfoHistoryWithTokens[]) {
private graphData(
history: PairLiquidityInfoHistoryWithTokens[],
pairAddress: ContractAddress | undefined,
tokenAddress: ContractAddress | undefined,
graphType: GraphType,
) {
let tvl = new BigNumber(0);
return history.reduce(
(
Expand All @@ -56,46 +70,112 @@ export class GraphService {
) => {
const entry =
this.pairLiquidityInfoHistoryService.mapToEntryWithPrice(tx);
// TVL
// deltaUsdValue is already calculated but absolute, so we need to check the deltaReserve to get the sign
const delta0 = new BigNumber(entry.delta0UsdValue || '').times(
Math.sign(Number(entry.deltaReserve0)),
);
const delta1 = new BigNumber(entry.delta1UsdValue || '').times(
Math.sign(Number(entry.deltaReserve1)),
);
tvl = tvl
.plus(delta0.isNaN() ? 0 : delta0)
.plus(delta1.isNaN() ? 0 : delta1);
acc.datasets[0].data = [...acc.datasets[0].data, tvl.toString()].map(
(d) => d || '0',
);

// VOLUME
if (entry.type === 'SwapTokens') {
acc.datasets[1].data = [
...acc.datasets[1].data,
new BigNumber(entry.delta0UsdValue || 0)
.plus(entry.delta1UsdValue || 0)
.toString(),
].map((d) => d || '0');
} else {
acc.datasets[1].data = [...acc.datasets[1].data, '0'].map(
(d) => d || '0',
);
if (!pairAddress && !tokenAddress) {
// OVERVIEW
if (graphType === GraphType.TVL) {
// TVL
// deltaUsdValue is already calculated but absolute, so we need to check the deltaReserve to get the sign
const delta0 = new BigNumber(entry.delta0UsdValue || '').times(
Math.sign(Number(entry.deltaReserve0)),
);
const delta1 = new BigNumber(entry.delta1UsdValue || '').times(
Math.sign(Number(entry.deltaReserve1)),
);
tvl = tvl
.plus(delta0.isNaN() ? 0 : delta0)
.plus(delta1.isNaN() ? 0 : delta1);
acc.datasets[0].data = [
...acc.datasets[0].data,
tvl.toString(),
].map((d) => d || '0');
} else if (graphType === GraphType.Volume) {
// VOLUME
if (entry.type === 'SwapTokens') {
acc.datasets[0].data = [
...acc.datasets[0].data,
new BigNumber(entry.delta0UsdValue || 0)
.plus(entry.delta1UsdValue || 0)
.toString(),
].map((d) => d || '0');
} else {
acc.datasets[0].data = [...acc.datasets[0].data, '0'].map(
(d) => d || '0',
);
}
} else {
return [];
}
} else if (tokenAddress && !pairAddress) {
// TOKENS
// TODO FILL
} else if (!tokenAddress && pairAddress) {
// POOLS
if (graphType === GraphType.Price0_1) {
// Price 0/1
acc.datasets[0].data = [
...acc.datasets[0].data,
new BigNumber(entry.reserve0)
.div(BigNumber(10).pow(tx.pair.token0.decimals))
.div(
new BigNumber(entry.reserve1).div(
BigNumber(10).pow(tx.pair.token1.decimals),
),
)
.toString(),
].map((d) => d || '0');
} else if (graphType === GraphType.Price1_0) {
// Price 1/0
acc.datasets[0].data = [
...acc.datasets[0].data,
new BigNumber(entry.reserve1)
.div(BigNumber(10).pow(tx.pair.token1.decimals))
.div(
new BigNumber(entry.reserve0).div(
BigNumber(10).pow(tx.pair.token0.decimals),
),
)
.toString(),
].map((d) => d || '0');
} else if (graphType === GraphType.TVL) {
// TVL
acc.datasets[0].data = [
...acc.datasets[0].data,
new BigNumber(entry.reserve0Usd || '')
.plus(entry.reserve1Usd || '')
.toString(),
].map((d) => d || '0');
} else if (graphType === GraphType.Fees) {
// Fee
acc.datasets[0].data = [
...acc.datasets[0].data,
entry.txUsdFee,
].map((d) => d || '0');
} else if (graphType === GraphType.Volume) {
// Volume
if (entry.type === 'SwapTokens') {
acc.datasets[0].data = [
...acc.datasets[0].data,
new BigNumber(entry.delta0UsdValue || '')
.plus(entry.delta1UsdValue || '')
.toString(),
].map((d) => d || '0');
} else {
acc.datasets[0].data = [...acc.datasets[0].data, '0'].map(
(d) => d || '0',
);
}
}
}

acc.x = [...acc.x, entry.microBlockTime];
return acc;
},
{
x: [],
datasets: [
{
label: GraphType.TVL,
data: [],
},
{
label: GraphType.Volume,
label: graphType,
data: [],
},
],
Expand Down

0 comments on commit 7920996

Please sign in to comment.