-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(indexer): add coingecko price collection from tokens against usd #138
base: master
Are you sure you want to change the base?
feat(indexer): add coingecko price collection from tokens against usd #138
Conversation
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"`); | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if these 2 migration files are different, but I guess one of them is not needed. The second migration will fail because the table will already be created by the first one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yea this was a mistake. wil fix
@Column() | ||
date: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should definitely be a typed Date
property and the DB column should be date
. This way we can take advantage of special queries/functionalities that SQL provides for such column type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think the complexity is that we only care about 1 price per day, which means deterministically calculating the "day" based on a js Date or timestamp becomes more involved. Ill add the logic and u can see if thats what you want
return; | ||
} | ||
await historicPriceRepository.insert({ | ||
date: dbFormattedDate, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I propose to not leak here the weird way that CoinGecko handles date format. Here we should pass a simple date instance as value
} | ||
await historicPriceRepository.insert({ | ||
date: dbFormattedDate, | ||
baseCurrency: symbol, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the past I experienced inconsistencies with the symbols returned by CoinGecko API, for example different letter capitalisation for the same token, so I propose to do a toLowerCase()
on the symbol
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is currently an enum, so if anything is wrong we can fix it in the enum
packages/indexer/src/parseEnv.ts
Outdated
@@ -193,6 +196,9 @@ export function envToConfig(env: Env): Config { | |||
enabledWebhookRequestWorkers: true, | |||
clients: parseWebhookClientsFromString(env.WEBHOOK_CLIENTS ?? "[]"), | |||
}; | |||
const coingeckoSymbols = parseArray(env.COINGECKO_SYMBOLS).map((symbol) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should document this ENV value in the README and specify the format of it (list, object, etc)
4bb3196
to
bc2bf3d
Compare
bc2bf3d
to
a3283e9
Compare
try { | ||
return assertModule.ok(value, message); | ||
return assertModule.ok(value !== null && value !== undefined, message); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What was the edge case that was not handled gracefully by the current implementation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i probably dont need to do this
/** | ||
* This worker listens to the `IntegratorId` queue and processes each job by: | ||
* - Retrieving the deposit information from the database based on the provided relay hash. | ||
* - Checking if the deposit record already has an integrator ID. | ||
* - If the integrator ID is not set, the worker fetches it from the transaction data. | ||
* - If found, the integrator ID is saved back into the deposit record in the database. | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These comments should be updated or removed
* - If found, the integrator ID is saved back into the deposit record in the database. | ||
*/ | ||
export class PriceWorker { | ||
public worker: Worker; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this property needed to be public?
const relayHashInfoRepository = this.postgres.getRepository( | ||
entities.RelayHashInfo, | ||
); | ||
const depositRepository = this.postgres.getRepository( | ||
entities.V3FundsDeposited, | ||
); | ||
const historicPriceRepository = this.postgres.getRepository( | ||
entities.HistoricPrice, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would make sense to make the repository variables to be class properties and set them in the constructor? This way we would reduce the scope of run()
function by not having it to deal with instantiating dependencies
const baseTokenInfo = findTokenByAddress( | ||
relayHashInfo.fillEvent.outputToken, | ||
relayHashInfo.destinationChainId, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that findTokenByAddress()
function works on a list of tokens that filters out the tokens for which we don't have the CoinGecko symbol defined in the indexer. I don't think this is necessary.
The only condition we need to check is if the token is part of the supported tokens specified in the @across-protocol/constants
. If we do this, we also don't need the CoinGecko env anymore
} | ||
const priceTime = yesterday(blockTime); | ||
const quoteCurrency = "usd"; | ||
const baseTokenInfo = findTokenByAddress( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to fetch price for input token too
* - If not, fetching the historic price from Coingecko and inserting it into the database. | ||
* - Logging errors and information at various stages of the process. | ||
*/ | ||
export class PriceWorker { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The way errors are handled in this worker should be reviewed and changed accordingly. The only situation in which the message should be discarded and execution should stop is the case when the deposit with a particular depositId
and originChainId
doesn't exist. Otherwise, we should log the error (as we do) and throw an error, so that the worker retries the message
deecf87
to
ae571cf
Compare
Signed-off-by: david <[email protected]>
Signed-off-by: david <[email protected]>
Signed-off-by: david <[email protected]>
… prices Signed-off-by: david <[email protected]>
ae571cf
to
8302d4a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Signed-off-by: david <[email protected]>
Signed-off-by: david <[email protected]>
…output price Signed-off-by: david <[email protected]>
8183e68
to
4e3756e
Compare
motivation
we want to start collecting price data in the indexer
changes
This dynamically looks up prices based on fill events, passing them to a worker to then fetch and/or cache the price at the deposit time