-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(indexer): add coingecko price collection from tokens against usd
Signed-off-by: david <david@umaproject.org>
- Loading branch information
Showing
12 changed files
with
259 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
.../indexer-database/packages/indexer-database/src/migrations/1733941169940-HistoricPrice.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { MigrationInterface, QueryRunner } from "typeorm"; | ||
|
||
export class HistoricPrice1733941169940 implements MigrationInterface { | ||
name = 'HistoricPrice1733941169940' | ||
|
||
public async up(queryRunner: QueryRunner): Promise<void> { | ||
await queryRunner.query(`CREATE TABLE "historic_market_price" ("id" SERIAL NOT NULL, "baseCurrency" character varying NOT NULL, "quoteCurrency" character varying NOT NULL DEFAULT 'usd', "date" date NOT NULL, "price" numeric NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "UK_historic_price_baseCurrency_quoteCurrency_date" UNIQUE ("baseCurrency", "quoteCurrency", "date"), CONSTRAINT "PK_b0a22436b47e742187aa7408561" PRIMARY KEY ("id"))`); | ||
} | ||
|
||
public async down(queryRunner: QueryRunner): Promise<void> { | ||
await queryRunner.query(`DROP TABLE "historic_market_price"`); | ||
} | ||
|
||
} |
14 changes: 14 additions & 0 deletions
14
.../indexer-database/packages/indexer-database/src/migrations/1734549610006-HistoricPrice.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { MigrationInterface, QueryRunner } from "typeorm"; | ||
|
||
export class HistoricPrice1734549610006 implements MigrationInterface { | ||
name = 'HistoricPrice1734549610006' | ||
|
||
public async up(queryRunner: QueryRunner): Promise<void> { | ||
await queryRunner.query(`CREATE TABLE "historic_price" ("id" SERIAL NOT NULL, "baseCurrency" character varying NOT NULL, "quoteCurrency" character varying NOT NULL DEFAULT 'usd', "date" character varying NOT NULL, "price" numeric NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "UK_historic_price_baseCurrency_quoteCurrency_date" UNIQUE ("baseCurrency", "quoteCurrency", "date"), CONSTRAINT "PK_77dc3f4978cdfb03f1bb3a7444b" PRIMARY KEY ("id"))`); | ||
} | ||
|
||
public async down(queryRunner: QueryRunner): Promise<void> { | ||
await queryRunner.query(`DROP TABLE "historic_price"`); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { | ||
Column, | ||
CreateDateColumn, | ||
Entity, | ||
PrimaryGeneratedColumn, | ||
Unique, | ||
} from "typeorm"; | ||
|
||
@Entity() | ||
@Unique("UK_historic_price_baseCurrency_quoteCurrency_date", [ | ||
"baseCurrency", | ||
"quoteCurrency", | ||
"date", | ||
]) | ||
export class HistoricPrice { | ||
@PrimaryGeneratedColumn() | ||
id: number; | ||
|
||
// bear in mind we are using coingecko symbols directly here, for all intents and purposes this is coingecko historic market price | ||
@Column() | ||
baseCurrency: string; | ||
|
||
@Column({ default: "usd" }) | ||
quoteCurrency: string; | ||
|
||
// yyyy-LL-dd | ||
@Column() | ||
date: string; | ||
|
||
@Column({ type: "decimal" }) | ||
price: string; | ||
|
||
@CreateDateColumn() | ||
createdAt: Date; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export * from "./bundles"; | ||
export * from "./spokePoolProcessor"; | ||
export * from "./BundleBuilderService"; | ||
export * from "./priceProcessor"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { CoingeckoSymbol, CoingeckoClient } from "../utils/coingeckoClient"; | ||
import { Logger } from "winston"; | ||
import { DataSource, entities } from "@repo/indexer-database"; | ||
import { BaseIndexer } from "../generics"; | ||
import { DateTime } from "luxon"; | ||
|
||
type Config = { | ||
symbols: CoingeckoSymbol[]; | ||
// not used currently | ||
quoteCurrency?: string; | ||
}; | ||
|
||
type Deps = { | ||
logger: Logger; | ||
postgres: DataSource; | ||
}; | ||
|
||
export class CoingeckoPriceProcessor extends BaseIndexer { | ||
private coingeckoClient: CoingeckoClient; | ||
constructor( | ||
private config: Config, | ||
private deps: Deps, | ||
) { | ||
super(deps.logger, "CoingeckoPriceProcessor"); | ||
this.coingeckoClient = new CoingeckoClient(); | ||
} | ||
|
||
protected async indexerLogic(): Promise<void> { | ||
const now = Date.now(); | ||
const dbFormattedDate = DateTime.fromMillis(now).toFormat("yyyy-LL-dd"); | ||
const quoteCurrency = this.config.quoteCurrency ?? "usd"; | ||
const historicPriceRepository = this.deps.postgres.getRepository( | ||
entities.HistoricPrice, | ||
); | ||
|
||
for (const symbol of this.config.symbols) { | ||
const existingPrice = await historicPriceRepository.findOne({ | ||
where: { | ||
date: dbFormattedDate, | ||
baseCurrency: symbol, | ||
quoteCurrency, | ||
}, | ||
}); | ||
// do nothing, we have a price for this day | ||
if (existingPrice) return; | ||
|
||
try { | ||
const historicPriceData = | ||
await this.coingeckoClient.getHistoricDailyPrice(now, symbol); | ||
const price = | ||
historicPriceData.market_data?.current_price[quoteCurrency]; | ||
// wasnt able to get a price | ||
if (price === undefined) { | ||
this.deps.logger.error( | ||
`Unable to find ${quoteCurrency} for ${symbol}`, | ||
); | ||
return; | ||
} | ||
await historicPriceRepository.insert({ | ||
date: dbFormattedDate, | ||
baseCurrency: symbol, | ||
quoteCurrency, | ||
price: price.toString(), | ||
}); | ||
this.logger.info({ | ||
at: "CoingeckoPriceProcessor#indexerLogic", | ||
message: `Inserted historic price for ${symbol} on ${dbFormattedDate}`, | ||
}); | ||
} catch (error) { | ||
this.logger.error({ | ||
at: "CoingeckoPriceProcessor#indexerLogic", | ||
message: `Failed to fetch or insert historic price for ${symbol} on ${dbFormattedDate}`, | ||
error: (error as Error).message, | ||
}); | ||
} | ||
} | ||
} | ||
|
||
protected async initialize(): Promise<void> { | ||
// Initialization logic if needed | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import * as s from "superstruct"; | ||
import { DateTime } from "luxon"; | ||
|
||
export const CoingeckoSymbol = s.enums([ | ||
"ethereum", | ||
"matic-network", | ||
"wrapped-bitcoin", | ||
"usd-coin", | ||
"uma", | ||
"badger-dao", | ||
"weth", | ||
"boba-network", | ||
"dai", | ||
"balancer", | ||
"tether", | ||
"across-protocol", | ||
"havven", | ||
"pooltogether", | ||
"bridged-usd-coin-base", | ||
"optimism", | ||
"usd-coin-ethereum-bridged", | ||
]); | ||
export type CoingeckoSymbol = s.Infer<typeof CoingeckoSymbol>; | ||
export const CGHistoricPriceBase = s.object({ | ||
id: s.string(), | ||
symbol: s.string(), | ||
name: s.string(), | ||
market_data: s.optional( | ||
s.object({ | ||
current_price: s.record(s.string(), s.number()), | ||
}), | ||
), | ||
}); | ||
export type CGHistoricPriceBase = s.Infer<typeof CGHistoricPriceBase>; | ||
|
||
export class CoingeckoClient { | ||
constructor(private baseUrl: string = "https://api.coingecko.com/api/v3") {} | ||
|
||
// rounds timestamp to the current day | ||
public async getHistoricDailyPrice( | ||
timestamp: number, | ||
symbol: CoingeckoSymbol, | ||
): Promise<CGHistoricPriceBase> { | ||
const cgFormattedDate = | ||
DateTime.fromMillis(timestamp).toFormat("dd-LL-yyyy"); | ||
const response = await fetch( | ||
`${this.baseUrl}/coins/${symbol}/history?date=${cgFormattedDate}&localization=false`, | ||
); | ||
if (!response.ok) { | ||
throw new Error(`Error fetching historic price: ${response.statusText}`); | ||
} | ||
return s.create(await response.json(), CGHistoricPriceBase); | ||
} | ||
} |
Oops, something went wrong.