Skip to content

Commit

Permalink
erc4626 price handler (#1529)
Browse files Browse the repository at this point in the history
  • Loading branch information
franzns authored Jan 28, 2025
1 parent 9d1a4b3 commit ed867e5
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/curvy-experts-behave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'backend': patch
---

add generalized erc4626 price handler
3 changes: 3 additions & 0 deletions modules/pool/lib/apr-data-sources/yb-tokens-apr.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ export class YbTokensAprService implements PoolAprService {
});

for (const pool of poolsWithYbTokensExpanded) {
if (pool.address === '0x43026d483f42fb35efe03c20b251142d022783f2') {
console.log('found');
}
if (!pool.dynamicData) {
continue;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { TokenPriceHandler } from '../../token-types';
import { PrismaTokenWithTypes } from '../../../../prisma/prisma-types';
import { timestampRoundedUpToNearestHour } from '../../../common/time';
import { prisma } from '../../../../prisma/prisma-client';
import _ from 'lodash';
import { tokenAndPrice, updatePrices } from './price-handler-helper';
import { Chain } from '@prisma/client';

export class ERC4626PriceHandlerService implements TokenPriceHandler {
public readonly exitIfFails = false;
public readonly id = 'ERC4626PriceHandlerService';

private getAcceptedTokens(tokens: PrismaTokenWithTypes[]): PrismaTokenWithTypes[] {
return tokens.filter((token) => token.types.includes('ERC4626'));
}

public async updatePricesForTokens(tokens: PrismaTokenWithTypes[]): Promise<PrismaTokenWithTypes[]> {
const acceptedTokens = this.getAcceptedTokens(tokens);

const tokenAndPrices: tokenAndPrice[] = [];
const timestamp = timestampRoundedUpToNearestHour();

// Group tokens by chain
const tokensByChain = _.groupBy(acceptedTokens, 'chain');

const updatedTokens: PrismaTokenWithTypes[] = [];
for (const chain in tokensByChain) {
// Use existing rates for erc4626 tokens);

const erc4626TokensForChain = tokensByChain[chain];
if (!erc4626TokensForChain.length) {
continue;
}

// Fetch rates for aave tokens
const underlying = erc4626TokensForChain
.map((token) => token.underlyingTokenAddress)
.filter((address) => address !== null);

const underlyingPrices = await prisma.prismaTokenCurrentPrice.findMany({
where: { tokenAddress: { in: _.uniq(underlying) }, chain: chain as Chain },
});

const underlyingMap = _.zipObject(
underlyingPrices.map((p) => p.tokenAddress),
underlyingPrices,
);

const rateMap = _.zipObject(
erc4626TokensForChain.map((token) => token.address),
erc4626TokensForChain.map((token) => Number(token.unwrapRate)),
);

for (const erc4626Token of erc4626TokensForChain) {
const dbToken = acceptedTokens.find((t) => t.address === erc4626Token.address);
const underlying = erc4626Token.underlyingTokenAddress;
if (!dbToken || !underlying || !underlyingMap[underlying]) {
console.error(
`ERC4626PriceHandlerService: Underlying price for ERC4626 ${erc4626Token.symbol} (underlying address: ${erc4626Token.underlyingTokenAddress}) on ${chain} not found`,
);
continue;
}
try {
const price = Number((rateMap[erc4626Token.address] * underlyingMap[underlying].price).toFixed(2));

updatedTokens.push(dbToken);
tokenAndPrices.push({
address: erc4626Token.address,
chain: erc4626Token.chain,
price,
});
} catch (e: any) {
console.error('ERC4626 price failed for', erc4626Token.address, chain, e.message);
}
}
}

await updatePrices(this.id, tokenAndPrices, timestamp);

return updatedTokens;
}
}
2 changes: 2 additions & 0 deletions modules/token/lib/token-price.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ import { MorphoPriceHandlerService } from './token-price-handlers/morpho-price-h
import { RektTokensHandlerService } from './token-price-handlers/rekt-tokens-handler.service';
import config from '../../../config';
import { BeetsPriceHandlerService } from './token-price-handlers/beets-price-handler.service';
import { ERC4626PriceHandlerService } from './token-price-handlers/erc4626-price-handler.service';

export class TokenPriceService {
cache: CacheClass<string, any> = new Cache<string, any>();
private readonly priceHandlers: TokenPriceHandler[] = [
new RektTokensHandlerService(),
new ERC4626PriceHandlerService(),
new FbeetsPriceHandlerService(),
new BeetsPriceHandlerService(),
new ClqdrPriceHandlerService(),
Expand Down
8 changes: 4 additions & 4 deletions modules/token/token.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ describe('Token service', () => {
await tokenService.updateTokenPrices(['SONIC']);
const prices = await tokenService.getCurrentTokenPrices(['SONIC']);

console.log(prices.find((price) => price.tokenAddress === '0x2d0e0814e62d80056181f5cd932274405966e4f0'));
console.log(prices.find((price) => price.tokenAddress === '0x541fd749419ca806a8bc7da8ac23d346f2df8b77'));
}, 500000);

test('sync tokens from pool tokens', async () => {
initRequestScopedContext();
setRequestScopedContextValue('chainId', '34443');
await tokenService.syncTokenContentData();
});
setRequestScopedContextValue('chainId', '146');
await tokenService.syncTokenContentData('SONIC');
}, 1000000);

test('get tokens', async () => {
initRequestScopedContext();
Expand Down
3 changes: 3 additions & 0 deletions tasks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { chainIdToChain } from '../modules/network/chain-id-to-chain';
import { backsyncSwaps } from './subgraph-syncing/backsync-swaps';
import { poolService } from '../modules/pool/pool.service';
import { initRequestScopedContext, setRequestScopedContextValue } from '../modules/context/request-scoped-context';
import { tokenService } from '../modules/token/token.service';

// TODO needed?
const sftmxController = SftmxController();
Expand Down Expand Up @@ -140,6 +141,8 @@ async function run(job: string = process.argv[2], chainId: string = process.argv
initRequestScopedContext();
setRequestScopedContextValue('chainId', chainId);
return poolService.updatePoolAprs(chain);
} else if (job === 'update-prices') {
return tokenService.updateTokenPrices([chain]);
}
// Maintenance
else if (job === 'sync-fx-quote-tokens') {
Expand Down

0 comments on commit ed867e5

Please sign in to comment.