diff --git a/abi/legacy-staking.json b/abi/legacy-staking.json new file mode 100644 index 00000000..650e8e51 --- /dev/null +++ b/abi/legacy-staking.json @@ -0,0 +1,254 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "GovernorshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "stakeType", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "rootHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "proofDepth", + "type": "uint256" + } + ], + "name": "NewAirDropRootHash", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "durations", + "type": "uint256[]" + } + ], + "name": "NewDurations", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "rates", + "type": "uint256[]" + } + ], + "name": "NewRates", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "yes", + "type": "bool" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "PendingGovernorshipTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "duration", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "rate", + "type": "uint256" + } + ], + "name": "Staked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "fromUser", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "toUser", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "numStakes", + "type": "uint256" + } + ], + "name": "StakesTransfered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "stakedAmount", + "type": "uint256" + } + ], + "name": "Withdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Staked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Withdrawn", + "type": "event" + } +] diff --git a/db/migrations/1719163778896-Data.js b/db/migrations/1719870615420-Data.js similarity index 99% rename from db/migrations/1719163778896-Data.js rename to db/migrations/1719870615420-Data.js index 9e8b7d8e..a337c366 100644 --- a/db/migrations/1719163778896-Data.js +++ b/db/migrations/1719870615420-Data.js @@ -1,5 +1,5 @@ -module.exports = class Data1719163778896 { - name = 'Data1719163778896' +module.exports = class Data1719870615420 { + name = 'Data1719870615420' async up(db) { await db.query(`CREATE TABLE "es_token" ("id" character varying NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "circulating" numeric NOT NULL, "staked" numeric NOT NULL, "total" numeric NOT NULL, CONSTRAINT "PK_69bef9eb94d9a5d42d726d1e661" PRIMARY KEY ("id"))`) @@ -178,6 +178,7 @@ module.exports = class Data1719163778896 { await db.query(`CREATE TABLE "governance_proposal_vote" ("id" character varying NOT NULL, "chain_id" integer NOT NULL, "address" text NOT NULL, "voter" text NOT NULL, "weight" numeric NOT NULL, "type" character varying(7) NOT NULL, "tx_hash" text NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "proposal_id" character varying, CONSTRAINT "PK_e13a15bfb5ccfdec4c19975de87" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_5531af241c24a09c854ead9d55" ON "governance_proposal_vote" ("proposal_id") `) await db.query(`CREATE INDEX "IDX_adc6eb148ad48697e74d23c722" ON "governance_proposal_vote" ("voter") `) + await db.query(`CREATE TABLE "legacy_staker" ("id" character varying NOT NULL, "input_amount" numeric NOT NULL, "output_amount" numeric NOT NULL, "balance" numeric NOT NULL, "reward_amount" numeric NOT NULL, CONSTRAINT "PK_11a12e3c39ce22c9a05990c9a56" PRIMARY KEY ("id"))`) await db.query(`CREATE TABLE "beacon_deposit_pubkey" ("id" character varying NOT NULL, "create_date" TIMESTAMP WITH TIME ZONE NOT NULL, "last_updated" TIMESTAMP WITH TIME ZONE NOT NULL, "count" integer NOT NULL, CONSTRAINT "PK_4741993dcf81760e9777dec18a7" PRIMARY KEY ("id"))`) await db.query(`CREATE TABLE "beacon_deposit_event" ("id" character varying NOT NULL, "chain_id" integer NOT NULL, "address" text NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "block_number" integer NOT NULL, "tx_hash" text NOT NULL, "caller" text NOT NULL, "withdrawal_credentials" text NOT NULL, "amount" text NOT NULL, "signature" text NOT NULL, "index" text NOT NULL, "pubkey_id" character varying, CONSTRAINT "PK_1ebc083560d266f487bd7098f43" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_4b157d82fc2a709b1cdd1b056a" ON "beacon_deposit_event" ("chain_id") `) @@ -528,6 +529,7 @@ module.exports = class Data1719163778896 { await db.query(`DROP TABLE "governance_proposal_vote"`) await db.query(`DROP INDEX "public"."IDX_5531af241c24a09c854ead9d55"`) await db.query(`DROP INDEX "public"."IDX_adc6eb148ad48697e74d23c722"`) + await db.query(`DROP TABLE "legacy_staker"`) await db.query(`DROP TABLE "beacon_deposit_pubkey"`) await db.query(`DROP TABLE "beacon_deposit_event"`) await db.query(`DROP INDEX "public"."IDX_4b157d82fc2a709b1cdd1b056a"`) diff --git a/schema.graphql b/schema.graphql index 0d342643..447b3771 100644 --- a/schema.graphql +++ b/schema.graphql @@ -535,7 +535,17 @@ type GovernanceProposalVote @entity { type: GovernanceVoteType! txHash: String! timestamp: DateTime! -}type BeaconDepositEvent @entity { +} + +type LegacyStaker @entity { + id: ID! @index + inputAmount: BigInt! + outputAmount: BigInt! + balance: BigInt! + rewardAmount: BigInt! +} + +type BeaconDepositEvent @entity { id: ID! # `chainId:logId` chainId: Int! @index address: String! @index diff --git a/schema/legacy-staking.graphql b/schema/legacy-staking.graphql new file mode 100644 index 00000000..625e382d --- /dev/null +++ b/schema/legacy-staking.graphql @@ -0,0 +1,10 @@ + + +type LegacyStaker @entity { + id: ID! @index + inputAmount: BigInt! + outputAmount: BigInt! + balance: BigInt! + rewardAmount: BigInt! +} + diff --git a/src/abi/legacy-staking.ts b/src/abi/legacy-staking.ts new file mode 100644 index 00000000..71557785 --- /dev/null +++ b/src/abi/legacy-staking.ts @@ -0,0 +1,35 @@ +import * as p from '@subsquid/evm-codec' +import { event, fun, viewFun, indexed, ContractBase } from '@subsquid/evm-abi' +import type { EventParams as EParams, FunctionArguments, FunctionReturn } from '@subsquid/evm-abi' + +export const events = { + GovernorshipTransferred: event("0xc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a", "GovernorshipTransferred(address,address)", {"previousGovernor": indexed(p.address), "newGovernor": indexed(p.address)}), + NewAirDropRootHash: event("0x1ac9c006454d2d601a481473a37c95bf489c5923bd7c2a701757d4016a0f022d", "NewAirDropRootHash(uint8,bytes32,uint256)", {"stakeType": p.uint8, "rootHash": p.bytes32, "proofDepth": p.uint256}), + NewDurations: event("0x180120279c2eb356244609197b5b64c0fbabd60f8d073b75aba771a296bb63d4", "NewDurations(address,uint256[])", {"user": indexed(p.address), "durations": p.array(p.uint256)}), + NewRates: event("0xa804368c7f1a6216d92d17d9753b923dfc3da14ae33d231e8d79e39202e249c3", "NewRates(address,uint256[])", {"user": indexed(p.address), "rates": p.array(p.uint256)}), + Paused: event("0xe8699cf681560fd07de85543bd994263f4557bdc5179dd702f256d15fd083e1d", "Paused(address,bool)", {"user": indexed(p.address), "yes": p.bool}), + PendingGovernorshipTransfer: event("0xa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d", "PendingGovernorshipTransfer(address,address)", {"previousGovernor": indexed(p.address), "newGovernor": indexed(p.address)}), + "Staked(address,uint256,uint256,uint256)": event("0xb4caaf29adda3eefee3ad552a8e85058589bf834c7466cae4ee58787f70589ed", "Staked(address,uint256,uint256,uint256)", {"user": indexed(p.address), "amount": p.uint256, "duration": p.uint256, "rate": p.uint256}), + StakesTransfered: event("0xd0ceb9c39a11711e51ee4b32b97b05d660d6229ecd8be94ce934fa9e77910263", "StakesTransfered(address,address,uint256)", {"fromUser": indexed(p.address), "toUser": p.address, "numStakes": p.uint256}), + "Withdrawn(address,uint256,uint256)": event("0x92ccf450a286a957af52509bc1c9939d1a6a481783e142e41e2499f0bb66ebc6", "Withdrawn(address,uint256,uint256)", {"user": indexed(p.address), "amount": p.uint256, "stakedAmount": p.uint256}), + Upgraded: event("0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", "Upgraded(address)", {"implementation": indexed(p.address)}), + "Staked(address,uint256)": event("0x9e71bc8eea02a63969f509818f2dafb9254532904319f9dbda79b67bd34a5f3d", "Staked(address,uint256)", {"user": indexed(p.address), "amount": p.uint256}), + "Withdrawn(address,uint256)": event("0x7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5", "Withdrawn(address,uint256)", {"user": indexed(p.address), "amount": p.uint256}), +} + +export class Contract extends ContractBase { +} + +/// Event types +export type GovernorshipTransferredEventArgs = EParams +export type NewAirDropRootHashEventArgs = EParams +export type NewDurationsEventArgs = EParams +export type NewRatesEventArgs = EParams +export type PausedEventArgs = EParams +export type PendingGovernorshipTransferEventArgs = EParams +export type StakedEventArgs_0 = EParams +export type StakesTransferedEventArgs = EParams +export type WithdrawnEventArgs_0 = EParams +export type UpgradedEventArgs = EParams +export type StakedEventArgs_1 = EParams +export type WithdrawnEventArgs_1 = EParams diff --git a/src/main-mainnet.ts b/src/main-mainnet.ts index cb2d6770..077ab938 100644 --- a/src/main-mainnet.ts +++ b/src/main-mainnet.ts @@ -15,6 +15,7 @@ import { } from '@utils/addresses' import * as curve from './mainnet/processors/curve' +import * as legacyStaking from './mainnet/processors/legacy-staking' import { erc20s } from './mainnet/processors/erc20s' import * as nativeStaking from './mainnet/processors/native-staking' import * as validate from './mainnet/validators/validate-mainnet' @@ -23,6 +24,7 @@ export const processor = { stateSchema: 'mainnet-processor', processors: [ nativeStaking, + legacyStaking, curve, ...erc20s(), createGovernanceProcessor({ from: 15491391, address: OGV_GOVERNANCE_ADDRESS }), diff --git a/src/mainnet/processors/legacy-staking.ts b/src/mainnet/processors/legacy-staking.ts new file mode 100644 index 00000000..28510117 --- /dev/null +++ b/src/mainnet/processors/legacy-staking.ts @@ -0,0 +1,108 @@ +import * as legacyStakingAbi from '@abi/legacy-staking' +import { LegacyStaker } from '@model' +import { Context } from '@processor' +import { EvmBatchProcessor } from '@subsquid/evm-processor' +import { LEGACY_OGN_STAKING } from '@utils/addresses' + +export const from = 11469389 // https://etherscan.io/tx/0xe6aebd9182872d2a360b281dd60dbd991548da71f8660028526363cac2c80bde + +interface IProcessResult { + legacyStakers: Map +} + +export const setup = (processor: EvmBatchProcessor) => { + processor.addLog({ + address: [LEGACY_OGN_STAKING], + topic0: [ + legacyStakingAbi.events['Staked(address,uint256)'], + legacyStakingAbi.events['Staked(address,uint256,uint256,uint256)'], + legacyStakingAbi.events['Withdrawn(address,uint256)'], + legacyStakingAbi.events['Withdrawn(address,uint256,uint256)'], + legacyStakingAbi.events.StakesTransfered, + ].map((ev) => ev.topic), + range: { from }, + }) +} + +export const process = async (ctx: Context) => { + const result: IProcessResult = { + legacyStakers: new Map(), + } + + for (const block of ctx.blocks) { + for (const log of block.logs) { + const firstTopic = log.topics[0] + + if (![LEGACY_OGN_STAKING].includes(log.address.toLowerCase())) { + continue + } + + if (firstTopic === legacyStakingAbi.events['Staked(address,uint256)'].topic) { + let { user, amount } = legacyStakingAbi.events['Staked(address,uint256)'].decode(log) + const staker = await _getStaker(ctx, user, result) + staker.inputAmount += amount + staker.balance += amount + } else if (firstTopic === legacyStakingAbi.events['Staked(address,uint256,uint256,uint256)'].topic) { + let { user, amount } = legacyStakingAbi.events['Staked(address,uint256,uint256,uint256)'].decode(log) + const staker = await _getStaker(ctx, user, result) + staker.inputAmount += amount + staker.balance += amount + } else if (firstTopic === legacyStakingAbi.events['Withdrawn(address,uint256)'].topic) { + let { user, amount } = legacyStakingAbi.events['Withdrawn(address,uint256)'].decode(log) + const staker = await _getStaker(ctx, user, result) + staker.outputAmount += amount + staker.balance -= amount + } else if (firstTopic === legacyStakingAbi.events['Withdrawn(address,uint256,uint256)'].topic) { + let { user, amount, stakedAmount } = legacyStakingAbi.events['Withdrawn(address,uint256,uint256)'].decode(log) + const staker = await _getStaker(ctx, user, result) + staker.outputAmount += stakedAmount + staker.rewardAmount += amount - stakedAmount + staker.balance -= stakedAmount + } else if (firstTopic === legacyStakingAbi.events.StakesTransfered.topic) { + let { toUser, fromUser } = legacyStakingAbi.events.StakesTransfered.decode(log) + + const toStaker = await _getStaker(ctx, toUser, result) + const fromStaker = await _getStaker(ctx, fromUser, result) + + toStaker.inputAmount += fromStaker.inputAmount + toStaker.outputAmount += fromStaker.outputAmount + toStaker.rewardAmount += fromStaker.rewardAmount + toStaker.balance += fromStaker.balance + + fromStaker.inputAmount = 0n + fromStaker.outputAmount = 0n + fromStaker.rewardAmount = 0n + fromStaker.balance = 0n + } + } + } + + await ctx.store.upsert(Array.from(result.legacyStakers.values())) +} + +const _getStaker = async (ctx: Context, id: string, result: IProcessResult): Promise => { + id = id.toLowerCase() + const { legacyStakers } = result + + if (legacyStakers.has(id)) { + return legacyStakers.get(id)! + } + + let legacyStaker = await ctx.store.findOneBy(LegacyStaker, { + id, + }) + + if (!legacyStaker) { + legacyStaker = new LegacyStaker({ + id: id.toLowerCase(), + inputAmount: 0n, + outputAmount: 0n, + rewardAmount: 0n, + balance: 0n, + }) + } + + legacyStakers.set(id, legacyStaker) + + return legacyStaker +} diff --git a/src/model/generated/index.ts b/src/model/generated/index.ts index 0d2a982d..56efea1c 100644 --- a/src/model/generated/index.ts +++ b/src/model/generated/index.ts @@ -45,6 +45,7 @@ export * from "./governanceProposal.model" export * from "./_governanceProposalState" export * from "./governanceProposalVote.model" export * from "./_governanceVoteType" +export * from "./legacyStaker.model" export * from "./beaconDepositEvent.model" export * from "./beaconDepositPubkey.model" export * from "./oethVault.model" diff --git a/src/model/generated/legacyStaker.model.ts b/src/model/generated/legacyStaker.model.ts new file mode 100644 index 00000000..e042790b --- /dev/null +++ b/src/model/generated/legacyStaker.model.ts @@ -0,0 +1,23 @@ +import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, BigIntColumn as BigIntColumn_} from "@subsquid/typeorm-store" + +@Entity_() +export class LegacyStaker { + constructor(props?: Partial) { + Object.assign(this, props) + } + + @PrimaryColumn_() + id!: string + + @BigIntColumn_({nullable: false}) + inputAmount!: bigint + + @BigIntColumn_({nullable: false}) + outputAmount!: bigint + + @BigIntColumn_({nullable: false}) + balance!: bigint + + @BigIntColumn_({nullable: false}) + rewardAmount!: bigint +} diff --git a/src/utils/addresses.ts b/src/utils/addresses.ts index 07f48258..d5137711 100644 --- a/src/utils/addresses.ts +++ b/src/utils/addresses.ts @@ -64,6 +64,8 @@ export const VEOGV_ADDRESS = '0x0c4576ca1c365868e162554af8e385dc3e7c66d9' export const XOGN_ADDRESS = '0x63898b3b6ef3d39332082178656e9862bee45c57' export const OGN_REWARDS_SOURCE_ADDRESS = '0x7609c88e5880e934dd3a75bcfef44e31b1badb8b' +export const LEGACY_OGN_STAKING = '0x501804b374ef06fa9c427476147ac09f1551b9a0' + // Token Helper Objects & Types export const tokens = { // Origin ERC20 Tokens