diff --git a/root/config/goerli.json b/root/config/goerli.json index f37d455..62e2c06 100644 --- a/root/config/goerli.json +++ b/root/config/goerli.json @@ -15,11 +15,11 @@ }, "rootChainManager": { "address": "0xBbD7cBFA79faee899Eaf900F13C9065bF03B1A74", - "startBlock": 0 + "startBlock": 3000761 }, "stakingInfo": { "address": "0x29C40836C17f22d16a7fE953Fb25DA670C96d69E", - "startBlock": 0 + "startBlock": 2917902 } } } diff --git a/root/config/mainnet.json b/root/config/mainnet.json index 319332c..fc0f879 100644 --- a/root/config/mainnet.json +++ b/root/config/mainnet.json @@ -15,11 +15,11 @@ }, "rootChainManager": { "address": "0xA0c68C638235ee32657e8f720a23ceC1bFc77C77", - "startBlock": 0 + "startBlock": 10735437 }, "stakingInfo": { "address": "0xa59C847Bd5aC0172Ff4FE912C5d29E5A71A7512B", - "startBlock": 0 + "startBlock": 10342572 } } } diff --git a/root/package.json b/root/package.json index e36c1ab..6bd40bf 100644 --- a/root/package.json +++ b/root/package.json @@ -12,7 +12,7 @@ "deploy": "graph deploy --node https://api.thegraph.com/deploy/ --ipfs https://api.thegraph.com/ipfs/ maticnetwork/mumbai-root-subgraphs", "create-local": "graph create --node http://localhost:8020/ maticnetwork/mumbai-root-subgraphs", "remove-local": "graph remove --node http://localhost:8020/ maticnetwork/mumbai-root-subgraphs", - "deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 maticnetwork/mumbai-root-subgraphs" + "deploy-local": "graph deploy --node http://localhost:8020/ --ipfs https://ipfs.infura.io:5001 maticnetwork/mumbai-root-subgraphs" }, "dependencies": { "@graphprotocol/graph-ts": "0.18.0" diff --git a/root/schema.graphql b/root/schema.graphql index 921ffb6..5669894 100644 --- a/root/schema.graphql +++ b/root/schema.graphql @@ -74,21 +74,45 @@ type Validator @entity { validatorId: BigInt! signer: Bytes! signerPubKey: Bytes! - reward: BigInt + liquidatedRewards: BigInt! activationEpoch: BigInt! - deactivationEpoch: BigInt - jailEndEpoch: BigInt - amount: BigInt! - total: BigInt! - commissionRate: BigInt - status: Int! + deactivationEpoch: BigInt! + totalStaked: BigInt! + commissionRate: BigInt! + nonce: BigInt! + unstaked: Boolean! + jailEndEpoch: BigInt! } type Delegator @entity { id: ID! validatorId: BigInt! address: Bytes! - amount: BigInt! + # total delegated amount + delegatedAmount: BigInt! + # total unclaimed amount (after sellVoucher and before claiming it) + unclaimedAmount: BigInt! + # total claimed amount (after withdraw delay, while claiming unstaked amount) + claimedAmount: BigInt! + # total current shares (works until tokens are non-transferable) tokens: BigInt! claimedRewards: BigInt! } + +# Heimdall topups +type Topup @entity { + id: ID! + address: Bytes! + topupAmount: BigInt! + withdrawAmount: BigInt! +} + +# Staking Params across all validators +type StakingParams @entity { + id: ID! + owner: Bytes + validatorThreshold: BigInt! + proposerBonus: BigInt! + dynasty: BigInt! + liquidatedRewards: BigInt! +} diff --git a/root/src/mappings/staking-info.ts b/root/src/mappings/staking-info.ts index 2019c56..54bcd89 100644 --- a/root/src/mappings/staking-info.ts +++ b/root/src/mappings/staking-info.ts @@ -1,229 +1,298 @@ +import { Address, BigInt } from '@graphprotocol/graph-ts' import { - Staked, Unstaked, UnstakeInit, - SignerChange, Restaked, Jailed, - UnJailed, StakeUpdate, ClaimRewards, - ConfirmAuction, ShareMinted, ShareBurned, - UpdateCommissionRate, DelegatorUnstaked, DelegatorClaimedRewards, + Delegator, + Validator, + Topup, + StakingParams, +} from '../../generated/schema' +import { + ClaimFee, + ClaimRewards, + DelegatorClaimedRewards, + DelegatorUnstaked, + DynastyValueChange, + Jailed, + OwnershipTransferred, + ProposerBonusChange, + Restaked, + ShareBurned, + ShareMinted, + SignerChange, + StakeUpdate, + Staked, + ThresholdChange, + TopUpFee, + UnJailed, + UnstakeInit, + Unstaked, + UpdateCommissionRate, } from '../../generated/StakingInfo/StakingInfo' -import { Validator, Delegator } from '../../generated/schema' -export function handleStaked(event: Staked): void { - let id = 'validator-' + event.params.validatorId +const STAKING_PARAMS_ID = 'staking:params' + + +// +// Validator related handlers +// + +function loadValidator(validatorId: BigInt): Validator { + let id = 'validator:' + validatorId.toString() + let entity = Validator.load(id) + if (entity == null) { + entity = new Validator(id) + entity.validatorId = validatorId + entity.totalStaked = BigInt.fromI32(0) + entity.nonce = BigInt.fromI32(0) + entity.deactivationEpoch = BigInt.fromI32(0) + entity.jailEndEpoch = BigInt.fromI32(0) + entity.liquidatedRewards = BigInt.fromI32(0) + entity.unstaked = false + entity.commissionRate = BigInt.fromI32(0) + } + + return entity as Validator +} - let entity = new Validator(id) +export function handleStaked(event: Staked): void { + let validator = loadValidator(event.params.validatorId) - entity.validatorId = event.params.validatorId - entity.signer = event.params.signer - entity.activationEpoch = event.params.activationEpoch - entity.amount = event.params.amount - entity.total = event.params.total - entity.signerPubKey = event.params.signerPubKey + validator.signer = event.params.signer + validator.activationEpoch = event.params.activationEpoch + validator.totalStaked = event.params.total + validator.signerPubKey = event.params.signerPubkey + validator.nonce = event.params.nonce - // save entity - entity.save() + // save entity + validator.save() } export function handleUnstaked(event: Unstaked): void { - let id = 'validator-' + event.params.validatorId - - let entity = Validator.load(id) - if (entity == null) { - entity = new Validator(id) - } - - entity.signer = event.params.user - entity.amount = event.params.amount - entity.total = event.params.total + let validator = loadValidator(event.params.validatorId) - // save entity - entity.save() + // update unstaked status + validator.unstaked = true + validator.save() } export function handleUnstakeInit(event: UnstakeInit): void { - let id = 'validator-' + event.params.validatorId + let validator = loadValidator(event.params.validatorId) - let entity = Validator.load(id) - if (entity == null) { - entity = new Validator(id) - } - - entity.signer = event.params.user - entity.deactivationEpoch = event.params.deactivationEpoch - entity.amount = event.params.amount - - // save entity - entity.save() + // set deactivation epoch + validator.deactivationEpoch = event.params.deactivationEpoch + validator.nonce = event.params.nonce + validator.save() } export function handleSignerChange(event: SignerChange): void { - let id = 'validator-' + event.params.validatorId - - let entity = Validator.load(id) - if (entity == null) { - entity = new Validator(id) - } + let validator = loadValidator(event.params.validatorId) - entity.signer = event.params.newSigner - entity.signerPubKey = event.params.signerPubKey - - // save entity - entity.save() + // save signer changes + validator.signer = event.params.newSigner + validator.signerPubKey = event.params.signerPubkey + validator.save() } export function handleRestaked(event: Restaked): void { - let id = 'validator-' + event.params.validatorId + let validator = loadValidator(event.params.validatorId) - let entity = Validator.load(id) - if (entity == null) { - entity = new Validator(id) - } + // update total staked + validator.totalStaked = event.params.total + validator.save() +} - entity.amount = event.params.amount - entity.total = event.params.total +export function handleJailed(event: Jailed): void { + let validator = loadValidator(event.params.validatorId) - // save entity - entity.save() + // save entity with jail end epoch + validator.jailEndEpoch = event.params.exitEpoch + validator.save() } -export function handleJailed(event: Jailed): void { - let id = 'validator-' + event.params.validatorId +export function handleUnJailed(event: UnJailed): void { + let validator = loadValidator(event.params.validatorId) - let entity = Validator.load(id) - if (entity == null) { - entity = new Validator(id) - } + // save entity with jail end epoch + validator.jailEndEpoch = BigInt.fromI32(0) + validator.save() +} - entity.jailEndEpoch = event.params.exitEpoch +export function handleStakeUpdate(event: StakeUpdate): void { + let validator = loadValidator(event.params.validatorId) - // save entity - entity.save() + // update total staked and nonce + validator.totalStaked = event.params.newAmount + validator.nonce = event.params.nonce + validator.save() } -export function handleUnJailed(event: UnJailed): void { - let id = 'validator-' + event.params.validatorId +export function handleClaimRewards(event: ClaimRewards): void { + let validator = loadValidator(event.params.validatorId) - let entity = Validator.load(id) - if (entity == null) { - entity = new Validator(id) - } + // update rewards for validator + validator.liquidatedRewards = validator.liquidatedRewards.plus(event.params.amount) + validator.save() - // save entity - entity.save() + // update staking params + let stakingParams = loadStakingParams() + stakingParams.liquidatedRewards = event.params.totalAmount + stakingParams.save() } -export function handleStakeUpdate(event: StakeUpdate): void { - let id = 'validator-' + event.params.validatorId - let entity = Validator.load(id) - if (entity == null) { - entity = new Validator(id) - } - entity.amount = event.params.newAmount +export function handleUpdateCommissionRate(event: UpdateCommissionRate): void { + let validator = loadValidator(event.params.validatorId) + + // save validator entity with new commission rate + validator.commissionRate = event.params.newCommissionRate + validator.save() +} - // save entity - entity.save() +// +// Delegator related handlers +// + +function loadDelegator(validatorId: BigInt, delegator: Address): Delegator { + let id = 'delegator:' + validatorId.toString() + ':' + delegator.toHexString() + let entity = Delegator.load(id) + if (entity == null) { + entity = new Delegator(id) + entity.validatorId = validatorId + entity.address = delegator + entity.delegatedAmount = BigInt.fromI32(0) + entity.claimedAmount = BigInt.fromI32(0) + entity.unclaimedAmount = BigInt.fromI32(0) + entity.tokens = BigInt.fromI32(0) + entity.claimedRewards = BigInt.fromI32(0) + } + + return entity as Delegator } -export function handleClaimRewards(event: ClaimRewards): void { - let id = 'validator-' + event.params.validatorId +export function handleShareMinted(event: ShareMinted): void { + let delegator = loadDelegator(event.params.validatorId, event.params.user) + + // update delegatedAmount and tokens + delegator.delegatedAmount = delegator.delegatedAmount.plus(event.params.amount) + // this works until tokens are not transaferable + delegator.tokens = delegator.tokens.plus(event.params.tokens) - let entity = Validator.load(id) - if (entity == null) { - entity = new Validator(id) - } + // save entity + delegator.save() +} + +export function handleShareBurned(event: ShareBurned): void { + let delegator = loadDelegator(event.params.validatorId, event.params.user) - entity.amount = event.params.amount - entity.total = event.params.totalAmount + // update claimedAmount and tokens + // it is possible to have: claimed amount < amount (when slashing happens) + // that's why having claimedAmount would be better + delegator.unclaimedAmount = delegator.unclaimedAmount.plus(event.params.amount) + // this works until tokens are not transaferable + delegator.tokens = delegator.tokens.minus(event.params.tokens) - // save entity - entity.save() + // save entity + delegator.save() } -export function handleConfirmAuction(event: ConfirmAuction): void { - let id = 'validator-' + event.params.validatorId +export function handleDelegatorUnstaked(event: DelegatorUnstaked): void { + let delegator = loadDelegator(event.params.validatorId, event.params.user) - let entity = Validator.load(id) - if (entity == null) { - entity = new Validator(id) - } + // update unclaimed amount by deducting total unclaimed amount + delegator.unclaimedAmount = delegator.unclaimedAmount.minus(event.params.amount) + // update claimed amount + delegator.claimedAmount = delegator.claimedAmount.plus(event.params.amount) + delegator.save() +} - entity.validatorId = event.params.newValidatorId +export function handleDelegatorClaimedRewards(event: DelegatorClaimedRewards): void { + let delegator = loadDelegator(event.params.validatorId, event.params.user) - // save entity - entity.save() + // update total claimed rewards by current event's rewards + delegator.claimedRewards = delegator.claimedRewards.plus(event.params.rewards) + delegator.save() } -export function handleShareMinted(event: ShareMinted): void { - let id = 'delegator-' + event.params.validatorId + event.params.user.toHexString() +// +// Topup +// - let entity = Delegator.load(id) - if (entity == null) { - entity = new Delegator(id) - } +function loadTopupAccount(user: Address): Topup { + let id = 'topup:' + user.toHexString() - entity.validatorId = event.params.validatorId - entity.address = event.params.user - entity.amount = entity.amount + event.params.amount - entity.tokens = event.params.tokens + let entity = Topup.load(id) + if (entity == null) { + entity = new Topup(id) + entity.address = user // set user + entity.topupAmount = BigInt.fromI32(0) // initialize topup amount + entity.withdrawAmount = BigInt.fromI32(0) // initialize topup amount + } - // save entity - entity.save() + return entity as Topup } -export function handleShareBurned(event: ShareBurned): void { - let id = 'delegator-' + event.params.validatorId + event.params.user.toHexString() +export function handleClaimFee(event: ClaimFee): void { + let topup = loadTopupAccount(event.params.user) - let entity = Delegator.load(id) - if (entity == null) { - entity = new Delegator(id) - } + // save entity with topup amount + topup.topupAmount = topup.topupAmount.plus(event.params.fee) + topup.save() +} - entity.amount = entity.amount - event.params.amount - entity.tokens = event.params.tokens +export function handleTopUpFee(event: TopUpFee): void { + let topup = loadTopupAccount(event.params.user) - // save entity - entity.save() + // save entity with withdraw amount + topup.withdrawAmount = topup.withdrawAmount.plus(event.params.fee) + topup.save() } -export function handleDelegatorUnstaked(event: DelegatorUnstaked): void { - let id = 'delegator-' + event.params.validatorId + event.params.user.toHexString() +// +// Staking params handlers +// + +function loadStakingParams(): StakingParams { + let entity = StakingParams.load(STAKING_PARAMS_ID) + if (entity == null) { + entity = new StakingParams(STAKING_PARAMS_ID) + entity.dynasty = BigInt.fromI32(0) + entity.proposerBonus = BigInt.fromI32(0) + entity.validatorThreshold = BigInt.fromI32(0) + entity.liquidatedRewards = BigInt.fromI32(0) + } + + return entity as StakingParams +} - let entity = Delegator.load(id) - if (entity == null) { - entity = new Delegator(id) - } +export function handleDynastyValueChange(event: DynastyValueChange): void { + let stakingParams = loadStakingParams() - entity.amount = entity.amount - event.params.amount - - // save entity - entity.save() + // save entity with dynasty + stakingParams.dynasty = event.params.newDynasty + stakingParams.save() } -export function handleDelegatorClaimedRewards(event: DelegatorClaimedRewards): void { - let id = 'delegator-' + event.params.validatorId + event.params.user.toHexString() - let entity = Delegator.load(id) - if (entity == null) { - entity = new Delegator(id) - } +export function handleOwnershipTransferred(event: OwnershipTransferred): void { + let stakingParams = loadStakingParams() - entity.claimedRewards = entity.claimedRewards + event.params.rewards - - // save entity - entity.save() + // save entity with owner + stakingParams.owner = event.params.newOwner + stakingParams.save() } -export function handleUpdateCommissionRate(event: UpdateCommissionRate): void { - let id = 'validator-' + event.params.validatorId +export function handleProposerBonusChange(event: ProposerBonusChange): void { + let stakingParams = loadStakingParams() - let entity = Validator.load(id) - if (entity == null) { - entity = new Validator(id) - } + // save entity with proposer bonus + stakingParams.proposerBonus = event.params.newProposerBonus + stakingParams.save() +} - entity.commissionRate = event.params.newCommissionRate +export function handleThresholdChange(event: ThresholdChange): void { + let stakingParams = loadStakingParams() - // save entity - entity.save() + // save entity with validator threshold + stakingParams.validatorThreshold = event.params.newThreshold + stakingParams.save() } diff --git a/root/subgraph.template.yaml b/root/subgraph.template.yaml index 5923db0..0f36dfd 100644 --- a/root/subgraph.template.yaml +++ b/root/subgraph.template.yaml @@ -102,7 +102,71 @@ dataSources: handler: handleRegistrationUpdated file: ./src/mappings/state-sync.ts -# StakingInfo contract's event handlers not being added intentionally -# because they don't seem to work as they're supposed to be working -# -# More investigation required in that front + - kind: ethereum/contract + name: StakingInfo + network: {{ network }} + source: + address: "{{contracts.stakingInfo.address}}" + abi: StakingInfo + startBlock: {{ contracts.stakingInfo.startBlock }} + mapping: + kind: ethereum/events + apiVersion: 0.0.4 + language: wasm/assemblyscript + entities: + - Validator + - Delegator + - StakingParams + - Topup + abis: + - name: StakingInfo + file: ./abis/StakingInfo.json + eventHandlers: + - event: ClaimFee(indexed address,indexed uint256) + handler: handleClaimFee + - event: ClaimRewards(indexed uint256,indexed uint256,indexed uint256) + handler: handleClaimRewards + # - event: ConfirmAuction(indexed uint256,indexed uint256,indexed uint256) + # handler: handleConfirmAuction + - event: DelegatorClaimedRewards(indexed uint256,indexed address,indexed uint256) + handler: handleDelegatorClaimedRewards + # - event: DelegatorRestaked(indexed uint256,indexed address,indexed uint256) + # handler: handleDelegatorRestaked + - event: DelegatorUnstaked(indexed uint256,indexed address,uint256) + handler: handleDelegatorUnstaked + - event: DynastyValueChange(uint256,uint256) + handler: handleDynastyValueChange + - event: Jailed(indexed uint256,indexed uint256,indexed address) + handler: handleJailed + - event: OwnershipTransferred(indexed address,indexed address) + handler: handleOwnershipTransferred + - event: ProposerBonusChange(uint256,uint256) + handler: handleProposerBonusChange + - event: Restaked(indexed uint256,uint256,uint256) + handler: handleRestaked + # - event: RewardUpdate(uint256,uint256) + - event: ShareBurned(indexed uint256,indexed address,indexed uint256,uint256) + handler: handleShareBurned + - event: ShareMinted(indexed uint256,indexed address,indexed uint256,uint256) + handler: handleShareMinted + - event: SignerChange(indexed uint256,uint256,indexed address,indexed address,bytes) + handler: handleSignerChange + # - event: Slashed(indexed uint256,indexed uint256) + - event: StakeUpdate(indexed uint256,indexed uint256,indexed uint256) + handler: handleStakeUpdate + - event: Staked(indexed address,indexed uint256,uint256,indexed uint256,uint256,uint256,bytes) + handler: handleStaked + # - event: StartAuction(indexed uint256,indexed uint256,indexed uint256) + - event: ThresholdChange(uint256,uint256) + handler: handleThresholdChange + - event: TopUpFee(indexed address,indexed uint256) + handler: handleTopUpFee + - event: UnJailed(indexed uint256,indexed address) + handler: handleUnJailed + - event: UnstakeInit(indexed address,indexed uint256,uint256,uint256,indexed uint256) + handler: handleUnstakeInit + - event: Unstaked(indexed address,indexed uint256,uint256,uint256) + handler: handleUnstaked + - event: UpdateCommissionRate(indexed uint256,indexed uint256,indexed uint256) + handler: handleUpdateCommissionRate + file: ./src/mappings/staking-info.ts