From 53b6b3ac89953a9ea23be389f77602baa19b1251 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 3 Aug 2026 11:05:24 +0200 Subject: [PATCH] feat: index RewardCallerSet reward caller delegation livepeer/protocol#648 lets an orchestrator nominate an address to call reward() on its behalf. The subgraph had no visibility into these delegations. Adds Transcoder.rewardCaller (null when unset) and RewardCallerSetEvent, wired to RewardCallerSet(indexed address,indexed address). The Reward path is unchanged: #648 kept emitting Reward(_transcoder), so attribution stays on the orchestrator and both existing handlers remain correct. The RewardCallerSet ABI entry was added by hand from the protocol deployment artifact. See #252. --- abis/BondingManager.json | 19 +++++++++++++++++++ package.json | 2 +- schema.graphql | 20 ++++++++++++++++++++ src/mappings/bondingManager.ts | 30 ++++++++++++++++++++++++++++++ subgraph.template.yaml | 3 +++ 5 files changed, 73 insertions(+), 1 deletion(-) diff --git a/abis/BondingManager.json b/abis/BondingManager.json index 2576e01..3225c21 100644 --- a/abis/BondingManager.json +++ b/abis/BondingManager.json @@ -820,6 +820,25 @@ "name": "Reward", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "rewardCaller", + "type": "address" + } + ], + "name": "RewardCallerSet", + "type": "event" + }, { "anonymous": false, "inputs": [ diff --git a/package.json b/package.json index d62c6dc..6c67250 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@livepeer/subgraph", - "version": "1.3.3", + "version": "1.4.0", "license": "MIT", "scripts": { "prepare": "yarn prepare:arbitrum-one && yarn codegen && yarn codegen:typechain", diff --git a/schema.graphql b/schema.graphql index b6ac70d..721ed11 100755 --- a/schema.graphql +++ b/schema.graphql @@ -106,6 +106,8 @@ type Transcoder @entity { delegator: Delegator "Service URI endpoint that can be used to send off-chain requests" serviceURI: String + "Address delegated to call reward on the transcoder's behalf - null if unset" + rewardCaller: String "Days which the transcoder earned fees" transcoderDays: [TranscoderDay!]! } @@ -586,6 +588,24 @@ type RewardEvent implements Event @entity { delegate: Transcoder! } +""" +RewardCallerSetEvent entities are created for every emitted RewardCallerSet event. +""" +type RewardCallerSetEvent implements Event @entity { + "Ethereum transaction hash + event log index" + id: ID! + "Reference to the transaction the event was included in" + transaction: Transaction! + "Timestamp of the transaction the event was included in" + timestamp: Int! + "Reference to the round the event occured in" + round: Round! + "Reference to the transcoder that set the reward caller" + delegate: Transcoder! + "Address authorized to call reward - the zero address when unset" + rewardCaller: String! +} + """ TranscoderActivatedEvent entities are created for every emitted TranscoderActivated event. """ diff --git a/src/mappings/bondingManager.ts b/src/mappings/bondingManager.ts index 2a49346..5218349 100755 --- a/src/mappings/bondingManager.ts +++ b/src/mappings/bondingManager.ts @@ -23,6 +23,7 @@ import { ParameterUpdate, Rebond, Reward, + RewardCallerSet, TranscoderActivated, TranscoderDeactivated, TranscoderSlashed, @@ -37,6 +38,7 @@ import { EarningsClaimedEvent, ParameterUpdateEvent, RebondEvent, + RewardCallerSetEvent, RewardEvent, TranscoderActivatedEvent, TranscoderDeactivatedEvent, @@ -574,6 +576,34 @@ export function transcoderUpdate(event: TranscoderUpdate): void { transcoderUpdateEvent.save(); } +export function rewardCallerSet(event: RewardCallerSet): void { + let round = createOrLoadRound(getBlockNum()); + let transcoder = createOrLoadTranscoder( + event.params.transcoder.toHex(), + event.block.timestamp.toI32() + ); + + // The zero address unsets the reward caller + if (event.params.rewardCaller.toHex() == EMPTY_ADDRESS.toHex()) { + transcoder.rewardCaller = null; + } else { + transcoder.rewardCaller = event.params.rewardCaller.toHex(); + } + transcoder.save(); + + createOrLoadTransactionFromEvent(event); + + let rewardCallerSetEvent = new RewardCallerSetEvent( + makeEventId(event.transaction.hash, event.logIndex) + ); + rewardCallerSetEvent.transaction = event.transaction.hash.toHex(); + rewardCallerSetEvent.timestamp = event.block.timestamp.toI32(); + rewardCallerSetEvent.round = round.id; + rewardCallerSetEvent.delegate = event.params.transcoder.toHex(); + rewardCallerSetEvent.rewardCaller = event.params.rewardCaller.toHex(); + rewardCallerSetEvent.save(); +} + export function transcoderActivated(event: TranscoderActivated): void { let round = createOrLoadRound(getBlockNum()); let transcoder = createOrLoadTranscoder( diff --git a/subgraph.template.yaml b/subgraph.template.yaml index 4f9163e..b296328 100644 --- a/subgraph.template.yaml +++ b/subgraph.template.yaml @@ -31,6 +31,7 @@ dataSources: - Pool - Protocol - RebondEvent + - RewardCallerSetEvent - RewardEvent - Transaction - Transcoder @@ -61,6 +62,8 @@ dataSources: handler: withdrawStake - event: Reward(indexed address,uint256) handler: reward + - event: RewardCallerSet(indexed address,indexed address) + handler: rewardCallerSet - event: WithdrawFees(indexed address,address,uint256) handler: withdrawFees - event: ParameterUpdate(string)