diff --git a/indexer/config.local.yaml b/indexer/config.local.yaml index 98dc336..889abae 100644 --- a/indexer/config.local.yaml +++ b/indexer/config.local.yaml @@ -48,7 +48,14 @@ networks: - event: OperatorRegistered(uint64 indexed blueprintId, address indexed operator, bytes ecdsaPublicKey, string rpcAddress) - event: OperatorUnregistered(uint64 indexed blueprintId, address indexed operator) - event: Paused(address account) - - event: PaymentDistributed(uint64 indexed serviceId, uint64 indexed blueprintId, address indexed token, uint256 grossAmount, address developerRecipient, uint256 developerAmount, uint256 protocolAmount, uint256 operatorPoolAmount, uint256 stakingPoolAmount) + - event: PaymentDistributed(uint64 indexed serviceId, uint64 indexed blueprintId, address indexed token, uint256 grossAmount, address developerRecipient, uint256 developerAmount, uint256 protocolAmount, uint256 operatorPoolAmount, uint256 stakerPoolAmount) + - event: KeeperRebateAccrued(uint64 indexed serviceId, address indexed keeper, address indexed token, uint256 amount) + - event: TntPaymentDiscountApplied(uint64 indexed serviceId, address indexed recipient, address indexed token, uint256 amount) + - event: StakerShareRefundedToEscrow(uint64 indexed serviceId, address indexed operator, address indexed token, uint256 amount, bytes reason) + - event: SubscriptionBaselineInitialized(uint64 indexed serviceId, uint256 baselineStake, uint256 operatorCount) + - event: SubscriptionBillSkippedNoOperators(uint64 indexed serviceId, uint64 period) + - event: SubscriptionBillAdjustedByManager(uint64 indexed serviceId, uint256 preAdjustmentAmount, uint256 adjustedAmount, uint16 adjustmentBps) + - event: RewardsClaimSkipped(address indexed account, address indexed token) - event: OperatorRewardAccrued(uint64 indexed serviceId, address indexed operator, address indexed token, uint64 blueprintId, uint256 amount) - event: QuoteUsed(address indexed operator, bytes32 indexed quoteHash) - event: RewardsClaimed(address indexed account, address indexed token, uint256 amount) @@ -128,7 +135,7 @@ networks: - event: SharesUpdated(address indexed owner, int256 sharesDelta, int256 newShares) - event: OperatorRegistered(address indexed operator) - event: OperatorDeregistered(address indexed operator) - - event: DelegatorSlashed(address indexed delegator, address indexed operator, uint256 amount) + - event: OperatorPoolSlashed(address indexed operator, uint256 slashedAssets, uint256 newTotalAssets, uint256 totalShares) - event: WithdrawalQueued(bytes32 indexed withdrawalRoot, address indexed staker, uint256 shares) - event: WithdrawalCompleted(bytes32 indexed withdrawalRoot, address indexed staker, uint256 shares) - name: LiquidDelegationFactory diff --git a/indexer/config.yaml b/indexer/config.yaml index d3a57bc..976120c 100644 --- a/indexer/config.yaml +++ b/indexer/config.yaml @@ -39,7 +39,14 @@ networks: - event: OperatorRegistered(uint64 indexed blueprintId, address indexed operator, bytes ecdsaPublicKey, string rpcAddress) - event: OperatorUnregistered(uint64 indexed blueprintId, address indexed operator) - event: Paused(address account) - - event: PaymentDistributed(uint64 indexed serviceId, uint64 indexed blueprintId, address indexed token, uint256 grossAmount, address developerRecipient, uint256 developerAmount, uint256 protocolAmount, uint256 operatorPoolAmount, uint256 stakingPoolAmount) + - event: PaymentDistributed(uint64 indexed serviceId, uint64 indexed blueprintId, address indexed token, uint256 grossAmount, address developerRecipient, uint256 developerAmount, uint256 protocolAmount, uint256 operatorPoolAmount, uint256 stakerPoolAmount) + - event: KeeperRebateAccrued(uint64 indexed serviceId, address indexed keeper, address indexed token, uint256 amount) + - event: TntPaymentDiscountApplied(uint64 indexed serviceId, address indexed recipient, address indexed token, uint256 amount) + - event: StakerShareRefundedToEscrow(uint64 indexed serviceId, address indexed operator, address indexed token, uint256 amount, bytes reason) + - event: SubscriptionBaselineInitialized(uint64 indexed serviceId, uint256 baselineStake, uint256 operatorCount) + - event: SubscriptionBillSkippedNoOperators(uint64 indexed serviceId, uint64 period) + - event: SubscriptionBillAdjustedByManager(uint64 indexed serviceId, uint256 preAdjustmentAmount, uint256 adjustedAmount, uint16 adjustmentBps) + - event: RewardsClaimSkipped(address indexed account, address indexed token) - event: OperatorRewardAccrued(uint64 indexed serviceId, address indexed operator, address indexed token, uint64 blueprintId, uint256 amount) - event: QuoteUsed(address indexed operator, bytes32 indexed quoteHash) - event: RewardsClaimed(address indexed account, address indexed token, uint256 amount) @@ -118,7 +125,7 @@ networks: - event: BeaconRebase(address indexed owner, int256 assetsDelta, uint256 newTotalAssets, uint256 totalSharesPool) - event: OperatorRegistered(address indexed operator) - event: OperatorDeregistered(address indexed operator) - - event: DelegatorSlashed(address indexed delegator, address indexed operator, uint256 amount) + - event: OperatorPoolSlashed(address indexed operator, uint256 slashedAssets, uint256 newTotalAssets, uint256 totalShares) - event: WithdrawalQueued(bytes32 indexed withdrawalRoot, address indexed staker, uint256 shares, uint256 assets) - event: WithdrawalCompleted(bytes32 indexed withdrawalRoot, address indexed staker, uint256 shares, uint256 assets) - name: LiquidDelegationFactory diff --git a/indexer/schema.graphql b/indexer/schema.graphql index 3f7d9d6..c1b22e4 100644 --- a/indexer/schema.graphql +++ b/indexer/schema.graphql @@ -205,15 +205,6 @@ type ValidatorPodWithdrawal { txHash: String! } -type ValidatorPodSlash { - id: ID! - delegator: String! - operator: String! - amount: BigInt! - timestamp: BigInt! - txHash: String! -} - type Operator { id: ID! ecdsaPublicKey: String @@ -392,11 +383,103 @@ type PaymentDistribution { developerAmount: BigInt! protocolAmount: BigInt! operatorPoolAmount: BigInt! - stakingPoolAmount: BigInt! + stakerPoolAmount: BigInt! distributedAt: BigInt! txHash: String! } +type KeeperRebate { + id: ID! + serviceId: BigInt! + keeper: String! + token: String! + amount: BigInt! + accruedAt: BigInt! + txHash: String! +} + +type TntPaymentDiscount { + id: ID! + serviceId: BigInt! + recipient: String! + token: String! + amount: BigInt! + appliedAt: BigInt! + txHash: String! +} + +""" +Staker-pool share that could not be routed to the fee distributor and was +refunded back into the service escrow. `reason` is the raw revert bytes from +the failed external call. +""" +type StakerShareEscrowRefund { + id: ID! + serviceId: BigInt! + operator: String! + token: String! + amount: BigInt! + reason: String! + refundedAt: BigInt! + txHash: String! +} + +""" +Per-service subscription baseline captured at activation. Keyed by service id. +""" +type SubscriptionBaseline { + id: ID! + service: Service! + baselineStake: BigInt! + operatorCount: BigInt! + initializedAt: BigInt! + txHash: String! +} + +type SubscriptionBillSkipped { + id: ID! + service: Service! + period: BigInt! + skippedAt: BigInt! + txHash: String! +} + +type SubscriptionBillAdjustment { + id: ID! + service: Service! + preAdjustmentAmount: BigInt! + adjustedAmount: BigInt! + adjustmentBps: Int! + adjustedAt: BigInt! + txHash: String! +} + +""" +O(1) pool slash emitted by ValidatorPodManager. Per-delegator loss is derived +off-chain from cached share balances against `newTotalAssets`/`totalShares`. +""" +type OperatorPoolSlash { + id: ID! + operator: String! + slashedAssets: BigInt! + newTotalAssets: BigInt! + totalShares: BigInt! + slashedAt: BigInt! + txHash: String! +} + +""" +Per-token reward sweep skip from `claimRewardsAll`. Recorded so operators can +identify which token griefed their sweep. +""" +type RewardsClaimSkip { + id: ID! + account: String! + token: String! + skippedAt: BigInt! + txHash: String! +} + type DeveloperPayment { id: ID! distribution: PaymentDistribution! diff --git a/indexer/src/handlers/tangle.ts b/indexer/src/handlers/tangle.ts index f473e4f..d69bd95 100644 --- a/indexer/src/handlers/tangle.ts +++ b/indexer/src/handlers/tangle.ts @@ -7,6 +7,7 @@ import type { JobCall, JobEventRate, JobResult, + KeeperRebate, OperatorRewardAccrual, Operator, OperatorIntent, @@ -15,6 +16,7 @@ import type { ProtocolState, QuoteUsage, RewardClaim, + RewardsClaimSkip, Role, RoleAssignment, Service, @@ -22,7 +24,12 @@ import type { ServiceRequest, SlashConfig, SlashProposal, + StakerShareEscrowRefund, + SubscriptionBaseline, SubscriptionBilling, + SubscriptionBillAdjustment, + SubscriptionBillSkipped, + TntPaymentDiscount, Upgrade, } from "generated/src/Types.gen"; import { @@ -850,7 +857,7 @@ export function registerTangleHandlers() { developerAmount: toBigInt(event.params.developerAmount), protocolAmount: toBigInt(event.params.protocolAmount), operatorPoolAmount: toBigInt(event.params.operatorPoolAmount), - stakingPoolAmount: toBigInt(event.params.stakingPoolAmount), + stakerPoolAmount: toBigInt(event.params.stakerPoolAmount), distributedAt: timestamp, txHash: getTxHash(event), } as PaymentDistribution; @@ -908,6 +915,101 @@ export function registerTangleHandlers() { context.RewardClaim.set(claim); }); + Tangle.KeeperRebateAccrued.handler(async ({ event, context }) => { + const timestamp = getTimestamp(event); + const rebate: KeeperRebate = { + id: getEventId(event), + serviceId: toBigInt(event.params.serviceId), + keeper: normalizeAddress(event.params.keeper), + token: normalizeAddress(event.params.token), + amount: toBigInt(event.params.amount), + accruedAt: timestamp, + txHash: getTxHash(event), + } as KeeperRebate; + context.KeeperRebate.set(rebate); + }); + + Tangle.TntPaymentDiscountApplied.handler(async ({ event, context }) => { + const timestamp = getTimestamp(event); + const discount: TntPaymentDiscount = { + id: getEventId(event), + serviceId: toBigInt(event.params.serviceId), + recipient: normalizeAddress(event.params.recipient), + token: normalizeAddress(event.params.token), + amount: toBigInt(event.params.amount), + appliedAt: timestamp, + txHash: getTxHash(event), + } as TntPaymentDiscount; + context.TntPaymentDiscount.set(discount); + }); + + Tangle.StakerShareRefundedToEscrow.handler(async ({ event, context }) => { + const timestamp = getTimestamp(event); + const refund: StakerShareEscrowRefund = { + id: getEventId(event), + serviceId: toBigInt(event.params.serviceId), + operator: normalizeAddress(event.params.operator), + token: normalizeAddress(event.params.token), + amount: toBigInt(event.params.amount), + reason: toHexString(event.params.reason), + refundedAt: timestamp, + txHash: getTxHash(event), + } as StakerShareEscrowRefund; + context.StakerShareEscrowRefund.set(refund); + }); + + Tangle.SubscriptionBaselineInitialized.handler(async ({ event, context }) => { + const timestamp = getTimestamp(event); + const serviceId = toBigInt(event.params.serviceId).toString(); + const baseline: SubscriptionBaseline = { + id: serviceId, + service_id: serviceId, + baselineStake: toBigInt(event.params.baselineStake), + operatorCount: toBigInt(event.params.operatorCount), + initializedAt: timestamp, + txHash: getTxHash(event), + } as SubscriptionBaseline; + context.SubscriptionBaseline.set(baseline); + }); + + Tangle.SubscriptionBillSkippedNoOperators.handler(async ({ event, context }) => { + const timestamp = getTimestamp(event); + const skip: SubscriptionBillSkipped = { + id: getEventId(event), + service_id: toBigInt(event.params.serviceId).toString(), + period: toBigInt(event.params.period), + skippedAt: timestamp, + txHash: getTxHash(event), + } as SubscriptionBillSkipped; + context.SubscriptionBillSkipped.set(skip); + }); + + Tangle.SubscriptionBillAdjustedByManager.handler(async ({ event, context }) => { + const timestamp = getTimestamp(event); + const adjustment: SubscriptionBillAdjustment = { + id: getEventId(event), + service_id: toBigInt(event.params.serviceId).toString(), + preAdjustmentAmount: toBigInt(event.params.preAdjustmentAmount), + adjustedAmount: toBigInt(event.params.adjustedAmount), + adjustmentBps: toNumber(event.params.adjustmentBps), + adjustedAt: timestamp, + txHash: getTxHash(event), + } as SubscriptionBillAdjustment; + context.SubscriptionBillAdjustment.set(adjustment); + }); + + Tangle.RewardsClaimSkipped.handler(async ({ event, context }) => { + const timestamp = getTimestamp(event); + const skip: RewardsClaimSkip = { + id: getEventId(event), + account: normalizeAddress(event.params.account), + token: normalizeAddress(event.params.token), + skippedAt: timestamp, + txHash: getTxHash(event), + } as RewardsClaimSkip; + context.RewardsClaimSkip.set(skip); + }); + Tangle.RoleAdminChanged.handler(async ({ event, context }) => { const timestamp = getTimestamp(event); const role: Role = { diff --git a/indexer/src/handlers/validatorPods.ts b/indexer/src/handlers/validatorPods.ts index b9b89aa..31e82c4 100644 --- a/indexer/src/handlers/validatorPods.ts +++ b/indexer/src/handlers/validatorPods.ts @@ -1,11 +1,11 @@ import { ValidatorPodManager } from "generated"; import type { Operator, + OperatorPoolSlash, ValidatorPod, ValidatorPodShareEvent, ValidatorPodBeaconRebase, ValidatorPodWithdrawal, - ValidatorPodSlash, NativeOperator, } from "generated/src/Types.gen"; import { @@ -128,17 +128,18 @@ export function registerValidatorPodHandlers() { context.NativeOperator.set(updated); }); - ValidatorPodManager.DelegatorSlashed.handler(async ({ event, context }) => { + ValidatorPodManager.OperatorPoolSlashed.handler(async ({ event, context }) => { const timestamp = getTimestamp(event); - const slash: ValidatorPodSlash = { - id: `slash-${getTxHash(event)}-${event.logIndex}`, - delegator: normalizeAddress(event.params.delegator), + const slash: OperatorPoolSlash = { + id: `pool-slash-${getTxHash(event)}-${event.logIndex}`, operator: normalizeAddress(event.params.operator), - amount: toBigInt(event.params.amount), - timestamp, + slashedAssets: toBigInt(event.params.slashedAssets), + newTotalAssets: toBigInt(event.params.newTotalAssets), + totalShares: toBigInt(event.params.totalShares), + slashedAt: timestamp, txHash: getTxHash(event), - } as ValidatorPodSlash; - context.ValidatorPodSlash.set(slash); + } as OperatorPoolSlash; + context.OperatorPoolSlash.set(slash); }); ValidatorPodManager.WithdrawalQueued.handler(async ({ event, context }) => {