Skip to content

Commit bf0e9f1

Browse files
committed
feat: add gateway fee tracking
Added gateway fee tracking with daily snapshots and rolling-window metrics like transcoders, and stored the active gateway set on Protocol so newRound can recompute 30/60/90 day sums efficiently.
1 parent bb88ef2 commit bf0e9f1

File tree

4 files changed

+161
-14
lines changed

4 files changed

+161
-14
lines changed

schema.graphql

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ type Protocol @entity {
5454
pendingDeactivation: [Transcoder!]!
5555
"Total number of delegators on the network"
5656
delegatorsCount: BigInt!
57+
"Broadcasters active within the current 90 day fee window"
58+
activeBroadcasters: [String!]!
5759
}
5860

5961
"""
@@ -231,6 +233,20 @@ type Broadcaster @entity {
231233
deposit: BigDecimal!
232234
"Amount of funds in reserve"
233235
reserve: BigDecimal!
236+
"Total fees paid out by this broadcaster in ETH"
237+
totalVolumeETH: BigDecimal!
238+
"Total fees paid out by this broadcaster in USD"
239+
totalVolumeUSD: BigDecimal!
240+
"Fees paid out by this broadcaster in ETH during the last 30 days"
241+
thirtyDayVolumeETH: BigDecimal!
242+
"Fees paid out by this broadcaster in ETH during the last 60 days"
243+
sixtyDayVolumeETH: BigDecimal!
244+
"Fees paid out by this broadcaster in ETH during the last 90 days"
245+
ninetyDayVolumeETH: BigDecimal!
246+
"Last day (timestamp) this broadcaster paid fees"
247+
lastActiveDay: Int!
248+
"Days in which this broadcaster paid out fees"
249+
broadcasterDays: [BroadcasterDay!]!
234250
}
235251

236252
"""
@@ -354,6 +370,22 @@ type TranscoderDay @entity {
354370
transcoder: Transcoder!
355371
}
356372

