diff --git a/.dockerignore b/.dockerignore index 4a9627bc..e74dc447 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,3 +6,6 @@ npm-debug.log # OS Files .DS_Store + +.env +/.idea diff --git a/db/migrations/1704990176018-Data.js b/db/migrations/1706043195032-Data.js similarity index 96% rename from db/migrations/1704990176018-Data.js rename to db/migrations/1706043195032-Data.js index 0cfac53b..dd1ce963 100644 --- a/db/migrations/1704990176018-Data.js +++ b/db/migrations/1706043195032-Data.js @@ -1,7 +1,8 @@ -module.exports = class Data1704990176018 { - name = 'Data1704990176018' +module.exports = class Data1706043195032 { + name = 'Data1706043195032' async up(db) { + await db.query(`CREATE TABLE "processing_status" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, CONSTRAINT "PK_85f5e2467b74fb70fac1a053021" PRIMARY KEY ("id"))`) await db.query(`CREATE TABLE "exchange_rate" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "pair" text NOT NULL, "base" text NOT NULL, "quote" text NOT NULL, "rate" numeric NOT NULL, CONSTRAINT "PK_5c5d27d2b900ef6cdeef0398472" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_9e23a3f1bf3634820c873a0fe8" ON "exchange_rate" ("timestamp") `) await db.query(`CREATE INDEX "IDX_c61a93768eed9e58ce399bbe01" ON "exchange_rate" ("block_number") `) @@ -14,6 +15,9 @@ module.exports = class Data1704990176018 { await db.query(`CREATE TABLE "strategy_daily_yield" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "strategy" text NOT NULL, "asset" text NOT NULL, "balance" numeric NOT NULL, "balance_weight" numeric NOT NULL, "earnings" numeric NOT NULL, "earnings_change" numeric NOT NULL, "apr" numeric NOT NULL, "apy" numeric NOT NULL, CONSTRAINT "PK_b0dd2686bc95bb032ff532b3a0e" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_0ba1974747f1906e0c102cd2cd" ON "strategy_daily_yield" ("timestamp") `) await db.query(`CREATE INDEX "IDX_df364fb6e82d1feeed2a5dfffa" ON "strategy_daily_yield" ("block_number") `) + await db.query(`CREATE TABLE "native_balance" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "account" text NOT NULL, "balance" numeric NOT NULL, CONSTRAINT "PK_62f6bdfe058e52d1914d0c2c1ce" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_45fb48f831ccadcf29f404824a" ON "native_balance" ("timestamp") `) + await db.query(`CREATE INDEX "IDX_82d827c3b96fb3c5ab43068502" ON "native_balance" ("block_number") `) await db.query(`CREATE TABLE "erc20" ("id" character varying NOT NULL, "address" text NOT NULL, "name" text NOT NULL, "decimals" integer NOT NULL, "symbol" text NOT NULL, CONSTRAINT "PK_8d43ce15401ba044c55a72a8ceb" PRIMARY KEY ("id"))`) await db.query(`CREATE TABLE "erc20_holder" ("id" character varying NOT NULL, "address" text NOT NULL, "account" text NOT NULL, CONSTRAINT "PK_3adce7edbac4bcb03c662c3a1ac" PRIMARY KEY ("id"))`) await db.query(`CREATE TABLE "erc20_state" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "address" text NOT NULL, "total_supply" numeric NOT NULL, "holder_count" integer NOT NULL, CONSTRAINT "PK_eac1124b07bbdedafd4fff2f7b7" PRIMARY KEY ("id"))`) @@ -44,6 +48,9 @@ module.exports = class Data1704990176018 { await db.query(`CREATE INDEX "IDX_e8046e8a298610b97bea2e6241" ON "maverick_pool_balance" ("timestamp") `) await db.query(`CREATE INDEX "IDX_6034a437728a8ccb3b49f4a348" ON "maverick_pool_balance" ("block_number") `) await db.query(`CREATE TABLE "liquidity_source" ("id" character varying NOT NULL, "address" text NOT NULL, "type" character varying(12) NOT NULL, "token" text NOT NULL, CONSTRAINT "PK_5d25173fd367a0a7a901ee2f738" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "liquidity_daily_balance" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "address" text NOT NULL, "token" text NOT NULL, "balance" numeric NOT NULL, CONSTRAINT "PK_bf1de97f9815d851f7b4abca9ca" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_c5116bd4f5406fc2848552f0ba" ON "liquidity_daily_balance" ("timestamp") `) + await db.query(`CREATE INDEX "IDX_58d14205a5e9ad1d05f94d7bb6" ON "liquidity_daily_balance" ("block_number") `) await db.query(`CREATE TABLE "oeth" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "total_supply" numeric NOT NULL, "rebasing_supply" numeric NOT NULL, "non_rebasing_supply" numeric NOT NULL, CONSTRAINT "PK_de1d885501070dbd1ab6f8577ba" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_5b81a67229bac2d68e0dc92cc4" ON "oeth" ("timestamp") `) await db.query(`CREATE INDEX "IDX_408e5f79f83093aa5cf2b0ea32" ON "oeth" ("block_number") `) @@ -211,6 +218,7 @@ module.exports = class Data1704990176018 { } async down(db) { + await db.query(`DROP TABLE "processing_status"`) await db.query(`DROP TABLE "exchange_rate"`) await db.query(`DROP INDEX "public"."IDX_9e23a3f1bf3634820c873a0fe8"`) await db.query(`DROP INDEX "public"."IDX_c61a93768eed9e58ce399bbe01"`) @@ -223,6 +231,9 @@ module.exports = class Data1704990176018 { await db.query(`DROP TABLE "strategy_daily_yield"`) await db.query(`DROP INDEX "public"."IDX_0ba1974747f1906e0c102cd2cd"`) await db.query(`DROP INDEX "public"."IDX_df364fb6e82d1feeed2a5dfffa"`) + await db.query(`DROP TABLE "native_balance"`) + await db.query(`DROP INDEX "public"."IDX_45fb48f831ccadcf29f404824a"`) + await db.query(`DROP INDEX "public"."IDX_82d827c3b96fb3c5ab43068502"`) await db.query(`DROP TABLE "erc20"`) await db.query(`DROP TABLE "erc20_holder"`) await db.query(`DROP TABLE "erc20_state"`) @@ -253,6 +264,9 @@ module.exports = class Data1704990176018 { await db.query(`DROP INDEX "public"."IDX_e8046e8a298610b97bea2e6241"`) await db.query(`DROP INDEX "public"."IDX_6034a437728a8ccb3b49f4a348"`) await db.query(`DROP TABLE "liquidity_source"`) + await db.query(`DROP TABLE "liquidity_daily_balance"`) + await db.query(`DROP INDEX "public"."IDX_c5116bd4f5406fc2848552f0ba"`) + await db.query(`DROP INDEX "public"."IDX_58d14205a5e9ad1d05f94d7bb6"`) await db.query(`DROP TABLE "oeth"`) await db.query(`DROP INDEX "public"."IDX_5b81a67229bac2d68e0dc92cc4"`) await db.query(`DROP INDEX "public"."IDX_408e5f79f83093aa5cf2b0ea32"`) diff --git a/schema-base.graphql b/schema-base.graphql index 37b7165e..11186919 100644 --- a/schema-base.graphql +++ b/schema-base.graphql @@ -1,3 +1,9 @@ +type ProcessingStatus @entity { + id: ID! + timestamp: DateTime! + blockNumber: Int! +} + """ Any entity which has a price associated with it should have that price go in here. Prices can change very frequently and we don't want those changes on the same track @@ -72,6 +78,17 @@ type StrategyDailyYield @entity { apy: Float! } +type NativeBalance @entity { + """ + Format: 'account:blockNumber' + """ + id: ID! + timestamp: DateTime! @index + blockNumber: Int! @index + account: String! + balance: BigInt! +} + type ERC20 @entity { """ Format: 'address' @@ -211,3 +228,12 @@ type LiquiditySource @entity { token: String! } +type LiquidityDailyBalance @entity { + id: ID! + timestamp: DateTime! @index + blockNumber: Int! @index + address: String! + token: String! + balance: BigInt! +} + diff --git a/schema.graphql b/schema.graphql index 1de807ca..bcfaed38 100644 --- a/schema.graphql +++ b/schema.graphql @@ -1,5 +1,11 @@ # GENERATED, DO NOT MODIFY +type ProcessingStatus @entity { + id: ID! + timestamp: DateTime! + blockNumber: Int! +} + """ Any entity which has a price associated with it should have that price go in here. Prices can change very frequently and we don't want those changes on the same track @@ -74,6 +80,17 @@ type StrategyDailyYield @entity { apy: Float! } +type NativeBalance @entity { + """ + Format: 'account:blockNumber' + """ + id: ID! + timestamp: DateTime! @index + blockNumber: Int! @index + account: String! + balance: BigInt! +} + type ERC20 @entity { """ Format: 'address' @@ -213,6 +230,15 @@ type LiquiditySource @entity { token: String! } +type LiquidityDailyBalance @entity { + id: ID! + timestamp: DateTime! @index + blockNumber: Int! @index + address: String! + token: String! + balance: BigInt! +} + """ The OETH entity tracks the change in total supply of OETH over time. """ diff --git a/src/main-oeth.ts b/src/main-oeth.ts index b1316932..9920421c 100644 --- a/src/main-oeth.ts +++ b/src/main-oeth.ts @@ -11,6 +11,7 @@ import * as vault from './oeth/processors/vault' import * as validateOeth from './oeth/validators/validate-oeth' import { run } from './processor' import * as exchangeRatesPostProcessor from './shared/post-processors/exchange-rates' +import { processStatus } from './shared/processor-templates/processor-status' export const processor = { stateSchema: 'oeth-processor', @@ -25,7 +26,11 @@ export const processor = { strategies, exchangeRates, ], - postProcessors: [exchangeRatesPostProcessor, dailyStats], + postProcessors: [ + exchangeRatesPostProcessor, + dailyStats, + processStatus('oeth'), + ], validators: [validateOeth], } export default processor diff --git a/src/main-ogv.ts b/src/main-ogv.ts index 3cda535e..753d66b7 100644 --- a/src/main-ogv.ts +++ b/src/main-ogv.ts @@ -3,11 +3,12 @@ import * as governance from './ogv/post-processors/governance' import * as ogv from './ogv/processors/ogv' import * as ogvSupply from './ogv/processors/ogv-supply' import { run } from './processor' +import { processStatus } from './shared/processor-templates/processor-status' export const processor = { stateSchema: 'ogv-processor', processors: [ogvSupply, ogv], - postProcessors: [governance, dailyStats], + postProcessors: [governance, dailyStats, processStatus('ogv')], validators: [], } export default processor diff --git a/src/main-other.ts b/src/main-other.ts index 0af10d6e..865ab8a6 100644 --- a/src/main-other.ts +++ b/src/main-other.ts @@ -1,16 +1,32 @@ import { run } from './processor' import * as exchangeRates from './shared/post-processors/exchange-rates' +import * as liquidity from './shared/post-processors/liquidity' +import { processStatus } from './shared/processor-templates/processor-status' import * as balancer from './shared/processors/balancer' import * as curve from './shared/processors/curve' import { erc20s } from './shared/processors/erc20s' import * as liquiditySources from './shared/processors/liquidity-sources' import * as maverick from './shared/processors/maverick' +import * as native from './shared/processors/native' +import * as sushiswap from './shared/processors/sushiswap' +import * as uniswap from './shared/processors/uniswap' +import * as validate from './shared/validators/validate-shared' + +sushiswap.initialize() +uniswap.initialize() export const processor = { stateSchema: 'other-processor', - processors: [balancer, curve, maverick, ...erc20s, liquiditySources], - postProcessors: [exchangeRates], - validators: [], + processors: [ + balancer, + curve, + maverick, + native, + ...erc20s(), + liquiditySources, + ], + postProcessors: [exchangeRates, liquidity, processStatus('other')], + validators: [validate], } export default processor diff --git a/src/main-ousd.ts b/src/main-ousd.ts index 4f2d4506..1b3edac6 100644 --- a/src/main-ousd.ts +++ b/src/main-ousd.ts @@ -4,11 +4,12 @@ import * as strategies from './ousd/processors/strategies/strategies' import * as validateOusd from './ousd/validators/validate-ousd' import { run } from './processor' import * as exchangeRates from './shared/post-processors/exchange-rates' +import { processStatus } from './shared/processor-templates/processor-status' export const processor = { stateSchema: 'ousd-processor', processors: [ousd, strategies], - postProcessors: [exchangeRates, dailyStats], + postProcessors: [exchangeRates, dailyStats, processStatus('ousd')], validators: [validateOusd], } export default processor diff --git a/src/model/generated/index.ts b/src/model/generated/index.ts index b4442947..af60c4c6 100644 --- a/src/model/generated/index.ts +++ b/src/model/generated/index.ts @@ -1,7 +1,9 @@ +export * from "./processingStatus.model" export * from "./exchangeRate.model" export * from "./strategyBalance.model" export * from "./strategyYield.model" export * from "./strategyDailyYield.model" +export * from "./nativeBalance.model" export * from "./erc20.model" export * from "./erc20Holder.model" export * from "./erc20State.model" @@ -16,6 +18,7 @@ export * from "./maverickPool.model" export * from "./maverickPoolBalance.model" export * from "./liquiditySource.model" export * from "./_liquiditySourceType" +export * from "./liquidityDailyBalance.model" export * from "./oeth.model" export * from "./oethAsset.model" export * from "./oethAddress.model" diff --git a/src/model/generated/liquidityDailyBalance.model.ts b/src/model/generated/liquidityDailyBalance.model.ts new file mode 100644 index 00000000..154b250c --- /dev/null +++ b/src/model/generated/liquidityDailyBalance.model.ts @@ -0,0 +1,29 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_} from "typeorm" +import * as marshal from "./marshal" + +@Entity_() +export class LiquidityDailyBalance { + constructor(props?: Partial) { + Object.assign(this, props) + } + + @PrimaryColumn_() + id!: string + + @Index_() + @Column_("timestamp with time zone", {nullable: false}) + timestamp!: Date + + @Index_() + @Column_("int4", {nullable: false}) + blockNumber!: number + + @Column_("text", {nullable: false}) + address!: string + + @Column_("text", {nullable: false}) + token!: string + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + balance!: bigint +} diff --git a/src/model/generated/nativeBalance.model.ts b/src/model/generated/nativeBalance.model.ts new file mode 100644 index 00000000..0e76fe8d --- /dev/null +++ b/src/model/generated/nativeBalance.model.ts @@ -0,0 +1,29 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_} from "typeorm" +import * as marshal from "./marshal" + +@Entity_() +export class NativeBalance { + constructor(props?: Partial) { + Object.assign(this, props) + } + + /** + * Format: 'account:blockNumber' + */ + @PrimaryColumn_() + id!: string + + @Index_() + @Column_("timestamp with time zone", {nullable: false}) + timestamp!: Date + + @Index_() + @Column_("int4", {nullable: false}) + blockNumber!: number + + @Column_("text", {nullable: false}) + account!: string + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + balance!: bigint +} diff --git a/src/model/generated/processingStatus.model.ts b/src/model/generated/processingStatus.model.ts new file mode 100644 index 00000000..9cc56b77 --- /dev/null +++ b/src/model/generated/processingStatus.model.ts @@ -0,0 +1,17 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_} from "typeorm" + +@Entity_() +export class ProcessingStatus { + constructor(props?: Partial) { + Object.assign(this, props) + } + + @PrimaryColumn_() + id!: string + + @Column_("timestamp with time zone", {nullable: false}) + timestamp!: Date + + @Column_("int4", {nullable: false}) + blockNumber!: number +} diff --git a/src/oeth/processors/strategies/strategies.ts b/src/oeth/processors/strategies/strategies.ts index 325551bc..95a7b58c 100644 --- a/src/oeth/processors/strategies/strategies.ts +++ b/src/oeth/processors/strategies/strategies.ts @@ -20,6 +20,7 @@ import { RETH_ADDRESS, STETH_ADDRESS, WETH_ADDRESS, + addresses, } from '../../../utils/addresses' export const oethStrategies: readonly IStrategyData[] = [ @@ -27,7 +28,7 @@ export const oethStrategies: readonly IStrategyData[] = [ from: 17249899, name: 'OETH Convex ETH+OETH (AMO)', contractName: 'ConvexEthMetaStrategy', - address: '0x1827f9ea98e0bf96550b2fc20f7233277fcd7e63', + address: addresses.strategies.oeth.ConvexEthMetaStrategy, oTokenAddress: OETH_ADDRESS, kind: 'CurveAMO', curvePoolInfo: { @@ -45,7 +46,7 @@ export const oethStrategies: readonly IStrategyData[] = [ from: 17067232, name: 'OETH Frax Staking', contractName: 'FraxETHStrategy', - address: '0x3ff8654d633d4ea0fae24c52aec73b4a20d0d0e5', + address: addresses.strategies.oeth.FraxETHStrategy, oTokenAddress: OETH_ADDRESS, kind: 'Generic', base: { address: currencies.ETH, decimals: 18 }, @@ -67,7 +68,7 @@ export const oethStrategies: readonly IStrategyData[] = [ from: 18156225, name: 'OETH Aura rETH/WETH', contractName: 'BalancerMetaPoolStrategy', - address: '0x49109629ac1deb03f2e9b2fe2ac4a623e0e7dfdc', + address: addresses.strategies.oeth.BalancerMetaPoolStrategy, oTokenAddress: OETH_ADDRESS, kind: 'BalancerMetaStablePool', base: { address: currencies.ETH, decimals: 18 }, diff --git a/src/shared/post-processors/liquidity.ts b/src/shared/post-processors/liquidity.ts new file mode 100644 index 00000000..8bdde1eb --- /dev/null +++ b/src/shared/post-processors/liquidity.ts @@ -0,0 +1,62 @@ +import dayjs from 'dayjs' + +import { LiquidityDailyBalance } from '../../model' +import { Context } from '../../processor' +import { useProcessorState } from '../../utils/state' + +const useLiquidityDailyBalance = (ctx: Context) => + useProcessorState( + ctx, + 'liquidity-daily-balance', + new Map(), + ) + +export const process = async (ctx: Context) => { + const [ldb] = useLiquidityDailyBalance(ctx) + if (ldb.size > 0) { + ctx.log.debug({ count: ldb.size }, 'liquidity-daily-balance') + await ctx.store.upsert([...ldb.values()]) + } +} + +export const updateLiquidityBalances = ( + ctx: Context, + block: Context['blocks'][number], + params: { + address: string + tokens: string[] + balances: bigint[] + }, +) => { + for (let i = 0; i < params.tokens.length; i++) { + updateLiquidityBalance(ctx, block, { + address: params.address, + token: params.tokens[0], + balance: params.balances[0], + }) + } +} + +export const updateLiquidityBalance = ( + ctx: Context, + block: Context['blocks'][number], + params: Pick, +) => { + const [ldb] = useLiquidityDailyBalance(ctx) + const date = dayjs + .utc(block.header.timestamp) + .endOf('day') + .format('YYYY-MM-DD') + const id = `${params.address}:${params.token}:${date}` + ldb.set( + id, + new LiquidityDailyBalance({ + id: id, + timestamp: new Date(block.header.timestamp), + blockNumber: block.header.height, + address: params.address, + token: params.token, + balance: params.balance, + }), + ) +} diff --git a/src/shared/processor-templates/balancer/balancer.ts b/src/shared/processor-templates/balancer/balancer.ts index 441e71ca..9ea91ca5 100644 --- a/src/shared/processor-templates/balancer/balancer.ts +++ b/src/shared/processor-templates/balancer/balancer.ts @@ -17,6 +17,7 @@ import { ADDRESS_ZERO, BALANCER_VAULT } from '../../../utils/addresses' import { blockFrequencyUpdater } from '../../../utils/blockFrequencyUpdater' import { ensureExchangeRates } from '../../post-processors/exchange-rates' import { Currency } from '../../post-processors/exchange-rates/currencies' +import { updateLiquidityBalances } from '../../post-processors/liquidity' import { registerLiquiditySource } from '../../processors/liquidity-sources' const eth1 = BigInt('1000000000000000000') @@ -104,6 +105,12 @@ export const createBalancerProcessor = ( balance2: balances.length > 2 ? balances[2] : 0n, balance3: balances.length > 3 ? balances[3] : 0n, }) + updateLiquidityBalances(ctx, block, { + address: poolAddress, + tokens, + balances, + }) + result.balancerPoolBalances.push(balance) if (poolType === 'Weighted') { diff --git a/src/shared/processor-templates/curve/curve.ts b/src/shared/processor-templates/curve/curve.ts index bb9badd5..913efc60 100644 --- a/src/shared/processor-templates/curve/curve.ts +++ b/src/shared/processor-templates/curve/curve.ts @@ -5,12 +5,12 @@ import { CurvePool, CurvePoolBalance, CurvePoolRate, - LiquiditySource, LiquiditySourceType, } from '../../../model' import { Context } from '../../../processor' import { blockFrequencyUpdater } from '../../../utils/blockFrequencyUpdater' import { range } from '../../../utils/range' +import { updateLiquidityBalances } from '../../post-processors/liquidity' import { registerLiquiditySource } from '../../processors/liquidity-sources' interface ProcessResult { @@ -102,6 +102,11 @@ export const createCurveProcessor = ({ balance1: balances[1] ?? 0n, balance2: balances[2] ?? 0n, }) + updateLiquidityBalances(ctx, block, { + address, + tokens, + balances, + }) result.curvePoolBalances.push(curve) result.curvePoolRates.push( ...rates.map( diff --git a/src/shared/processor-templates/erc20/erc20.ts b/src/shared/processor-templates/erc20/erc20.ts index 2088ef00..65a7d64a 100644 --- a/src/shared/processor-templates/erc20/erc20.ts +++ b/src/shared/processor-templates/erc20/erc20.ts @@ -3,7 +3,7 @@ import { EvmBatchProcessor } from '@subsquid/evm-processor' import * as abi from '../../../abi/erc20' import { ERC20, ERC20Balance, ERC20Holder, ERC20State } from '../../../model' import { Context } from '../../../processor' -import { ADDRESS_ZERO } from '../../../utils/addresses' +import { ADDRESS_ZERO, TokenAddress } from '../../../utils/addresses' import { blockFrequencyTracker } from '../../../utils/blockFrequencyUpdater' import { LogFilter, logFilter } from '../../../utils/logFilter' import { multicall } from '../../../utils/multicall' @@ -18,12 +18,11 @@ export const createERC20Tracker = ({ intervalTracking = false, }: { from: number - address: string + address: TokenAddress accountFilter?: string[] rebaseFilters?: LogFilter[] intervalTracking?: boolean // To be used *with* `accountFilter`. }) => { - address = address.toLowerCase() accountFilter = accountFilter?.map((a) => a.toLowerCase()) if (duplicateTracker.has(address)) { throw new Error('An ERC20 tracker was already created for: ' + address) diff --git a/src/shared/processor-templates/maverick/maverick.ts b/src/shared/processor-templates/maverick/maverick.ts index 38bcc726..94beb1fb 100644 --- a/src/shared/processor-templates/maverick/maverick.ts +++ b/src/shared/processor-templates/maverick/maverick.ts @@ -10,6 +10,7 @@ import { } from '../../../model' import { Context } from '../../../processor' import { blockFrequencyUpdater } from '../../../utils/blockFrequencyUpdater' +import { updateLiquidityBalances } from '../../post-processors/liquidity' import { registerLiquiditySource } from '../../processors/liquidity-sources' // Maverick Pool Reference: https://docs.mav.xyz/guides/technical-reference/pool @@ -57,10 +58,12 @@ export const createMaverickProcessor = ({ name, address, from, + tokens, }: { name: string address: string from: number + tokens: [string, string] }) => { const update = blockFrequencyUpdater({ from }) return async (ctx: Context) => { @@ -86,6 +89,11 @@ export const createMaverickProcessor = ({ binBalanceB: binBalanceB ?? 0n, }) result.maverickPoolBalances.push(curve) + updateLiquidityBalances(ctx, block, { + address, + tokens, + balances: [binBalanceA, binBalanceB], + }) }) await ctx.store.insert(result.maverickPoolBalances) } diff --git a/src/shared/processor-templates/processor-status/index.ts b/src/shared/processor-templates/processor-status/index.ts new file mode 100644 index 00000000..02c8eb2e --- /dev/null +++ b/src/shared/processor-templates/processor-status/index.ts @@ -0,0 +1 @@ +export * from './processor-status' diff --git a/src/shared/processor-templates/processor-status/processor-status.ts b/src/shared/processor-templates/processor-status/processor-status.ts new file mode 100644 index 00000000..dec2abd2 --- /dev/null +++ b/src/shared/processor-templates/processor-status/processor-status.ts @@ -0,0 +1,27 @@ +import { ProcessingStatus } from '../../../model' +import { Context } from '../../../processor' + +const processorIds = new Set() + +export const processStatus = (id: string) => { + if (processorIds.has(id)) { + throw new Error(`Already have a \`processStatus\` with id: ${id}`) + } else { + processorIds.add(id) + } + + return { + async process(ctx: Context) { + const header = ctx.blocks[ctx.blocks.length - 1].header + if (header) { + await ctx.store.upsert([ + new ProcessingStatus({ + id, + blockNumber: header.height, + timestamp: new Date(header.timestamp), + }), + ]) + } + }, + } +} diff --git a/src/shared/processors/erc20s.ts b/src/shared/processors/erc20s.ts index e2ec4fb9..cebfe76d 100644 --- a/src/shared/processors/erc20s.ts +++ b/src/shared/processors/erc20s.ts @@ -1,99 +1,149 @@ import * as otoken from '../../abi/otoken' -import { LiquiditySourceType } from '../../model' -import { OETH_ADDRESS } from '../../utils/addresses' +import { + OETH_ADDRESS, + OETH_DRIPPER_ADDRESS, + OETH_VAULT_ADDRESS, + OUSD_VAULT_ADDRESS, + TokenSymbol, + oethStrategyArray, + ousdStrategyArray, + tokens, +} from '../../utils/addresses' import { logFilter } from '../../utils/logFilter' import { createERC20Tracker } from '../processor-templates/erc20' -import { registerLiquiditySource } from './liquidity-sources' // TODO: Would be nice if interested parties could register their desires here from other parts of the code, // allowing multiple declarations of need without issue. -const tracks: Parameters[0][] = [ +let initialized = false + +const tracks: Record[0]> = { // Origin Specific - { + OGN: { from: 15350225, // 6436154, - address: '0x8207c1ffc5b6804f6024322ccf34f29c3541ae26', // OGN + address: tokens.OGN, accountFilter: [ '0x2eae0cae2323167abf78462e0c0686865c67a655', // Origin: Team Distribution (starts at block 15350225) ], intervalTracking: true, }, - { + OGV: { from: 15350225, // 14439231, - address: '0x9c354503c38481a7a7a51629142963f98ecc12d0', // OGV + address: tokens.OGV, accountFilter: [ '0x2eae0cae2323167abf78462e0c0686865c67a655', // Origin: Team Distribution (starts at block 15350225) ], intervalTracking: true, }, + OETH: { + from: 16935276, + address: tokens.OETH, + rebaseFilters: [ + logFilter({ + address: [OETH_ADDRESS], + topic0: [otoken.events.TotalSupplyUpdatedHighres.topic], + transaction: true, + range: { from: 16935276 }, + }), + ], + }, // OUSD Related - { + USDT: { from: 11362821, - address: '0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT + address: tokens.USDT, accountFilter: [ + ...ousdStrategyArray, + OUSD_VAULT_ADDRESS, '0x3ed3b47dd13ec9a98b44e6204a523e766b225811', // aUSDT '0xf650c3d88d12db855b8bf7d11be6c55a4e07dcc9', // cUSDT ], intervalTracking: true, }, - { + USDC: { from: 11367200, - address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC + address: tokens.USDC, accountFilter: [ + ...ousdStrategyArray, + OUSD_VAULT_ADDRESS, '0xbcca60bb61934080951369a648fb03df4f96263c', // aUSDC '0x39aa39c021dfbae8fac545936693ac917d5e7563', // cUSDC ], intervalTracking: true, }, - { + DAI: { from: 11367184, - address: '0x6b175474e89094c44da98b954eedeac495271d0f', // DAI + address: tokens.DAI, accountFilter: [ + ...ousdStrategyArray, + OUSD_VAULT_ADDRESS, '0x028171bca77440897b824ca71d1c56cac55b68a3', // aDAI '0x5d3a536e4d6dbd6114cc1ead35777bab948e3643', // cDAI ], intervalTracking: true, }, // OETH Related - { + WETH: { from: 16933090, // oeth deploy date - address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH + address: tokens.WETH, accountFilter: [ + ...oethStrategyArray, + OETH_VAULT_ADDRESS, + OETH_DRIPPER_ADDRESS, '0xa4e0faA58465A2D369aa21B3e42d43374c6F9613', // Uniswap wstETH/WETH '0x109830a1aaad605bbf02a9dfa7b0b92ec2fb7daa', // Uniswap rETH/WETH ], intervalTracking: true, }, - { + rETH: { from: 16933090, // oeth deploy date - address: '0xae78736cd615f374d3085123a210448e74fc6393', // rETH + address: tokens.rETH, accountFilter: [ + ...oethStrategyArray, + OETH_VAULT_ADDRESS, '0xa4e0faA58465A2D369aa21B3e42d43374c6F9613', // Uniswap rETH/WETH + '0x553e9c493678d8606d6a5ba284643db2110df823', // Uniswap rETH/WETH ], intervalTracking: true, }, - { + wstETH: { from: 16933090, // oeth deploy date - address: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', // wstETH + address: tokens.wstETH, accountFilter: [ + ...oethStrategyArray, + OETH_VAULT_ADDRESS, '0x109830a1aaad605bbf02a9dfa7b0b92ec2fb7daa', // Uniswap wstETH/WETH ], intervalTracking: true, }, -] + stETH: { + from: 16933090, // oeth deploy date + address: tokens.stETH, + accountFilter: [...oethStrategyArray, OETH_VAULT_ADDRESS], + intervalTracking: true, + }, + frxETH: { + from: 16933090, // oeth deploy date + address: tokens.frxETH, + accountFilter: [...oethStrategyArray, OETH_VAULT_ADDRESS], + intervalTracking: true, + }, +} -export const erc20s = [ - ...tracks.map(createERC20Tracker), - createERC20Tracker({ - from: 16935276, - address: OETH_ADDRESS, - rebaseFilters: [ - logFilter({ - address: [OETH_ADDRESS], - topic0: [otoken.events.TotalSupplyUpdatedHighres.topic], - transaction: true, - range: { from: 16935276 }, - }), - ], - }), -] +// This is a function to allow others to subscribe to balance tracking +export const erc20s = () => { + initialized = true + return Object.values(tracks).map(createERC20Tracker) +} + +export const addERC20Processing = (symbol: TokenSymbol, account: string) => { + if (initialized) { + throw new Error('erc20s already initialized, check load order') + } + const track = tracks[symbol] + if (track) { + // If there is no `accountFilter` then it is OK to have this as a noop. (we already want everything) + track.accountFilter?.push(account) + } else { + throw new Error(`Symbol ${symbol} not added to \`tracks\``) + } +} diff --git a/src/shared/processors/liquidity-sources.ts b/src/shared/processors/liquidity-sources.ts index 80cca953..a1761ea9 100644 --- a/src/shared/processors/liquidity-sources.ts +++ b/src/shared/processors/liquidity-sources.ts @@ -54,15 +54,3 @@ registerLiquiditySource( LiquiditySourceType.Compound, '0x6b175474e89094c44da98b954eedeac495271d0f', // DAI ) - -registerLiquiditySource( - '0xa4e0faA58465A2D369aa21B3e42d43374c6F9613', // Uniswap rETH/WETH - LiquiditySourceType.UniswapPool, - '0xae78736cd615f374d3085123a210448e74fc6393', // rETH -) - -registerLiquiditySource( - '0x109830a1aaad605bbf02a9dfa7b0b92ec2fb7daa', // Uniswap wstETH/WETH - LiquiditySourceType.UniswapPool, - '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', // wstETH -) diff --git a/src/shared/processors/maverick.ts b/src/shared/processors/maverick.ts index 3d6bf2cc..d6fbee79 100644 --- a/src/shared/processors/maverick.ts +++ b/src/shared/processors/maverick.ts @@ -26,6 +26,12 @@ const pools: (Parameters['0'] & from: Math.max(17216790, oethDeployFrom), tokens: [tokens.rETH, tokens.ETH], }, + { + name: 'OETH-ETH', + address: '0xa51822839bfa4685b27ad60305264a37eae28a68', + from: Math.max(17444830, oethDeployFrom), + tokens: [tokens.OETH, tokens.ETH], + }, ] export const setup = (processor: EvmBatchProcessor) => { diff --git a/src/shared/processors/native.ts b/src/shared/processors/native.ts new file mode 100644 index 00000000..70be1433 --- /dev/null +++ b/src/shared/processors/native.ts @@ -0,0 +1,60 @@ +import { EvmBatchProcessor } from '@subsquid/evm-processor' +import { minBy } from 'lodash' + +import { NativeBalance } from '../../model' +import { Context } from '../../processor' +import { + OETH_VAULT_ADDRESS, + OUSD_VAULT_ADDRESS, + oethStrategyArray, + ousdStrategyArray, +} from '../../utils/addresses' +import { blockFrequencyUpdater } from '../../utils/blockFrequencyUpdater' +import { getNativeBalances } from '../../utils/nativeBalance' + +/** + * We end up with a lot of initial 0 balance entities. + * This isn't ideal but probably not worth improving. + */ + +const tracks = [ + { + from: 19072024, // As of this block, none of these addresses have ever held ETH. + addresses: [ + ...ousdStrategyArray, + OUSD_VAULT_ADDRESS, + ...oethStrategyArray, + OETH_VAULT_ADDRESS, + ], + }, +] + +export const from = minBy(tracks, 'from')?.from ?? 19072024 + +export const setup = (processor: EvmBatchProcessor) => { + processor.includeAllBlocks({ from }) +} + +const updater = blockFrequencyUpdater({ from }) +export const process = async (ctx: Context) => { + const results: NativeBalance[] = [] + await updater(ctx, async (ctx: Context, block: Context['blocks'][number]) => { + const addresses = tracks + .filter((track) => track.from <= block.header.height) + .flatMap((track) => track.addresses) + const balances = await getNativeBalances(ctx, addresses, block) + results.push( + ...addresses.map( + (account, index) => + new NativeBalance({ + id: `${account}:${block.header.height}`, + blockNumber: block.header.height, + timestamp: new Date(block.header.timestamp), + account, + balance: balances[index], + }), + ), + ) + }) + await ctx.store.insert(results) +} diff --git a/src/shared/processors/sushiswap.ts b/src/shared/processors/sushiswap.ts new file mode 100644 index 00000000..d2536831 --- /dev/null +++ b/src/shared/processors/sushiswap.ts @@ -0,0 +1,24 @@ +import { LiquiditySourceType } from '../../model' +import { addresses } from '../../utils/addresses' +import { addERC20Processing } from './erc20s' +import { registerLiquiditySource } from './liquidity-sources' + +const pools = [ + { + address: '0xf3e920bd7665d5e8e408dc4a3a765ade52314aaf', + tokens: ['OETH', 'WETH'], + }, +] as const + +export const initialize = () => { + for (const pool of pools) { + for (const token of pool.tokens) { + registerLiquiditySource( + pool.address, + LiquiditySourceType.UniswapPool, + addresses.tokens[token], + ) + addERC20Processing(token, pool.address) + } + } +} diff --git a/src/shared/processors/uniswap.ts b/src/shared/processors/uniswap.ts new file mode 100644 index 00000000..74b2a79c --- /dev/null +++ b/src/shared/processors/uniswap.ts @@ -0,0 +1,36 @@ +import { LiquiditySourceType } from '../../model' +import { addresses } from '../../utils/addresses' +import { addERC20Processing } from './erc20s' +import { registerLiquiditySource } from './liquidity-sources' + +const pools = [ + { + address: '0xa4e0faA58465A2D369aa21B3e42d43374c6F9613', + tokens: ['rETH', 'WETH'], + }, + { + address: '0x553e9c493678d8606d6a5ba284643db2110df823', + tokens: ['rETH', 'WETH'], + }, + { + address: '0x109830a1aaad605bbf02a9dfa7b0b92ec2fb7daa', + tokens: ['wstETH', 'WETH'], + }, + { + address: '0x52299416c469843f4e0d54688099966a6c7d720f', + tokens: ['OETH', 'WETH'], + }, +] as const + +export const initialize = () => { + for (const pool of pools) { + for (const token of pool.tokens) { + registerLiquiditySource( + pool.address, + LiquiditySourceType.UniswapPool, + addresses.tokens[token], + ) + addERC20Processing(token, pool.address) + } + } +} diff --git a/src/shared/validators/validate-shared.ts b/src/shared/validators/validate-shared.ts index 809503af..31baace7 100644 --- a/src/shared/validators/validate-shared.ts +++ b/src/shared/validators/validate-shared.ts @@ -2,7 +2,7 @@ import { Entity, EntityClass } from '@subsquid/typeorm-store' import assert from 'assert' import { sortBy } from 'lodash' -import { ERC20Balance } from '../../model' +import { ERC20Balance, ERC20State } from '../../model' import { Block, Context } from '../../processor' import { env } from '../../utils/env' import { jsonify } from '../../utils/jsonify' @@ -14,6 +14,7 @@ let firstBlock = true export const process = async (ctx: Context) => { if (env.BLOCK_FROM) return for (const block of ctx.blocks) { + await validateExpectations(ctx, block, ERC20State, expectations.erc20States) await validateExpectations( ctx, block, @@ -91,13 +92,120 @@ const e = (arr: any[]) => { } const expectations = { + erc20States: e([ + { + totalSupply: '1000000000000000000000000000', + timestamp: '2022-08-17T23:57:22.000000Z', + id: '15361708:0x8207c1ffc5b6804f6024322ccf34f29c3541ae26', + holderCount: 1, + blockNumber: 15361708, + address: '0x8207c1ffc5b6804f6024322ccf34f29c3541ae26', + }, + { + totalSupply: '38364302476507778835584', + timestamp: '2023-07-17T07:17:35.000000Z', + id: '17711440:0x856c4efb76c1d1ae02e20ceb03a2a6a08b0b8dc3', + holderCount: 348, + blockNumber: 17711440, + address: '0x856c4efb76c1d1ae02e20ceb03a2a6a08b0b8dc3', + }, + { + totalSupply: '38364302476507778835584', + timestamp: '2023-07-17T10:39:35.000000Z', + id: '17712439:0x856c4efb76c1d1ae02e20ceb03a2a6a08b0b8dc3', + holderCount: 349, + blockNumber: 17712439, + address: '0x856c4efb76c1d1ae02e20ceb03a2a6a08b0b8dc3', + }, + { + totalSupply: '38364302476507778835584', + timestamp: '2023-07-17T10:41:47.000000Z', + id: '17712450:0x856c4efb76c1d1ae02e20ceb03a2a6a08b0b8dc3', + holderCount: 348, + blockNumber: 17712450, + address: '0x856c4efb76c1d1ae02e20ceb03a2a6a08b0b8dc3', + }, + { + totalSupply: '38406082572640197584644', + timestamp: '2024-01-01T17:38:11.000000Z', + id: '18914116:0x856c4efb76c1d1ae02e20ceb03a2a6a08b0b8dc3', + holderCount: 592, + blockNumber: 18914116, + address: '0x856c4efb76c1d1ae02e20ceb03a2a6a08b0b8dc3', + }, + ]), erc20Balances: e([ { - id: '11424000:0x6b175474e89094c44da98b954eedeac495271d0f:0x028171bca77440897b824ca71d1c56cac55b68a3', - blockNumber: 11424000, + id: '11362821:0xdac17f958d2ee523a2206206994597c13d831ec7:0xf650c3d88d12db855b8bf7d11be6c55a4e07dcc9', + balance: '11457161992607', + timestamp: '2020-11-30T22:20:30.000000Z', + blockNumber: 11362821, + address: '0xdac17f958d2ee523a2206206994597c13d831ec7', + account: '0xf650c3d88d12db855b8bf7d11be6c55a4e07dcc9', + }, + { + id: '11378250:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48:0xbcca60bb61934080951369a648fb03df4f96263c', + balance: '9900888622', + timestamp: '2020-12-03T07:18:44.000000Z', + blockNumber: 11378250, + address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + account: '0xbcca60bb61934080951369a648fb03df4f96263c', + }, + { + id: '17149443:0x9c354503c38481a7a7a51629142963f98ecc12d0:0x2eae0cae2323167abf78462e0c0686865c67a655', + balance: '7931542000000000000000000', + timestamp: '2023-04-29T04:41:23.000000Z', + blockNumber: 17149443, + address: '0x9c354503c38481a7a7a51629142963f98ecc12d0', + account: '0x2eae0cae2323167abf78462e0c0686865c67a655', + }, + { + id: '17288778:0x856c4efb76c1d1ae02e20ceb03a2a6a08b0b8dc3:0xab7C7E7ac51f70dd959f3541316dBd715773158B', + balance: '5023909062476346', + timestamp: '2023-05-18T20:22:11.000000Z', + blockNumber: 17288778, + address: '0x856c4efb76c1d1ae02e20ceb03a2a6a08b0b8dc3', + account: '0xab7c7e7ac51f70dd959f3541316dbd715773158b', + }, + { + id: '17572288:0x856c4efb76c1d1ae02e20ceb03a2a6a08b0b8dc3:0x67dFCfd5D79A5D1bE7eE295233C2C966eF3A403E', + balance: '9859680088763371928', + timestamp: '2023-06-27T17:52:23.000000Z', + blockNumber: 17572288, + address: '0x856c4efb76c1d1ae02e20ceb03a2a6a08b0b8dc3', + account: '0x67dfcfd5d79a5d1be7ee295233c2c966ef3a403e', + }, + { + id: '18732788:0x856c4efb76c1d1ae02e20ceb03a2a6a08b0b8dc3:0xad3b67BCA8935Cb510C8D18bD45F0b94F54A968f', + balance: '56178503424784614', + timestamp: '2023-12-07T06:59:59.000000Z', + blockNumber: 18732788, + address: '0x856c4efb76c1d1ae02e20ceb03a2a6a08b0b8dc3', + account: '0xad3b67bca8935cb510c8d18bd45f0b94f54a968f', + }, + { + id: '19029947:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48:0x39aa39c021dfbae8fac545936693ac917d5e7563', + balance: '28479817605372', + timestamp: '2024-01-17T23:42:59.000000Z', + blockNumber: 19029947, + address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + account: '0x39aa39c021dfbae8fac545936693ac917d5e7563', + }, + { + id: '19029947:0x6b175474e89094c44da98b954eedeac495271d0f:0x028171bca77440897b824ca71d1c56cac55b68a3', + balance: '14897045505942361213471443', + timestamp: '2024-01-17T23:42:59.000000Z', + blockNumber: 19029947, address: '0x6b175474e89094c44da98b954eedeac495271d0f', account: '0x028171bca77440897b824ca71d1c56cac55b68a3', - balance: '345330738036343186917749', + }, + { + id: '19086303:0xdac17f958d2ee523a2206206994597c13d831ec7:0xf650c3d88d12db855b8bf7d11be6c55a4e07dcc9', + balance: '40175463143636', + timestamp: '2024-01-25T21:26:35.000000Z', + blockNumber: 19086303, + address: '0xdac17f958d2ee523a2206206994597c13d831ec7', + account: '0xf650c3d88d12db855b8bf7d11be6c55a4e07dcc9', }, ]), } as const diff --git a/src/utils/addresses.ts b/src/utils/addresses.ts index 6a88a30b..e476a625 100644 --- a/src/utils/addresses.ts +++ b/src/utils/addresses.ts @@ -1,5 +1,4 @@ // Lowercase Addresses - export const ADDRESS_ZERO = '0x0000000000000000000000000000000000000000' export const ETH_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' @@ -31,24 +30,6 @@ export const SFRXETH_ADDRESS = '0xac3e018457b222d93114458476f3e3416abbe38f' export const BAL_ADDRESS = '0xba100000625a3754423978a60c9317c58a424e3d' -export const tokens = { - ETH: ETH_ADDRESS, - OUSD: OUSD_ADDRESS, - DAI: DAI_ADDRESS, - USDT: USDT_ADDRESS, - USDC: USDC_ADDRESS, - LUSD: LUSD_ADDRESS, - CRV3: CRV3_ADDRESS, - OETH: OETH_ADDRESS, - WETH: WETH_ADDRESS, - stETH: STETH_ADDRESS, - wstETH: WSTETH_ADDRESS, - rETH: RETH_ADDRESS, - frxETH: FRXETH_ADDRESS, - sfrxETH: SFRXETH_ADDRESS, - BAL: BAL_ADDRESS, -} - export const OUSD_VAULT_ERC20_ADDRESSES = [ DAI_ADDRESS, USDC_ADDRESS, @@ -90,3 +71,95 @@ export const OGN_ADDRESS = '0x8207c1ffc5b6804f6024322ccf34f29c3541ae26' export const OGV_ADDRESS = '0x9c354503c38481a7a7a51629142963f98ecc12d0' export const VEOGV_ADDRESS = '0x0c4576ca1c365868e162554af8e385dc3e7c66d9' + +// Token Helper Objects & Types +export const tokens = { + // Origin ERC20 Tokens + OGN: OGN_ADDRESS, + OGV: OGV_ADDRESS, + veOGV: VEOGV_ADDRESS, + + // Origin OTokens + OETH: OETH_ADDRESS, + OUSD: OUSD_ADDRESS, + + // Dollar + DAI: DAI_ADDRESS, + USDT: USDT_ADDRESS, + USDC: USDC_ADDRESS, + LUSD: LUSD_ADDRESS, + CRV3: CRV3_ADDRESS, + + // Ether + ETH: ETH_ADDRESS, + WETH: WETH_ADDRESS, + stETH: STETH_ADDRESS, + wstETH: WSTETH_ADDRESS, + rETH: RETH_ADDRESS, + frxETH: FRXETH_ADDRESS, + sfrxETH: SFRXETH_ADDRESS, + + // Non-stable + BAL: BAL_ADDRESS, +} as const +export type TokenSymbol = keyof typeof tokens +export type TokenAddress = (typeof tokens)[TokenSymbol] + +// Strategy Helper Objects & Types +export const strategies = { + oeth: { + ConvexEthMetaStrategy: '0x1827f9ea98e0bf96550b2fc20f7233277fcd7e63', + FraxETHStrategy: '0x3ff8654d633d4ea0fae24c52aec73b4a20d0d0e5', + MorphoAaveStrategy: '0xc1fc9e5ec3058921ea5025d703cbe31764756319', + BalancerMetaPoolStrategy: '0x49109629ac1deb03f2e9b2fe2ac4a623e0e7dfdc', + }, + ousd: { + ConvexOUSDMetaStrategy: '0x89eb88fedc50fc77ae8a18aad1ca0ac27f777a90', + AaveStrategy: '0x5e3646a1db86993f73e6b74a57d8640b69f7e259', + MorphoCompoundStrategy: '0x5a4eee58744d1430876d5ca93cab5ccb763c037d', + MorphoAaveStrategy: '0x79f2188ef9350a1dc11a062cca0abe90684b0197', + FluxStrategy: '0x76bf500b6305dc4ea851384d3d5502f1c7a0ed44', + Generalized4626Strategy: '0x6b69b755c629590ed59618a2712d8a2957ca98fc', + CompoundStrategy: '0x9c459eeb3fa179a40329b81c1635525e9a0ef094', // Deprecated + ConvexStrategy: '0xea2ef2e2e5a749d4a66b41db9ad85a38aa264cb3', // Deprecated + LUSDMetaStrategy: '0x7A192DD9Cc4Ea9bdEdeC9992df74F1DA55e60a19', // Deprecated + }, +} as const + +export type OETHStrategyKey = keyof typeof strategies.oeth +export type OETHStrategyAddress = (typeof strategies.oeth)[OETHStrategyKey] +export const oethStrategyArray = Object.values(strategies.oeth) +export type OUSDStrategyKey = keyof typeof strategies.ousd +export type OUSDStrategyAddress = (typeof strategies.ousd)[OUSDStrategyKey] +export const ousdStrategyArray = Object.values(strategies.ousd) + +export const oeth = { + address: OETH_ADDRESS, + vault: OETH_VAULT_ADDRESS, + dripper: OETH_DRIPPER_ADDRESS, + zapper: OETH_ZAPPER_ADDRESS, + harvester: OETH_HARVESTER_ADDRESS, + vaultTokens: OETH_VAULT_ERC20_ADDRESSES, +} + +export const ousd = { + address: OUSD_ADDRESS, + vault: OUSD_VAULT_ADDRESS, + dripper: OUSD_DRIPPER_ADDRESS, + harvester: OUSD_HARVESTER_ADDRESS, + vaultTokens: OUSD_VAULT_ERC20_ADDRESSES, +} + +export const ogv = { + ogv: OGV_ADDRESS, + veOGV: VEOGV_ADDRESS, + governance: GOVERNANCE_ADDRESS, +} + +export const addresses = { + tokens, + strategies, + oeth, + ousd, + ogv, +} diff --git a/src/utils/getEthBalance.ts b/src/utils/getEthBalance.ts deleted file mode 100644 index 9b19c691..00000000 --- a/src/utils/getEthBalance.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { hexToBigInt } from 'viem' - -import { Context } from '../processor' - -export const getEthBalance = async ( - ctx: Context, - address: string, - block: Context['blocks']['0'], -) => - await ctx._chain.client - .call('eth_getBalance', [address, block.header.hash]) - .then((r: `0x${string}`) => hexToBigInt(r)) diff --git a/src/utils/nativeBalance.ts b/src/utils/nativeBalance.ts new file mode 100644 index 00000000..df1c63d8 --- /dev/null +++ b/src/utils/nativeBalance.ts @@ -0,0 +1,29 @@ +import { hexToBigInt } from 'viem' + +import { Context } from '../processor' + +export const getNativeBalance = async ( + ctx: Context, + address: string, + block: Context['blocks']['0'], +) => { + return await ctx._chain.client + .call('eth_getBalance', [address, block.header.hash]) + .then((r: `0x${string}`) => hexToBigInt(r)) +} + +export const getNativeBalances = async ( + ctx: Context, + addresses: string[], + block: Context['blocks']['0'], +) => { + if (!addresses.length) return [] + return await ctx._chain.client + .batchCall( + addresses.map((address) => ({ + method: 'eth_getBalance', + params: [address, block.header.hash], + })), + ) + .then((rs: `0x${string}`[]) => rs.map((r) => hexToBigInt(r))) +}