diff --git a/db/migrations/1701729003798-Data.js b/db/migrations/1701813557098-Data.js similarity index 89% rename from db/migrations/1701729003798-Data.js rename to db/migrations/1701813557098-Data.js index 921f625b..1823b990 100644 --- a/db/migrations/1701729003798-Data.js +++ b/db/migrations/1701813557098-Data.js @@ -1,5 +1,5 @@ -module.exports = class Data1701729003798 { - name = 'Data1701729003798' +module.exports = class Data1701813557098 { + name = 'Data1701813557098' async up(db) { 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"))`) @@ -43,7 +43,7 @@ module.exports = class Data1701729003798 { await db.query(`CREATE INDEX "IDX_b1a448045d1ed9d655b679a371" ON "oethapy" ("timestamp") `) await db.query(`CREATE INDEX "IDX_6b8a7a706a0701e659a7d81508" ON "oethapy" ("block_number") `) await db.query(`CREATE INDEX "IDX_c0c03168bb0139e3cffda4f00e" ON "oethapy" ("tx_hash") `) - await db.query(`CREATE TABLE "oeth_rebase" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "tx_hash" text NOT NULL, "total_supply" numeric NOT NULL, "rebasing_credits" numeric NOT NULL, "rebasing_credits_per_token" numeric NOT NULL, "fee" numeric NOT NULL, "yield" numeric NOT NULL, "apy_id" character varying, CONSTRAINT "PK_5f8f4dd071caf685b4ac2d54de3" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "oeth_rebase" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "tx_hash" text NOT NULL, "total_supply" numeric NOT NULL, "rebasing_credits" numeric NOT NULL, "rebasing_credits_per_token" numeric NOT NULL, "fee_eth" numeric NOT NULL, "fee_usd" numeric NOT NULL, "yield_eth" numeric NOT NULL, "yield_usd" numeric NOT NULL, "apy_id" character varying, CONSTRAINT "PK_5f8f4dd071caf685b4ac2d54de3" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_fbb7b3f2fff9896eb683b86de7" ON "oeth_rebase" ("timestamp") `) await db.query(`CREATE INDEX "IDX_d3255d02d9407bba89380d01fa" ON "oeth_rebase" ("block_number") `) await db.query(`CREATE INDEX "IDX_8b6bb0243472af88612fe6a01f" ON "oeth_rebase" ("tx_hash") `) @@ -77,7 +77,7 @@ module.exports = class Data1701729003798 { await db.query(`CREATE INDEX "IDX_6c7096c96a000d8471256ca8fc" ON "oeth_strategy_daily_stat" ("daily_stat_id_id") `) await db.query(`CREATE TABLE "oeth_collateral_daily_stat" ("id" character varying NOT NULL, "symbol" text NOT NULL, "amount" numeric NOT NULL, "price" numeric NOT NULL, "value" numeric NOT NULL, "daily_stat_id_id" character varying, CONSTRAINT "PK_5fb23d7bae30dffe4543e7aa069" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_a90045de50406be7bd56efd3ea" ON "oeth_collateral_daily_stat" ("daily_stat_id_id") `) - await db.query(`CREATE TABLE "oeth_daily_stat" ("id" character varying NOT NULL, "block_number" integer NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "apr" numeric NOT NULL, "apy" numeric NOT NULL, "apy7_day_avg" numeric NOT NULL, "apy14_day_avg" numeric NOT NULL, "apy30_day_avg" numeric NOT NULL, "total_supply" numeric NOT NULL, "total_supply_usd" numeric NOT NULL, "rebasing_supply" numeric NOT NULL, "non_rebasing_supply" numeric NOT NULL, "amo_supply" numeric NOT NULL, "dripper_weth" numeric NOT NULL, "wrapped_supply" numeric NOT NULL, "yield" numeric NOT NULL, "fees" numeric NOT NULL, "revenue" numeric NOT NULL, "revenue7_day_avg" numeric NOT NULL, "revenue7_day_total" numeric NOT NULL, "revenue_all_time" numeric NOT NULL, "peg_price" numeric NOT NULL, CONSTRAINT "PK_9144a02ab13b1baa818a7d5eae5" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "oeth_daily_stat" ("id" character varying NOT NULL, "block_number" integer NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "apr" numeric NOT NULL, "apy" numeric NOT NULL, "apy7_day_avg" numeric NOT NULL, "apy14_day_avg" numeric NOT NULL, "apy30_day_avg" numeric NOT NULL, "total_supply" numeric NOT NULL, "total_supply_usd" numeric NOT NULL, "rebasing_supply" numeric NOT NULL, "non_rebasing_supply" numeric NOT NULL, "amo_supply" numeric NOT NULL, "dripper_weth" numeric NOT NULL, "wrapped_supply" numeric NOT NULL, "yield_eth" numeric NOT NULL, "yield_eth_all_time" numeric NOT NULL, "yield_usd" numeric NOT NULL, "yield_usd_all_time" numeric NOT NULL, "fees_eth" numeric NOT NULL, "fees_eth7_day" numeric NOT NULL, "fees_eth_all_time" numeric NOT NULL, "fees_usd" numeric NOT NULL, "fees_usd7_day" numeric NOT NULL, "fees_usd_all_time" numeric NOT NULL, "peg_price" numeric NOT NULL, CONSTRAINT "PK_9144a02ab13b1baa818a7d5eae5" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_98d9001013aa37425ca47b7126" ON "oeth_daily_stat" ("block_number") `) await db.query(`CREATE INDEX "IDX_c3e66051c7df4efd6a8fa8f9c1" ON "oeth_daily_stat" ("timestamp") `) await db.query(`CREATE TABLE "oeth_reward_token_collected" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "strategy" text NOT NULL, "recipient" text NOT NULL, "reward_token" text NOT NULL, "amount" numeric NOT NULL, CONSTRAINT "PK_47098cc5fbc7cb95c2374fa33cd" PRIMARY KEY ("id"))`) @@ -116,7 +116,7 @@ module.exports = class Data1701729003798 { await db.query(`CREATE INDEX "IDX_c514963f42908ce84d65a84a77" ON "ousdapy" ("timestamp") `) await db.query(`CREATE INDEX "IDX_4f606414b3b5ce1a366bd0fbf6" ON "ousdapy" ("block_number") `) await db.query(`CREATE INDEX "IDX_0e84a81a109b66fe6f01f77c74" ON "ousdapy" ("tx_hash") `) - await db.query(`CREATE TABLE "ousd_rebase" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "tx_hash" text NOT NULL, "total_supply" numeric NOT NULL, "rebasing_credits" numeric NOT NULL, "rebasing_credits_per_token" numeric NOT NULL, "fee" numeric NOT NULL, "yield" numeric NOT NULL, "apy_id" character varying, CONSTRAINT "PK_04cf0de72399a99798dde61b237" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "ousd_rebase" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "tx_hash" text NOT NULL, "total_supply" numeric NOT NULL, "rebasing_credits" numeric NOT NULL, "rebasing_credits_per_token" numeric NOT NULL, "fee_eth" numeric NOT NULL, "fee_usd" numeric NOT NULL, "yield_eth" numeric NOT NULL, "yield_usd" numeric NOT NULL, "apy_id" character varying, CONSTRAINT "PK_04cf0de72399a99798dde61b237" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_f8eb4a16ce58a146b3227ee21a" ON "ousd_rebase" ("timestamp") `) await db.query(`CREATE INDEX "IDX_3fb03b1a410e64c7367226d0b6" ON "ousd_rebase" ("block_number") `) await db.query(`CREATE INDEX "IDX_1a76c478199672aaeec340f619" ON "ousd_rebase" ("tx_hash") `) @@ -160,6 +160,15 @@ module.exports = class Data1701729003798 { await db.query(`CREATE INDEX "IDX_87dcf07f9e54c132535acf47a4" ON "ousd_activity" ("timestamp") `) await db.query(`CREATE INDEX "IDX_e80a0f99e9f31fe69ac50b5a4c" ON "ousd_activity" ("block_number") `) await db.query(`CREATE INDEX "IDX_2af2ad9975c024d1aa3535928f" ON "ousd_activity" ("tx_hash") `) + await db.query(`CREATE TABLE "ousd_strategy_holding_daily_stat" ("id" character varying NOT NULL, "symbol" text NOT NULL, "amount" numeric NOT NULL, "value" numeric NOT NULL, "strategy_daily_stat_id_id" character varying, CONSTRAINT "PK_ef2f51867e1d465c554262efd1b" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_e1a93bab3d12271dc114234d3a" ON "ousd_strategy_holding_daily_stat" ("strategy_daily_stat_id_id") `) + await db.query(`CREATE TABLE "ousd_strategy_daily_stat" ("id" character varying NOT NULL, "name" text NOT NULL, "total" numeric NOT NULL, "tvl" numeric NOT NULL, "daily_stat_id_id" character varying, CONSTRAINT "PK_ac65d3f28f290211ddeb950e157" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_3edda581683682257159cb0863" ON "ousd_strategy_daily_stat" ("daily_stat_id_id") `) + await db.query(`CREATE TABLE "ousd_collateral_daily_stat" ("id" character varying NOT NULL, "symbol" text NOT NULL, "amount" numeric NOT NULL, "price" numeric NOT NULL, "value" numeric NOT NULL, "daily_stat_id_id" character varying, CONSTRAINT "PK_85a804677f14a0784914e27b7b9" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_78be9d3f3dc1f31cf17ae810fe" ON "ousd_collateral_daily_stat" ("daily_stat_id_id") `) + await db.query(`CREATE TABLE "ousd_daily_stat" ("id" character varying NOT NULL, "block_number" integer NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "apr" numeric NOT NULL, "apy" numeric NOT NULL, "apy7_day_avg" numeric NOT NULL, "apy14_day_avg" numeric NOT NULL, "apy30_day_avg" numeric NOT NULL, "total_supply" numeric NOT NULL, "total_supply_usd" numeric NOT NULL, "rebasing_supply" numeric NOT NULL, "non_rebasing_supply" numeric NOT NULL, "amo_supply" numeric NOT NULL, "dripper_weth" numeric NOT NULL, "wrapped_supply" numeric NOT NULL, "yield_eth" numeric NOT NULL, "yield_eth_all_time" numeric NOT NULL, "yield_usd" numeric NOT NULL, "yield_usd_all_time" numeric NOT NULL, "fees_eth" numeric NOT NULL, "fees_eth7_day" numeric NOT NULL, "fees_eth_all_time" numeric NOT NULL, "fees_usd" numeric NOT NULL, "fees_usd7_day" numeric NOT NULL, "fees_usd_all_time" numeric NOT NULL, "peg_price" numeric NOT NULL, CONSTRAINT "PK_f8adaf321a99f2b4b877c262880" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_f9020d89932aad2d0de8923490" ON "ousd_daily_stat" ("block_number") `) + await db.query(`CREATE INDEX "IDX_0bb5f72bf5fa59ce8c232caa4c" ON "ousd_daily_stat" ("timestamp") `) await db.query(`ALTER TABLE "oeth_history" ADD CONSTRAINT "FK_94e47c4c49128c78f60b185b46b" FOREIGN KEY ("address_id") REFERENCES "oeth_address"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) await db.query(`ALTER TABLE "oeth_rebase" ADD CONSTRAINT "FK_3331819842173de7c27c046547a" FOREIGN KEY ("apy_id") REFERENCES "oethapy"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) await db.query(`ALTER TABLE "oeth_rebase_option" ADD CONSTRAINT "FK_034428879698039839b4ba6ffe8" FOREIGN KEY ("address_id") REFERENCES "oeth_address"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) @@ -176,6 +185,9 @@ module.exports = class Data1701729003798 { await db.query(`ALTER TABLE "ousd_history" ADD CONSTRAINT "FK_70291ea600c0c4d67d9bfe6a6bf" FOREIGN KEY ("address_id") REFERENCES "ousd_address"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) await db.query(`ALTER TABLE "ousd_rebase" ADD CONSTRAINT "FK_427468c97f9838b804efd6c8e55" FOREIGN KEY ("apy_id") REFERENCES "ousdapy"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) await db.query(`ALTER TABLE "ousd_rebase_option" ADD CONSTRAINT "FK_b04173f9349ddd991a3b60e914a" FOREIGN KEY ("address_id") REFERENCES "ousd_address"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "ousd_strategy_holding_daily_stat" ADD CONSTRAINT "FK_e1a93bab3d12271dc114234d3ae" FOREIGN KEY ("strategy_daily_stat_id_id") REFERENCES "ousd_strategy_daily_stat"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "ousd_strategy_daily_stat" ADD CONSTRAINT "FK_3edda581683682257159cb08638" FOREIGN KEY ("daily_stat_id_id") REFERENCES "ousd_daily_stat"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) + await db.query(`ALTER TABLE "ousd_collateral_daily_stat" ADD CONSTRAINT "FK_78be9d3f3dc1f31cf17ae810fee" FOREIGN KEY ("daily_stat_id_id") REFERENCES "ousd_daily_stat"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`) } async down(db) { @@ -337,6 +349,15 @@ module.exports = class Data1701729003798 { await db.query(`DROP INDEX "public"."IDX_87dcf07f9e54c132535acf47a4"`) await db.query(`DROP INDEX "public"."IDX_e80a0f99e9f31fe69ac50b5a4c"`) await db.query(`DROP INDEX "public"."IDX_2af2ad9975c024d1aa3535928f"`) + await db.query(`DROP TABLE "ousd_strategy_holding_daily_stat"`) + await db.query(`DROP INDEX "public"."IDX_e1a93bab3d12271dc114234d3a"`) + await db.query(`DROP TABLE "ousd_strategy_daily_stat"`) + await db.query(`DROP INDEX "public"."IDX_3edda581683682257159cb0863"`) + await db.query(`DROP TABLE "ousd_collateral_daily_stat"`) + await db.query(`DROP INDEX "public"."IDX_78be9d3f3dc1f31cf17ae810fe"`) + await db.query(`DROP TABLE "ousd_daily_stat"`) + await db.query(`DROP INDEX "public"."IDX_f9020d89932aad2d0de8923490"`) + await db.query(`DROP INDEX "public"."IDX_0bb5f72bf5fa59ce8c232caa4c"`) await db.query(`ALTER TABLE "oeth_history" DROP CONSTRAINT "FK_94e47c4c49128c78f60b185b46b"`) await db.query(`ALTER TABLE "oeth_rebase" DROP CONSTRAINT "FK_3331819842173de7c27c046547a"`) await db.query(`ALTER TABLE "oeth_rebase_option" DROP CONSTRAINT "FK_034428879698039839b4ba6ffe8"`) @@ -353,5 +374,8 @@ module.exports = class Data1701729003798 { await db.query(`ALTER TABLE "ousd_history" DROP CONSTRAINT "FK_70291ea600c0c4d67d9bfe6a6bf"`) await db.query(`ALTER TABLE "ousd_rebase" DROP CONSTRAINT "FK_427468c97f9838b804efd6c8e55"`) await db.query(`ALTER TABLE "ousd_rebase_option" DROP CONSTRAINT "FK_b04173f9349ddd991a3b60e914a"`) + await db.query(`ALTER TABLE "ousd_strategy_holding_daily_stat" DROP CONSTRAINT "FK_e1a93bab3d12271dc114234d3ae"`) + await db.query(`ALTER TABLE "ousd_strategy_daily_stat" DROP CONSTRAINT "FK_3edda581683682257159cb08638"`) + await db.query(`ALTER TABLE "ousd_collateral_daily_stat" DROP CONSTRAINT "FK_78be9d3f3dc1f31cf17ae810fee"`) } } diff --git a/schema-oeth.graphql b/schema-oeth.graphql index f413b5c8..ce7eea15 100644 --- a/schema-oeth.graphql +++ b/schema-oeth.graphql @@ -50,8 +50,10 @@ type OETHRebase @entity { rebasingCredits: BigInt! rebasingCreditsPerToken: BigInt! apy: OETHAPY! - fee: BigInt! - yield: BigInt! + feeETH: BigInt! + feeUSD: BigInt! + yieldETH: BigInt! + yieldUSD: BigInt! } """ @@ -178,12 +180,18 @@ type OETHDailyStat @entity { dripperWETH: BigInt! wrappedSupply: BigInt! - yield: BigInt! - fees: BigInt! - revenue: BigInt! - revenue7DayAvg: BigInt! - revenue7DayTotal: BigInt! - revenueAllTime: BigInt! + yieldETH: BigInt! + yieldETHAllTime: BigInt! + yieldUSD: BigInt! + yieldUSDAllTime: BigInt! + + feesETH: BigInt! + feesETH7Day: BigInt! + feesETHAllTime: BigInt! + + feesUSD: BigInt! + feesUSD7Day: BigInt! + feesUSDAllTime: BigInt! pegPrice: BigInt! """ @@ -206,7 +214,7 @@ type OETHStrategyDailyStat @entity { Total ETH value """ holdings: [OETHStrategyHoldingDailyStat] - @derivedFrom(field: "strategyDailyStatId") + @derivedFrom(field: "strategyDailyStatId") } type OETHStrategyHoldingDailyStat @entity { @@ -279,4 +287,3 @@ type OETHActivity @entity { toSymbol: String amount: BigInt } - diff --git a/schema-ousd.graphql b/schema-ousd.graphql index 768a279f..0fc46a36 100644 --- a/schema-ousd.graphql +++ b/schema-ousd.graphql @@ -50,8 +50,10 @@ type OUSDRebase @entity { rebasingCredits: BigInt! rebasingCreditsPerToken: BigInt! apy: OUSDAPY! - fee: BigInt! - yield: BigInt! + feeETH: BigInt! + feeUSD: BigInt! + yieldETH: BigInt! + yieldUSD: BigInt! } """ @@ -192,3 +194,66 @@ type OUSDActivity @entity { toSymbol: String amount: BigInt } + +type OUSDDailyStat @entity { + id: ID! + blockNumber: Int! @index + timestamp: DateTime! @index + apr: Float! + apy: Float! + apy7DayAvg: Float! + apy14DayAvg: Float! + apy30DayAvg: Float! + + totalSupply: BigInt! + totalSupplyUSD: Float! + rebasingSupply: BigInt! + nonRebasingSupply: BigInt! + amoSupply: BigInt! + dripperWETH: BigInt! + wrappedSupply: BigInt! + + yieldETH: BigInt! + yieldETHAllTime: BigInt! + yieldUSD: BigInt! + yieldUSDAllTime: BigInt! + + feesETH: BigInt! + feesETH7Day: BigInt! + feesETHAllTime: BigInt! + + feesUSD: BigInt! + feesUSD7Day: BigInt! + feesUSDAllTime: BigInt! + + pegPrice: BigInt! + strategies: [OUSDStrategyDailyStat] @derivedFrom(field: "dailyStatId") + collateral: [OUSDCollateralDailyStat] @derivedFrom(field: "dailyStatId") +} + +type OUSDStrategyDailyStat @entity { + id: ID! + dailyStatId: OUSDDailyStat! + name: String! + total: BigInt! + tvl: BigInt! + holdings: [OUSDStrategyHoldingDailyStat] + @derivedFrom(field: "strategyDailyStatId") +} + +type OUSDStrategyHoldingDailyStat @entity { + id: ID! + strategyDailyStatId: OUSDStrategyDailyStat! + symbol: String! + amount: BigInt! + value: BigInt! +} + +type OUSDCollateralDailyStat @entity { + id: ID! + dailyStatId: OUSDDailyStat! @index + symbol: String! + amount: BigInt! + price: BigInt! + value: BigInt! +} diff --git a/schema.graphql b/schema.graphql index e07bbb1a..5c1a7087 100644 --- a/schema.graphql +++ b/schema.graphql @@ -201,8 +201,10 @@ type OETHRebase @entity { rebasingCredits: BigInt! rebasingCreditsPerToken: BigInt! apy: OETHAPY! - fee: BigInt! - yield: BigInt! + feeETH: BigInt! + feeUSD: BigInt! + yieldETH: BigInt! + yieldUSD: BigInt! } """ @@ -329,12 +331,18 @@ type OETHDailyStat @entity { dripperWETH: BigInt! wrappedSupply: BigInt! - yield: BigInt! - fees: BigInt! - revenue: BigInt! - revenue7DayAvg: BigInt! - revenue7DayTotal: BigInt! - revenueAllTime: BigInt! + yieldETH: BigInt! + yieldETHAllTime: BigInt! + yieldUSD: BigInt! + yieldUSDAllTime: BigInt! + + feesETH: BigInt! + feesETH7Day: BigInt! + feesETHAllTime: BigInt! + + feesUSD: BigInt! + feesUSD7Day: BigInt! + feesUSDAllTime: BigInt! pegPrice: BigInt! """ @@ -357,7 +365,7 @@ type OETHStrategyDailyStat @entity { Total ETH value """ holdings: [OETHStrategyHoldingDailyStat] - @derivedFrom(field: "strategyDailyStatId") + @derivedFrom(field: "strategyDailyStatId") } type OETHStrategyHoldingDailyStat @entity { @@ -430,7 +438,6 @@ type OETHActivity @entity { toSymbol: String amount: BigInt } - type OGV @entity { id: ID! timestamp: DateTime! @index @@ -582,8 +589,10 @@ type OUSDRebase @entity { rebasingCredits: BigInt! rebasingCreditsPerToken: BigInt! apy: OUSDAPY! - fee: BigInt! - yield: BigInt! + feeETH: BigInt! + feeUSD: BigInt! + yieldETH: BigInt! + yieldUSD: BigInt! } """ @@ -724,3 +733,66 @@ type OUSDActivity @entity { toSymbol: String amount: BigInt } + +type OUSDDailyStat @entity { + id: ID! + blockNumber: Int! @index + timestamp: DateTime! @index + apr: Float! + apy: Float! + apy7DayAvg: Float! + apy14DayAvg: Float! + apy30DayAvg: Float! + + totalSupply: BigInt! + totalSupplyUSD: Float! + rebasingSupply: BigInt! + nonRebasingSupply: BigInt! + amoSupply: BigInt! + dripperWETH: BigInt! + wrappedSupply: BigInt! + + yieldETH: BigInt! + yieldETHAllTime: BigInt! + yieldUSD: BigInt! + yieldUSDAllTime: BigInt! + + feesETH: BigInt! + feesETH7Day: BigInt! + feesETHAllTime: BigInt! + + feesUSD: BigInt! + feesUSD7Day: BigInt! + feesUSDAllTime: BigInt! + + pegPrice: BigInt! + strategies: [OUSDStrategyDailyStat] @derivedFrom(field: "dailyStatId") + collateral: [OUSDCollateralDailyStat] @derivedFrom(field: "dailyStatId") +} + +type OUSDStrategyDailyStat @entity { + id: ID! + dailyStatId: OUSDDailyStat! + name: String! + total: BigInt! + tvl: BigInt! + holdings: [OUSDStrategyHoldingDailyStat] + @derivedFrom(field: "strategyDailyStatId") +} + +type OUSDStrategyHoldingDailyStat @entity { + id: ID! + strategyDailyStatId: OUSDStrategyDailyStat! + symbol: String! + amount: BigInt! + value: BigInt! +} + +type OUSDCollateralDailyStat @entity { + id: ID! + dailyStatId: OUSDDailyStat! @index + symbol: String! + amount: BigInt! + price: BigInt! + value: BigInt! +} diff --git a/src/main-ousd.ts b/src/main-ousd.ts index b384c389..4f2d4506 100644 --- a/src/main-ousd.ts +++ b/src/main-ousd.ts @@ -1,3 +1,4 @@ +import * as dailyStats from './ousd/post-processors/daily-stats' import * as ousd from './ousd/processors/ousd' import * as strategies from './ousd/processors/strategies/strategies' import * as validateOusd from './ousd/validators/validate-ousd' @@ -7,7 +8,7 @@ import * as exchangeRates from './shared/post-processors/exchange-rates' export const processor = { stateSchema: 'ousd-processor', processors: [ousd, strategies], - postProcessors: [exchangeRates], + postProcessors: [exchangeRates, dailyStats], validators: [validateOusd], } export default processor diff --git a/src/model/generated/index.ts b/src/model/generated/index.ts index 737b66fa..326bf6ee 100644 --- a/src/model/generated/index.ts +++ b/src/model/generated/index.ts @@ -57,3 +57,7 @@ export * from "./ousdAaveStrategy.model" export * from "./ousdMetaStrategy.model" export * from "./ousdConvexLusdPlus3Crv.model" export * from "./ousdActivity.model" +export * from "./ousdDailyStat.model" +export * from "./ousdStrategyDailyStat.model" +export * from "./ousdStrategyHoldingDailyStat.model" +export * from "./ousdCollateralDailyStat.model" diff --git a/src/model/generated/oethDailyStat.model.ts b/src/model/generated/oethDailyStat.model.ts index 6cf65561..fb120cde 100644 --- a/src/model/generated/oethDailyStat.model.ts +++ b/src/model/generated/oethDailyStat.model.ts @@ -66,22 +66,34 @@ export class OETHDailyStat { wrappedSupply!: bigint @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - yield!: bigint + yieldETH!: bigint @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - fees!: bigint + yieldETHAllTime!: bigint @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - revenue!: bigint + yieldUSD!: bigint @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - revenue7DayAvg!: bigint + yieldUSDAllTime!: bigint @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - revenue7DayTotal!: bigint + feesETH!: bigint @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - revenueAllTime!: bigint + feesETH7Day!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + feesETHAllTime!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + feesUSD!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + feesUSD7Day!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + feesUSDAllTime!: bigint @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) pegPrice!: bigint diff --git a/src/model/generated/oethRebase.model.ts b/src/model/generated/oethRebase.model.ts index 126e8ad5..68aa87b5 100644 --- a/src/model/generated/oethRebase.model.ts +++ b/src/model/generated/oethRebase.model.ts @@ -40,8 +40,14 @@ export class OETHRebase { apy!: OETHAPY @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - fee!: bigint + feeETH!: bigint @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - yield!: bigint + feeUSD!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + yieldETH!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + yieldUSD!: bigint } diff --git a/src/model/generated/ousdCollateralDailyStat.model.ts b/src/model/generated/ousdCollateralDailyStat.model.ts new file mode 100644 index 00000000..30b3dc41 --- /dev/null +++ b/src/model/generated/ousdCollateralDailyStat.model.ts @@ -0,0 +1,29 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "typeorm" +import * as marshal from "./marshal" +import {OUSDDailyStat} from "./ousdDailyStat.model" + +@Entity_() +export class OUSDCollateralDailyStat { + constructor(props?: Partial) { + Object.assign(this, props) + } + + @PrimaryColumn_() + id!: string + + @Index_() + @ManyToOne_(() => OUSDDailyStat, {nullable: true}) + dailyStatId!: OUSDDailyStat + + @Column_("text", {nullable: false}) + symbol!: string + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + amount!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + price!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + value!: bigint +} diff --git a/src/model/generated/ousdDailyStat.model.ts b/src/model/generated/ousdDailyStat.model.ts new file mode 100644 index 00000000..649f3160 --- /dev/null +++ b/src/model/generated/ousdDailyStat.model.ts @@ -0,0 +1,97 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_, OneToMany as OneToMany_} from "typeorm" +import * as marshal from "./marshal" +import {OUSDStrategyDailyStat} from "./ousdStrategyDailyStat.model" +import {OUSDCollateralDailyStat} from "./ousdCollateralDailyStat.model" + +@Entity_() +export class OUSDDailyStat { + constructor(props?: Partial) { + Object.assign(this, props) + } + + @PrimaryColumn_() + id!: string + + @Index_() + @Column_("int4", {nullable: false}) + blockNumber!: number + + @Index_() + @Column_("timestamp with time zone", {nullable: false}) + timestamp!: Date + + @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) + apr!: number + + @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) + apy!: number + + @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) + apy7DayAvg!: number + + @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) + apy14DayAvg!: number + + @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) + apy30DayAvg!: number + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + totalSupply!: bigint + + @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) + totalSupplyUSD!: number + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + rebasingSupply!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + nonRebasingSupply!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + amoSupply!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + dripperWETH!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + wrappedSupply!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + yieldETH!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + yieldETHAllTime!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + yieldUSD!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + yieldUSDAllTime!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + feesETH!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + feesETH7Day!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + feesETHAllTime!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + feesUSD!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + feesUSD7Day!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + feesUSDAllTime!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + pegPrice!: bigint + + @OneToMany_(() => OUSDStrategyDailyStat, e => e.dailyStatId) + strategies!: OUSDStrategyDailyStat[] + + @OneToMany_(() => OUSDCollateralDailyStat, e => e.dailyStatId) + collateral!: OUSDCollateralDailyStat[] +} diff --git a/src/model/generated/ousdRebase.model.ts b/src/model/generated/ousdRebase.model.ts index 524deea1..de18b747 100644 --- a/src/model/generated/ousdRebase.model.ts +++ b/src/model/generated/ousdRebase.model.ts @@ -40,8 +40,14 @@ export class OUSDRebase { apy!: OUSDAPY @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - fee!: bigint + feeETH!: bigint @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) - yield!: bigint + feeUSD!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + yieldETH!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + yieldUSD!: bigint } diff --git a/src/model/generated/ousdStrategyDailyStat.model.ts b/src/model/generated/ousdStrategyDailyStat.model.ts new file mode 100644 index 00000000..03fbb355 --- /dev/null +++ b/src/model/generated/ousdStrategyDailyStat.model.ts @@ -0,0 +1,30 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_, OneToMany as OneToMany_} from "typeorm" +import * as marshal from "./marshal" +import {OUSDDailyStat} from "./ousdDailyStat.model" +import {OUSDStrategyHoldingDailyStat} from "./ousdStrategyHoldingDailyStat.model" + +@Entity_() +export class OUSDStrategyDailyStat { + constructor(props?: Partial) { + Object.assign(this, props) + } + + @PrimaryColumn_() + id!: string + + @Index_() + @ManyToOne_(() => OUSDDailyStat, {nullable: true}) + dailyStatId!: OUSDDailyStat + + @Column_("text", {nullable: false}) + name!: string + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + total!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + tvl!: bigint + + @OneToMany_(() => OUSDStrategyHoldingDailyStat, e => e.strategyDailyStatId) + holdings!: OUSDStrategyHoldingDailyStat[] +} diff --git a/src/model/generated/ousdStrategyHoldingDailyStat.model.ts b/src/model/generated/ousdStrategyHoldingDailyStat.model.ts new file mode 100644 index 00000000..aff13b8f --- /dev/null +++ b/src/model/generated/ousdStrategyHoldingDailyStat.model.ts @@ -0,0 +1,26 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "typeorm" +import * as marshal from "./marshal" +import {OUSDStrategyDailyStat} from "./ousdStrategyDailyStat.model" + +@Entity_() +export class OUSDStrategyHoldingDailyStat { + constructor(props?: Partial) { + Object.assign(this, props) + } + + @PrimaryColumn_() + id!: string + + @Index_() + @ManyToOne_(() => OUSDStrategyDailyStat, {nullable: true}) + strategyDailyStatId!: OUSDStrategyDailyStat + + @Column_("text", {nullable: false}) + symbol!: string + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + amount!: bigint + + @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) + value!: bigint +} diff --git a/src/oeth/post-processors/daily-stats/daily-stats.ts b/src/oeth/post-processors/daily-stats/daily-stats.ts index 0040a966..ca2dd311 100644 --- a/src/oeth/post-processors/daily-stats/daily-stats.ts +++ b/src/oeth/post-processors/daily-stats/daily-stats.ts @@ -128,7 +128,7 @@ async function updateDailyStats(ctx: Context, date: Date) { lastDripper, lastRethRate, lastSfrxEthRate, - ] + ].filter(Boolean) if (![lastApy, lastOeth].every((entity) => !!entity)) { return null } @@ -143,11 +143,12 @@ async function updateDailyStats(ctx: Context, date: Date) { const yieldStats = await entityManager.query< { period: string - total_yield: bigint - total_fees: bigint - total_revenue: bigint + total_yield_usd: bigint + total_yield_eth: bigint + total_fees_usd: bigint + total_fees_eth: bigint }[] - >(yieldStatsQuery, [end]) + >(yieldStatsQuery, [end.toJSON()]) const mostRecentEntity = allEntities.reduce((highest, current) => { if (!highest || !current) return current @@ -175,12 +176,16 @@ async function updateDailyStats(ctx: Context, date: Date) { dripperWETH: lastDripper?.weth || 0n, wrappedSupply: lastWrappedOETHHistory?.balance || 0n, - yield: yieldStats[0].total_yield || 0n, - fees: yieldStats[0].total_fees || 0n, - revenue: yieldStats[0].total_revenue || 0n, - revenue7DayAvg: BigInt(yieldStats[1].total_revenue || 0n) / 7n, - revenue7DayTotal: yieldStats[1].total_revenue || 0n, - revenueAllTime: yieldStats[2].total_revenue || 0n, + yieldUSD: yieldStats[0].total_yield_usd || 0n, + yieldUSDAllTime: yieldStats[2].total_yield_usd || 0n, + yieldETH: yieldStats[0].total_yield_eth || 0n, + yieldETHAllTime: yieldStats[2].total_yield_eth || 0n, + feesUSD: yieldStats[0].total_fees_usd || 0n, + feesUSD7Day: yieldStats[1].total_fees_usd || 0n, + feesUSDAllTime: yieldStats[2].total_fees_usd || 0n, + feesETH: yieldStats[0].total_fees_eth || 0n, + feesETH7Day: yieldStats[1].total_fees_eth || 0n, + feesETHAllTime: yieldStats[2].total_fees_eth || 0n, pegPrice: 0n, }) @@ -376,21 +381,36 @@ async function updateDailyStats(ctx: Context, date: Date) { const yieldStatsQuery = ` -- Results for 1 day -SELECT '1 day' as period, SUM(fee) as total_fees, SUM(yield - fee) as total_yield, SUM(yield) as total_revenue +SELECT '1 day' as period, + SUM(fee_usd) as total_fees_usd, + SUM(yield_usd - fee_usd) as total_yield_usd, + SUM(fee_eth) as total_fees_eth, + SUM(yield_eth - fee_eth) as total_yield_eth + FROM oeth_rebase WHERE timestamp BETWEEN ($1::timestamp - interval '1 day') AND $1::timestamp UNION ALL -- Results for 7 days -SELECT '7 days' as period, SUM(fee) as total_fees, SUM(yield - fee) as total_yield, SUM(yield) as total_revenue +SELECT '7 days' as period, + SUM(fee_usd) as total_fees_usd, + SUM(yield_usd - fee_usd) as total_yield_usd, + SUM(fee_eth) as total_fees_eth, + SUM(yield_eth - fee_eth) as total_yield_eth + FROM oeth_rebase WHERE timestamp BETWEEN ($1::timestamp - interval '7 days') AND $1::timestamp UNION ALL -- Results for all time up to the end date -SELECT 'all time' as period, SUM(fee) as total_fees, SUM(yield - fee) as total_yield, SUM(yield) as total_revenue +SELECT 'all time' as period, + SUM(fee_usd) as total_fees_usd, + SUM(yield_usd - fee_usd) as total_yield_usd, + SUM(fee_eth) as total_fees_eth, + SUM(yield_eth - fee_eth) as total_yield_eth + FROM oeth_rebase WHERE timestamp <= $1::timestamp ` diff --git a/src/oeth/validators/validate-oeth/validate-oeth.ts b/src/oeth/validators/validate-oeth/validate-oeth.ts index 40576911..708af262 100644 --- a/src/oeth/validators/validate-oeth/validate-oeth.ts +++ b/src/oeth/validators/validate-oeth/validate-oeth.ts @@ -387,61 +387,71 @@ const expectations = { id: '0017305345-000456-5f62d', blockNumber: 17305345, timestamp: '2023-05-21T04:22:11.000000Z', - fee: '6000055597516131', + feeETH: '6000055597516131', + feeUSD: '10916381153008898418', rebasingCredits: '2787113542887662778878885791818', rebasingCreditsPerToken: '973864011520629021168619646', totalSupply: '4100236147122104146565', txHash: '0x7de27a14c9f139d9019e6f067dc926de5a1318d1c8703696d1f87c7e11b405eb', - yield: '30000277987580656', + yieldETH: '30000277987580656', + yieldUSD: '54581905765044493913', }, { id: '0017331591-000369-f9f77', blockNumber: 17331591, timestamp: '2023-05-24T21:02:59.000000Z', - fee: '6492737577781034', + feeETH: '6492737577781034', + feeUSD: '11727132294271875807', rebasingCredits: '3677621799805326022027580379682', rebasingCreditsPerToken: '972810711211478539104302424', totalSupply: '5393628338867046745384', txHash: '0x651848930f76eb8677a2d90067841ae1d5419276dafb7828d145eeb482b362cc', - yield: '32463687888905171', + yieldETH: '32463687888905171', + yieldUSD: '58635661471359380845', }, { id: '0017583331-000121-dccba', blockNumber: 17583331, timestamp: '2023-06-29T07:00:23.000000Z', - fee: '577769460917579313', + feeETH: '577769460917579313', + feeUSD: '1062477594765164126055', rebasingCredits: '11978493735413759353827056755312', rebasingCreditsPerToken: '963907687618519656276956464', totalSupply: '21187279626610714331049', txHash: '0xe9261579ff7ccbbbe0cc5895a376c8177409a8a0503acf3bf04ee106ccb9c540', - yield: '2888847304587896566', + yieldETH: '2888847304587896566', + yieldUSD: '5312387973825820632114', }, { id: '0018139997-000303-52abd', blockNumber: 18139997, timestamp: '2023-09-15T06:59:59.000000Z', - fee: '1190995054063049444', + feeETH: '1190995054063049444', + feeUSD: '1941953165501424009925', rebasingCredits: '22395831398283302999639860139823', rebasingCreditsPerToken: '948155891702979766036181091', totalSupply: '44971365122525849270172', txHash: '0xb3441c6184326378fe98af3cb3e425c015a5a2cce4d0055f2e8c3b0dc1bcdd98', - yield: '5954975270315247221', + yieldETH: '5954975270315247221', + yieldUSD: '9709765827507120051257', }, { id: '0018404209-000053-b6b49', blockNumber: 18404209, timestamp: '2023-10-22T06:59:47.000000Z', - fee: '885619331985372380', + feeETH: '885619331985372380', + feeUSD: '1455772085654965614517', rebasingCredits: '29496888949221038689228739185366', rebasingCreditsPerToken: '941672759591769536254510513', totalSupply: '41530447614871349372266', txHash: '0x1eab96d75579c8050869ffadec5d38bccf8a9714f49d6e27ce67088f15be0535', - yield: '4428096659926861900', + yieldETH: '4428096659926861900', + yieldUSD: '7278860428274828072586', }, ]), oethVaults: e([ diff --git a/src/ogv/post-processors/governance.ts b/src/ogv/post-processors/governance.ts index a23650b5..e6b906a8 100644 --- a/src/ogv/post-processors/governance.ts +++ b/src/ogv/post-processors/governance.ts @@ -12,6 +12,7 @@ import { } from '../../model' import { Block, Context, Log } from '../../processor' import { GOVERNANCE_ADDRESS } from '../../utils/addresses' +import { env } from '../../utils/env' export const from = 15491391 // https://etherscan.io/tx/0x0e04e429248c384e6b36229edf8eb5a77bec7023c58808c21b702edfcbc0e0d6 @@ -53,25 +54,32 @@ export const process = async (ctx: Context) => { if (log.address !== GOVERNANCE_ADDRESS) continue const firstTopic = log.topics[0] - if (firstTopic == governanceAbi.events.ProposalCreated.topic) { - await _processProposalCreated(ctx, result, block, log) - } else if (firstTopic == governanceAbi.events.ProposalExtended.topic) { - await _processProposalExtended(ctx, result, block, log) - } else if ( - [ - governanceAbi.events.ProposalQueued.topic, - governanceAbi.events.ProposalCanceled.topic, - governanceAbi.events.ProposalExecuted.topic, - ].includes(firstTopic) - ) { - await _processProposalEvents(ctx, result, block, log) - } else if ( - [ - governanceAbi.events.VoteCast.topic, - governanceAbi.events.VoteCastWithParams.topic, - ].includes(firstTopic) - ) { - await _processVoteCast(ctx, result, block, log) + try { + if (firstTopic == governanceAbi.events.ProposalCreated.topic) { + await _processProposalCreated(ctx, result, block, log) + } else if (firstTopic == governanceAbi.events.ProposalExtended.topic) { + await _processProposalExtended(ctx, result, block, log) + } else if ( + [ + governanceAbi.events.ProposalQueued.topic, + governanceAbi.events.ProposalCanceled.topic, + governanceAbi.events.ProposalExecuted.topic, + ].includes(firstTopic) + ) { + await _processProposalEvents(ctx, result, block, log) + } else if ( + [ + governanceAbi.events.VoteCast.topic, + governanceAbi.events.VoteCastWithParams.topic, + ].includes(firstTopic) + ) { + await _processVoteCast(ctx, result, block, log) + } + } catch (e) { + if (!env.BLOCK_FROM) { + throw e + } + ctx.log.error('Could not process governance event') } } } diff --git a/src/ousd/post-processors/daily-stats/daily-stats.ts b/src/ousd/post-processors/daily-stats/daily-stats.ts new file mode 100644 index 00000000..ffe9a066 --- /dev/null +++ b/src/ousd/post-processors/daily-stats/daily-stats.ts @@ -0,0 +1,231 @@ +import { EvmBatchProcessor } from '@subsquid/evm-processor' +import dayjs from 'dayjs' +import utc from 'dayjs/plugin/utc' +import { EntityManager, FindOptionsOrderValue, LessThanOrEqual } from 'typeorm' + +import { + ExchangeRate, + OUSD, + OUSDAPY, + OUSDCollateralDailyStat, + OUSDDailyStat, + OUSDHistory, + OUSDMorphoAave, + OUSDStrategyDailyStat, + OUSDStrategyHoldingDailyStat, + OUSDVault, +} from '../../../model' +import { Context } from '../../../processor' + +dayjs.extend(utc) + +export const from = 11585978 // OUSDReset + +export const setup = async (processor: EvmBatchProcessor) => { + processor.includeAllBlocks({ from }) +} + +export const process = async (ctx: Context) => { + const firstBlockTimestamp = ctx.blocks.find((b) => b.header.height >= from) + ?.header.timestamp + if (!firstBlockTimestamp) return + + const firstBlock = ctx.blocks[0] + const lastBlock = ctx.blocks[ctx.blocks.length - 1] + const startDate = dayjs.utc(firstBlock.header.timestamp).endOf('day') + const endDate = dayjs.utc(lastBlock.header.timestamp).endOf('day') + + let dates: Date[] = [] + for ( + let date = startDate; + !date.isAfter(endDate); + date = date.add(1, 'day').endOf('day') + ) { + dates.push(date.toDate()) + } + + const dailyStats = [] as OUSDDailyStat[] + const dailyCollateralStats = [] as OUSDCollateralDailyStat[] + const dailyStrategyStats = [] as OUSDStrategyDailyStat[] + const dailyHoldingsStats = [] as OUSDStrategyHoldingDailyStat[] + + for (const date of dates) { + const dailyStatInserts = await updateDailyStats(ctx, date) + if (dailyStatInserts) { + dailyStats.push(dailyStatInserts.dailyStat) + dailyCollateralStats.push(...dailyStatInserts.dailyCollateralStats) + dailyStrategyStats.push(...dailyStatInserts.dailyStrategyStats) + dailyHoldingsStats.push(...dailyStatInserts.dailyStrategyHoldingsStats) + } + } + + await ctx.store.upsert(dailyStats) + await Promise.all([ + ctx.store.upsert(dailyCollateralStats), + ctx.store.upsert(dailyStrategyStats), + ctx.store.upsert(dailyHoldingsStats), + ]) +} + +async function updateDailyStats(ctx: Context, date: Date) { + const queryParams = { + where: { timestamp: LessThanOrEqual(date) }, + order: { timestamp: 'desc' as FindOptionsOrderValue }, + } + + const [lastApy, lastOusd, lastVault, lastMorpho, lastWrappedOUSDHistory] = + await Promise.all([ + ctx.store.findOne(OUSDAPY, queryParams), + ctx.store.findOne(OUSD, queryParams), + ctx.store.findOne(OUSDVault, queryParams), + ctx.store.findOne(OUSDMorphoAave, queryParams), + ctx.store.findOne(OUSDHistory, { + where: { + timestamp: LessThanOrEqual(date), + address: { id: '0xd2af830e8cbdfed6cc11bab697bb25496ed6fa62' }, + }, + order: { timestamp: 'desc' as FindOptionsOrderValue }, + }), + ]) + + // Do we have any useful data yet? + const allEntities = [lastApy, lastOusd, lastVault, lastMorpho].filter(Boolean) + if (![lastApy, lastOusd].every((entity) => !!entity)) { + return null + } + + const entityManager = ( + ctx.store as unknown as { + em: () => EntityManager + } + ).em() + + const end = dayjs.utc(date).endOf('day').toDate() + const yieldStats = await entityManager.query< + { + period: string + total_yield_usd: bigint + total_yield_eth: bigint + total_fees_usd: bigint + total_fees_eth: bigint + }[] + >(yieldStatsQuery, [end.toJSON()]) + + const mostRecentEntity = allEntities.reduce((highest, current) => { + if (!highest || !current) return current + return current.blockNumber > highest.blockNumber ? current : highest + }) + + if (!mostRecentEntity?.blockNumber) { + return null + } + + const id = date.toISOString().substring(0, 10) + + const dailyStat = new OUSDDailyStat({ + id, + blockNumber: mostRecentEntity?.blockNumber, + timestamp: mostRecentEntity?.timestamp, + + apr: lastApy?.apr, + apy: lastApy?.apy, + apy7DayAvg: lastApy?.apy7DayAvg, + apy14DayAvg: lastApy?.apy14DayAvg, + apy30DayAvg: lastApy?.apy30DayAvg, + + totalSupply: lastOusd?.totalSupply || 0n, + totalSupplyUSD: 0, + rebasingSupply: lastOusd?.rebasingSupply || 0n, + nonRebasingSupply: lastOusd?.nonRebasingSupply || 0n, + amoSupply: 0n, + dripperWETH: 0n, + wrappedSupply: lastWrappedOUSDHistory?.balance || 0n, + + yieldUSD: yieldStats[0].total_yield_usd || 0n, + yieldUSDAllTime: yieldStats[2].total_yield_usd || 0n, + yieldETH: yieldStats[0].total_yield_eth || 0n, + yieldETHAllTime: yieldStats[2].total_yield_eth || 0n, + feesUSD: yieldStats[0].total_fees_usd || 0n, + feesUSD7Day: yieldStats[1].total_fees_usd || 0n, + feesUSDAllTime: yieldStats[2].total_fees_usd || 0n, + feesETH: yieldStats[0].total_fees_eth || 0n, + feesETH7Day: yieldStats[1].total_fees_eth || 0n, + feesETHAllTime: yieldStats[2].total_fees_eth || 0n, + + pegPrice: 0n, + }) + + const dailyStrategyStats: OUSDStrategyDailyStat[] = [ + // new OUSDStrategyDailyStat({ + // id: `${id}-CURVE`, + // name: 'CURVE', + // dailyStatId: id as unknown as OUSDDailyStat, + // tvl: lastCurve?.totalSupplyOwned || 0n, + // total: lastCurve?.totalSupplyOwned || 0n, + // }), + ] + + const dailyStrategyHoldingsStats: OUSDStrategyHoldingDailyStat[] = [ + // new OUSDStrategyHoldingDailyStat({ + // id: `${id}-CURVE-ETH`, + // strategyDailyStatId: `${id}-CURVE` as unknown as OUSDStrategyDailyStat, + // symbol: 'ETH', + // amount: lastCurve?.ethOwned || 0n, + // value: lastCurve?.ethOwned || 0n, + // }), + ] + + const dailyCollateralStats: OUSDCollateralDailyStat[] = [ + // new OUSDCollateralDailyStat({ + // id: `${id}-ETH`, + // dailyStatId: id as unknown as OUSDDailyStat, + // symbol: 'ETH', + // amount: ETH, + // price: 1n, + // value: ETH, + // }), + ] + + return { + dailyStat, + dailyCollateralStats, + dailyStrategyStats, + dailyStrategyHoldingsStats, + } +} + +const yieldStatsQuery = ` +-- Results for 1 day +SELECT '1 day' as period, + SUM(fee_usd) as total_fees_usd, + SUM(yield_usd - fee_usd) as total_yield_usd, + SUM(fee_eth) as total_fees_eth, + SUM(yield_eth - fee_eth) as total_yield_eth + +FROM ousd_rebase +WHERE timestamp BETWEEN ($1::timestamp - interval '1 day') AND $1::timestamp + +UNION ALL + +-- Results for 7 days +SELECT '7 days' as period, + SUM(fee_usd) as total_fees_usd, + SUM(yield_usd - fee_usd) as total_yield_usd, + SUM(fee_eth) as total_fees_eth, + SUM(yield_eth - fee_eth) as total_yield_eth + +FROM ousd_rebase +WHERE timestamp BETWEEN ($1::timestamp - interval '7 days') AND $1::timestamp + +UNION ALL + +-- Results for all time up to the end date +SELECT 'all time' as period, + SUM(fee_usd) as total_fees_usd, + SUM(yield_usd - fee_usd) as total_yield_usd, + SUM(fee_eth) as total_fees_eth, + SUM(yield_eth - fee_eth) as total_yield_eth + +FROM ousd_rebase +WHERE timestamp <= $1::timestamp +` diff --git a/src/ousd/post-processors/daily-stats/index.ts b/src/ousd/post-processors/daily-stats/index.ts new file mode 100644 index 00000000..966fdd9a --- /dev/null +++ b/src/ousd/post-processors/daily-stats/index.ts @@ -0,0 +1 @@ +export * from './daily-stats' diff --git a/src/shared/processor-templates/otoken/otoken.ts b/src/shared/processor-templates/otoken/otoken.ts index d4c91169..525b63db 100644 --- a/src/shared/processor-templates/otoken/otoken.ts +++ b/src/shared/processor-templates/otoken/otoken.ts @@ -28,6 +28,7 @@ import { ADDRESS_ZERO } from '../../../utils/addresses' import { DECIMALS_18 } from '../../../utils/constants' import { EntityClassT, InstanceTypeOfConstructor } from '../../../utils/type' import { getLatestEntity } from '../../../utils/utils' +import { ensureExchangeRate } from '../../post-processors/exchange-rates' import { createAddress, createRebaseAPY } from './utils' type OToken = EntityClassT | EntityClassT @@ -364,6 +365,11 @@ export const createOTokenProcessor = (params: { throw new Error('lastYieldDistributionEvent is not set') } + const exchangeRate = await ensureExchangeRate(ctx, block, 'ETH', 'USD') + if (!exchangeRate) { + throw new Error('Could not fetch ETH/USD exchange rate') + } + // Rebase events let rebase = createRebaseAPY( params.OTokenAPY, @@ -374,6 +380,7 @@ export const createOTokenProcessor = (params: { log, data, result.lastYieldDistributionEvent, + exchangeRate, ) for (const address of owners!.values()) { diff --git a/src/shared/processor-templates/otoken/utils.ts b/src/shared/processor-templates/otoken/utils.ts index bb33af5e..6c64de46 100644 --- a/src/shared/processor-templates/otoken/utils.ts +++ b/src/shared/processor-templates/otoken/utils.ts @@ -4,6 +4,7 @@ import { LessThan, MoreThanOrEqual } from 'typeorm' import * as otoken from '../../../abi/otoken' import { + ExchangeRate, OETHAPY, OETHAddress, OETHRebase, @@ -61,7 +62,26 @@ export async function createRebaseAPY< fee: bigint yield: bigint }, + exchangeRate: ExchangeRate, ) { + let feeETH = 0n + let yieldETH = 0n + let feeUSD = 0n + let yieldUSD = 0n + const rate = exchangeRate.rate + + if (OTokenAPY.name === 'OUSDAPY') { + feeUSD = lastYieldDistributionEvent.fee + yieldUSD = lastYieldDistributionEvent.yield + feeETH = (feeUSD * 1000000000000000000n) / rate / 10000000000n + yieldETH = (yieldUSD * 1000000000000000000n) / rate / 10000000000n + } else { + feeETH = lastYieldDistributionEvent.fee + yieldETH = lastYieldDistributionEvent.yield + feeUSD = (feeETH * exchangeRate.rate) / 100000000n + yieldUSD = (yieldETH * exchangeRate.rate) / 100000000n + } + const rebase = new OTokenRebase({ id: log.id, blockNumber: block.header.height, @@ -70,8 +90,10 @@ export async function createRebaseAPY< rebasingCredits: rebaseEvent.rebasingCredits, rebasingCreditsPerToken: rebaseEvent.rebasingCreditsPerToken, totalSupply: rebaseEvent.totalSupply, - fee: lastYieldDistributionEvent.fee, - yield: lastYieldDistributionEvent.yield, + feeUSD, + yieldUSD, + feeETH, + yieldETH, }) // use date as id for APY