373+
"""
374+
Broadcaster data accumulated and condensed into day stats
375+
"""
376+
type BroadcasterDay @entity {
377+
"Combination of the broadcaster address and the timestamp rounded to the current day by dividing by 86400"
378+
id: ID!
379+
"The date beginning at 12:00am UTC"
380+
date: Int!
381+
"Fees paid this day in ETH"
382+
volumeETH: BigDecimal!
383+
"Fees paid this day in USD"
384+
volumeUSD: BigDecimal!
385+
"Broadcaster associated with the day"
386+
broadcaster: Broadcaster!
387+
}
388+
357389
type TreasuryProposal @entity {
358390
"Governor proposal ID formatted as a decimal number"
359391
id: ID!

src/mappings/roundsManager.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import {
2828
} from "../types/RoundsManager/RoundsManager";
2929
// Import entity types generated from the GraphQL schema
3030
import {
31+
Broadcaster,
32+
BroadcasterDay,
3133
NewRoundEvent,
3234
ParameterUpdateEvent,
3335
Pool,
@@ -177,6 +179,59 @@ export function newRound(event: NewRound): void {
177179
}
178180
}
179181

182+
// Update rolling fee windows for tracked broadcasters (gateways)
183+
let trackedBroadcasters = protocol.activeBroadcasters;
184+
let activeBroadcasters: string[] = [];
185+
if (trackedBroadcasters && trackedBroadcasters.length) {
186+
for (let i = 0; i < trackedBroadcasters.length; i++) {
187+
let broadcaster = Broadcaster.load(trackedBroadcasters[i]);
188+
189+
if (broadcaster) {
190+
// --- Get the 30, 60, 90 day sums of volume ---
191+
let broadcasterThirtyDaySum = ZERO_BD;
192+
let broadcasterSixtyDaySum = ZERO_BD;
193+
let broadcasterNinetyDaySum = ZERO_BD;
194+
195+
// capped at <90 - broadcaster days are ordered newest first
196+
let broadcasterDays = broadcaster.broadcasterDays;
197+
let broadcasterDaysLength =
198+
broadcasterDays.length > 90 ? 90 : broadcasterDays.length;
199+
for (let j = 0; j < broadcasterDaysLength; j++) {
200+
let broadcasterDay = BroadcasterDay.load(broadcasterDays[j]);
201+
202+
if (broadcasterDay) {
203+
if (broadcasterDay.date >= thirtyDayTimestamp) {
204+
broadcasterThirtyDaySum = broadcasterThirtyDaySum.plus(
205+
broadcasterDay.volumeETH
206+
);
207+
}
208+
if (broadcasterDay.date >= sixtyDayTimestamp) {
209+
broadcasterSixtyDaySum = broadcasterSixtyDaySum.plus(
210+
broadcasterDay.volumeETH
211+
);
212+
}
213+
if (broadcasterDay.date >= ninetyDayTimestamp) {
214+
broadcasterNinetyDaySum = broadcasterNinetyDaySum.plus(
215+
broadcasterDay.volumeETH
216+
);
217+
}
218+
}
219+
}
220+
221+
broadcaster.thirtyDayVolumeETH = broadcasterThirtyDaySum;
222+
broadcaster.sixtyDayVolumeETH = broadcasterSixtyDaySum;
223+
broadcaster.ninetyDayVolumeETH = broadcasterNinetyDaySum;
224+
broadcaster.save();
225+
226+
if (broadcaster.lastActiveDay >= ninetyDayTimestamp) {
227+
activeBroadcasters.push(broadcaster.id);
228+
}
229+
}
230+
}
231+
}
232+
233+
protocol.activeBroadcasters = activeBroadcasters;
234+
180235
let lptPriceEth = getLptPriceEth();
181236

182237
protocol.lptPriceEth = lptPriceEth;

src/mappings/ticketBroker.ts

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Address, BigInt, dataSource, log } from "@graphprotocol/graph-ts";
22
import {
33
convertToDecimal,
44
createOrLoadBroadcaster,
5+
createOrLoadBroadcasterDay,
56
createOrLoadDay,
67
createOrLoadProtocol,
78
createOrLoadRound,
@@ -32,13 +33,15 @@ import {
3233

3334
export function winningTicketRedeemed(event: WinningTicketRedeemed): void {
3435
let round = createOrLoadRound(getBlockNum());
35-
let day = createOrLoadDay(event.block.timestamp.toI32());
36+
let timestamp = event.block.timestamp.toI32();
37+
let day = createOrLoadDay(timestamp);
3638
let winningTicketRedeemedEvent = new WinningTicketRedeemedEvent(
3739
makeEventId(event.transaction.hash, event.logIndex)
3840
);
3941
let protocol = createOrLoadProtocol();
4042
let faceValue = convertToDecimal(event.params.faceValue);
4143
let ethPrice = getEthPriceUsd();
44+
let faceValueUSD = faceValue.times(ethPrice);
4245
let poolId = makePoolId(event.params.recipient.toHex(), round.id);
4346
let pool = Pool.load(poolId);
4447

@@ -50,7 +53,7 @@ export function winningTicketRedeemed(event: WinningTicketRedeemed): void {
5053
winningTicketRedeemedEvent.sender = event.params.sender.toHex();
5154
winningTicketRedeemedEvent.recipient = event.params.recipient.toHex();
5255
winningTicketRedeemedEvent.faceValue = faceValue;
53-
winningTicketRedeemedEvent.faceValueUSD = faceValue.times(ethPrice);
56+
winningTicketRedeemedEvent.faceValueUSD = faceValueUSD;
5457
winningTicketRedeemedEvent.winProb = event.params.winProb;
5558
winningTicketRedeemedEvent.senderNonce = event.params.senderNonce;
5659
winningTicketRedeemedEvent.recipientRand = event.params.recipientRand;
@@ -76,6 +79,23 @@ export function winningTicketRedeemed(event: WinningTicketRedeemed): void {
7679
} else {
7780
broadcaster.deposit = broadcaster.deposit.minus(faceValue);
7881
}
82+
83+
broadcaster.totalVolumeETH = broadcaster.totalVolumeETH.plus(faceValue);
84+
broadcaster.totalVolumeUSD = broadcaster.totalVolumeUSD.plus(faceValueUSD);
85+
86+
let broadcasterDay = createOrLoadBroadcasterDay(
87+
timestamp,
88+
event.params.sender.toHex()
89+
);
90+
broadcaster.lastActiveDay = broadcasterDay.date;
91+
broadcasterDay.volumeETH = broadcasterDay.volumeETH.plus(faceValue);
92+
broadcasterDay.volumeUSD = broadcasterDay.volumeUSD.plus(faceValueUSD);
93+
broadcasterDay.save();
94+
let broadcasterDays = broadcaster.broadcasterDays;
95+
if (!broadcasterDays.includes(broadcasterDay.id)) {
96+
broadcasterDays.unshift(broadcasterDay.id);
97+
broadcaster.broadcasterDays = broadcasterDays;
98+
}
7999
broadcaster.save();
80100

81101
// Update transcoder's fee volume
@@ -84,15 +104,11 @@ export function winningTicketRedeemed(event: WinningTicketRedeemed): void {
84104
event.block.timestamp.toI32()
85105
);
86106
transcoder.totalVolumeETH = transcoder.totalVolumeETH.plus(faceValue);
87-
transcoder.totalVolumeUSD = transcoder.totalVolumeUSD.plus(
88-
faceValue.times(ethPrice)
89-
);
107+
transcoder.totalVolumeUSD = transcoder.totalVolumeUSD.plus(faceValueUSD);
90108

91109
// Update total protocol fee volume
92110
protocol.totalVolumeETH = protocol.totalVolumeETH.plus(faceValue);
93-
protocol.totalVolumeUSD = protocol.totalVolumeUSD.plus(
94-
faceValue.times(ethPrice)
95-
);
111+
protocol.totalVolumeUSD = protocol.totalVolumeUSD.plus(faceValueUSD);
96112

97113
protocol.winningTicketCount = protocol.winningTicketCount + 1;
98114
protocol.save();
@@ -107,17 +123,15 @@ export function winningTicketRedeemed(event: WinningTicketRedeemed): void {
107123
day.totalActiveStake = protocol.totalActiveStake;
108124
day.participationRate = protocol.participationRate;
109125
day.volumeETH = day.volumeETH.plus(faceValue);
110-
day.volumeUSD = day.volumeUSD.plus(faceValue.times(ethPrice));
126+
day.volumeUSD = day.volumeUSD.plus(faceValueUSD);
111127
day.save();
112128

113129
let transcoderDay = createOrLoadTranscoderDay(
114-
event.block.timestamp.toI32(),
130+
timestamp,
115131
event.params.recipient.toHex()
116132
);
117133
transcoderDay.volumeETH = transcoderDay.volumeETH.plus(faceValue);
118-
transcoderDay.volumeUSD = transcoderDay.volumeUSD.plus(
119-
faceValue.times(ethPrice)
120-
);
134+
transcoderDay.volumeUSD = transcoderDay.volumeUSD.plus(faceValueUSD);
121135
transcoderDay.save();
122136

123137
// Manually manage the array of transcoder days (add newest to the beginning of the list)
@@ -130,7 +144,7 @@ export function winningTicketRedeemed(event: WinningTicketRedeemed): void {
130144

131145
// Update fee volume for this round
132146
round.volumeETH = round.volumeETH.plus(faceValue);
133-
round.volumeUSD = round.volumeUSD.plus(faceValue.times(ethPrice));
147+
round.volumeUSD = round.volumeUSD.plus(faceValueUSD);
134148
round.save();
135149
}
136150

utils/helpers.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
import { RoundsManager } from "../src/types/RoundsManager/RoundsManager";
1010
import {
1111
Broadcaster,
12+
BroadcasterDay,
1213
Day,
1314
Delegator,
1415
LivepeerAccount,
@@ -158,6 +159,7 @@ export function createOrLoadProtocol(): Protocol {
158159
protocol.winningTicketCount = 0;
159160
protocol.roundCount = 0;
160161
protocol.lptPriceEth = ZERO_BD;
162+
protocol.activeBroadcasters = [];
161163

162164
const network = dataSource.network();
163165
// 3520 is the count of total delegators from the mainnet subgraph (in the final round)
@@ -169,6 +171,12 @@ export function createOrLoadProtocol(): Protocol {
169171
protocol.pendingDeactivation = [];
170172
protocol.save();
171173
}
174+
175+
// Ensure backwards compatibility
176+
if (protocol.activeBroadcasters == null) {
177+
protocol.activeBroadcasters = [];
178+
protocol.save();
179+
}
172180
return protocol;
173181
}
174182

@@ -179,10 +187,25 @@ export function createOrLoadBroadcaster(id: string): Broadcaster {
179187
broadcaster = new Broadcaster(id);
180188
broadcaster.deposit = ZERO_BD;
181189
broadcaster.reserve = ZERO_BD;
190+
broadcaster.totalVolumeETH = ZERO_BD;
191+
broadcaster.totalVolumeUSD = ZERO_BD;
192+
broadcaster.thirtyDayVolumeETH = ZERO_BD;
193+
broadcaster.sixtyDayVolumeETH = ZERO_BD;
194+
broadcaster.ninetyDayVolumeETH = ZERO_BD;
195+
broadcaster.lastActiveDay = 0;
196+
broadcaster.broadcasterDays = [];
182197

183198
broadcaster.save();
184199
}
185200

201+
let protocol = createOrLoadProtocol();
202+
let activeBroadcasters = protocol.activeBroadcasters;
203+
if (!activeBroadcasters.includes(id)) {
204+
activeBroadcasters.push(id);
205+
protocol.activeBroadcasters = activeBroadcasters;
206+
protocol.save();
207+
}
208+
186209
return broadcaster;
187210
}
188211

@@ -316,6 +339,29 @@ export function createOrLoadTranscoderDay(
316339
return transcoderDay;
317340
}
318341

342+
export function createOrLoadBroadcasterDay(
343+
timestamp: i32,
344+
broadcasterAddress: string
345+
): BroadcasterDay {
346+
let dayID = timestamp / 86400;
347+
let dayStartTimestamp = dayID * 86400;
348+
let broadcasterDayID = broadcasterAddress
349+
.concat("-")
350+
.concat(BigInt.fromI32(dayID).toString());
351+
let broadcasterDay = BroadcasterDay.load(broadcasterDayID);
352+
353+
if (broadcasterDay == null) {
354+
broadcasterDay = new BroadcasterDay(broadcasterDayID);
355+
broadcasterDay.date = dayStartTimestamp;
356+
broadcasterDay.broadcaster = broadcasterAddress;
357+
broadcasterDay.volumeUSD = ZERO_BD;
358+
broadcasterDay.volumeETH = ZERO_BD;
359+
360+
broadcasterDay.save();
361+
}
362+
return broadcasterDay;
363+
}
364+
319365
export function createOrLoadRound(blockNumber: BigInt): Round {
320366
let protocol = createOrLoadProtocol();
321367
let roundsSinceLastUpdate = blockNumber

0 commit comments

Comments
 (0)