@@ -15,6 +15,7 @@ import {
1515 EvidenceGroup ,
1616 Evidence ,
1717 LArbitrator ,
18+ Submitter
1819} from '../generated/schema' ;
1920import {
2021 AppealPossible ,
@@ -104,6 +105,38 @@ CONTRACT_STATUS_NAMES.set(REGISTERED_CODE, 'Registered');
104105CONTRACT_STATUS_NAMES . set ( REGISTRATION_REQUESTED_CODE , 'RegistrationRequested' ) ;
105106CONTRACT_STATUS_NAMES . set ( CLEARING_REQUESTED_CODE , 'ClearingRequested' ) ;
106107
108+ /**
109+ * Safely decrement a counter without going below zero.
110+ * Used whenever a registry/item/request counter is reduced.
111+ */
112+ function safeDecrement ( value : BigInt ) : BigInt {
113+ if ( value . gt ( BigInt . fromI32 ( 0 ) ) ) {
114+ return value . minus ( BigInt . fromI32 ( 1 ) ) ;
115+ }
116+ return BigInt . fromI32 ( 0 ) ;
117+ }
118+
119+ function loadOrCreateSubmitter ( addr : Address ) : Submitter {
120+ let id = addr . toHexString ( ) . toLowerCase ( ) ;
121+ let s = Submitter . load ( id ) ;
122+ if ( s == null ) {
123+ s = new Submitter ( id ) ;
124+ s . totalSubmissions = BigInt . fromI32 ( 0 ) ;
125+ s . ongoingSubmissions = BigInt . fromI32 ( 0 ) ;
126+ s . pastSubmissions = BigInt . fromI32 ( 0 ) ;
127+ s . save ( ) ;
128+ }
129+ return s as Submitter ;
130+ }
131+
132+ function moveRequestToPast ( requester : Address ) : void {
133+ let s = loadOrCreateSubmitter ( requester ) ;
134+
135+ s . ongoingSubmissions = safeDecrement ( s . ongoingSubmissions ) ;
136+ s . pastSubmissions = s . pastSubmissions . plus ( BigInt . fromI32 ( 1 ) ) ;
137+ s . save ( ) ;
138+ }
139+
107140function getExtendedStatus ( disputed : boolean , status : string ) : number {
108141 if ( disputed ) {
109142 if ( status == CONTRACT_STATUS_NAMES . get ( REGISTRATION_REQUESTED_CODE ) )
@@ -175,23 +208,21 @@ function updateCounters(
175208 }
176209
177210 if ( previousStatus == ABSENT_CODE ) {
178- registry . numberOfAbsent = registry . numberOfAbsent . minus ( BigInt . fromI32 ( 1 ) ) ;
211+ registry . numberOfAbsent = safeDecrement ( registry . numberOfAbsent ) ;
179212 } else if ( previousStatus == REGISTERED_CODE ) {
180- registry . numberOfRegistered = registry . numberOfRegistered . minus (
181- BigInt . fromI32 ( 1 ) ,
182- ) ;
213+ registry . numberOfRegistered = safeDecrement ( registry . numberOfRegistered ) ;
183214 } else if ( previousStatus == REGISTRATION_REQUESTED_CODE ) {
184215 registry . numberOfRegistrationRequested =
185- registry . numberOfRegistrationRequested . minus ( BigInt . fromI32 ( 1 ) ) ;
216+ safeDecrement ( registry . numberOfRegistrationRequested ) ;
186217 } else if ( previousStatus == CLEARING_REQUESTED_CODE ) {
187218 registry . numberOfClearingRequested =
188- registry . numberOfClearingRequested . minus ( BigInt . fromI32 ( 1 ) ) ;
219+ safeDecrement ( registry . numberOfClearingRequested ) ;
189220 } else if ( previousStatus == CHALLENGED_REGISTRATION_REQUEST_CODE ) {
190221 registry . numberOfChallengedRegistrations =
191- registry . numberOfChallengedRegistrations . minus ( BigInt . fromI32 ( 1 ) ) ;
222+ safeDecrement ( registry . numberOfChallengedRegistrations ) ;
192223 } else if ( previousStatus == CHALLENGED_CLEARING_REQUEST_CODE ) {
193224 registry . numberOfChallengedClearing =
194- registry . numberOfChallengedClearing . minus ( BigInt . fromI32 ( 1 ) ) ;
225+ safeDecrement ( registry . numberOfChallengedClearing ) ;
195226 }
196227
197228 if ( newStatus == ABSENT_CODE ) {
@@ -284,6 +315,7 @@ export function handleRequestSubmitted(event: RequestSubmitted): void {
284315 ] ) ;
285316 return ;
286317 }
318+
287319 // `previousStatus` and `newStatus` are used for accounting.
288320 // Note that if this is the very first request of an item,
289321 // item.status and item.dispute are dirty because they were set by
@@ -300,8 +332,16 @@ export function handleRequestSubmitted(event: RequestSubmitted): void {
300332
301333 let newStatus = getExtendedStatus ( item . disputed , item . status ) ;
302334
303- let requestIndex = item . numberOfRequests . minus ( BigInt . fromI32 ( 1 ) ) ;
304- let requestInfo = tcr . getRequestInfo ( event . params . _itemID , requestIndex ) ;
335+ let requestIndex = safeDecrement ( item . numberOfRequests ) ;
336+ let requestInfoResult = tcr . try_getRequestInfo ( event . params . _itemID , requestIndex ) ;
337+ if ( requestInfoResult . reverted ) {
338+ log . error ( `Failed to get request info for item {} at request index {}` , [
339+ event . params . _itemID . toHexString ( ) ,
340+ requestIndex . toString ( )
341+ ] ) ;
342+ return ;
343+ }
344+ let requestInfo = requestInfoResult . value ;
305345 let requestID = graphItemID + '-' + requestIndex . toString ( ) ;
306346
307347 let request = new LRequest ( requestID ) ;
@@ -359,6 +399,11 @@ export function handleRequestSubmitted(event: RequestSubmitted): void {
359399 updateCounters ( previousStatus , newStatus , event . address ) ;
360400 }
361401
402+ let submitter = loadOrCreateSubmitter ( Address . fromBytes ( request . requester ) ) ;
403+ submitter . totalSubmissions = submitter . totalSubmissions . plus ( BigInt . fromI32 ( 1 ) ) ;
404+ submitter . ongoingSubmissions = submitter . ongoingSubmissions . plus ( BigInt . fromI32 ( 1 ) ) ;
405+ submitter . save ( ) ;
406+
362407 round . save ( ) ;
363408 request . save ( ) ;
364409 item . save ( ) ;
@@ -449,8 +494,16 @@ export function handleRequestChallenged(event: Dispute): void {
449494 item . latestChallenger = event . transaction . from ;
450495 let newStatus = getExtendedStatus ( item . disputed , item . status ) ;
451496
452- let requestIndex = item . numberOfRequests . minus ( BigInt . fromI32 ( 1 ) ) ;
453- let requestInfo = tcr . getRequestInfo ( itemID , requestIndex ) ;
497+ let requestIndex = safeDecrement ( item . numberOfRequests ) ;
498+ let requestInfoResult = tcr . try_getRequestInfo ( itemID , requestIndex ) ;
499+ if ( requestInfoResult . reverted ) {
500+ log . error ( `Failed to get request info for item {} at request index {}` , [
501+ itemID . toHexString ( ) ,
502+ requestIndex . toString ( )
503+ ] ) ;
504+ return ;
505+ }
506+ let requestInfo = requestInfoResult . value ;
454507 let requestID = graphItemID + '-' + requestIndex . toString ( ) ;
455508 let request = LRequest . load ( requestID ) ;
456509 if ( ! request ) {
@@ -495,7 +548,7 @@ export function handleAppealPossible(event: AppealPossible): void {
495548 }
496549
497550 let requestID =
498- item . id + '-' + item . numberOfRequests . minus ( BigInt . fromI32 ( 1 ) ) . toString ( ) ;
551+ item . id + '-' + safeDecrement ( item . numberOfRequests ) . toString ( ) ;
499552 let request = LRequest . load ( requestID ) ;
500553 if ( ! request ) {
501554 log . error ( `Appeal Possible LRequest {} not found. tx {}` , [
@@ -506,9 +559,7 @@ export function handleAppealPossible(event: AppealPossible): void {
506559 }
507560
508561 let roundID =
509- request . id +
510- '-' +
511- request . numberOfRounds . minus ( BigInt . fromI32 ( 1 ) ) . toString ( ) ;
562+ request . id + '-' + safeDecrement ( request . numberOfRounds ) . toString ( ) ;
512563 let round = LRound . load ( roundID ) ;
513564 if ( ! round ) {
514565 log . error ( `Appeal Possible LRound {} not found. tx {}` , [
@@ -556,7 +607,7 @@ export function handleAppealDecision(event: AppealDecision): void {
556607 }
557608
558609 let requestID =
559- item . id + '-' + item . numberOfRequests . minus ( BigInt . fromI32 ( 1 ) ) . toString ( ) ;
610+ item . id + '-' + safeDecrement ( item . numberOfRequests ) . toString ( ) ;
560611 let request = LRequest . load ( requestID ) ;
561612 if ( ! request ) {
562613 log . error ( `Appeal Decision LRequest {} not found. tx {}` , [
@@ -567,9 +618,7 @@ export function handleAppealDecision(event: AppealDecision): void {
567618 }
568619
569620 let roundID =
570- request . id +
571- '-' +
572- request . numberOfRounds . minus ( BigInt . fromI32 ( 1 ) ) . toString ( ) ;
621+ request . id + '-' + safeDecrement ( request . numberOfRounds ) . toString ( ) ;
573622 let round = LRound . load ( roundID ) ;
574623 if ( ! round ) {
575624 log . error ( `Appeal Decision LRound {} not found. tx {}` , [
@@ -637,8 +686,16 @@ export function handleStatusUpdated(event: ItemStatusChange): void {
637686
638687 item . latestRequestResolutionTime = event . block . timestamp ;
639688
640- let requestIndex = item . numberOfRequests . minus ( BigInt . fromI32 ( 1 ) ) ;
641- let requestInfo = tcr . getRequestInfo ( event . params . _itemID , requestIndex ) ;
689+ let requestIndex = safeDecrement ( item . numberOfRequests ) ;
690+ let requestInfoResult = tcr . try_getRequestInfo ( event . params . _itemID , requestIndex ) ;
691+ if ( requestInfoResult . reverted ) {
692+ log . error ( `Failed to get request info for item {} at request index {}` , [
693+ event . params . _itemID . toHexString ( ) ,
694+ requestIndex . toString ( )
695+ ] ) ;
696+ return ;
697+ }
698+ let requestInfo = requestInfoResult . value ;
642699
643700 let requestID = graphItemID + '-' + requestIndex . toString ( ) ;
644701 let request = LRequest . load ( requestID ) ;
@@ -653,6 +710,11 @@ export function handleStatusUpdated(event: ItemStatusChange): void {
653710 // requestInfo.value6 is request.ruling.
654711 request . disputeOutcome = getFinalRuling ( requestInfo . value6 ) ;
655712
713+ if ( item . status == REGISTERED || item . status == ABSENT ) {
714+ // request just moved to a “finished” final state
715+ moveRequestToPast ( Address . fromBytes ( request . requester ) ) ;
716+ }
717+
656718 // Iterate over every contribution and mark it as withdrawable if it is.
657719 // Start from the second round as the first is automatically withdrawn
658720 // when the request resolves.
@@ -695,7 +757,7 @@ export function handleStatusUpdated(event: ItemStatusChange): void {
695757 // the contributors get to withdraw.
696758 if ( contribution . side == BigInt . fromI32 ( REQUESTER_CODE ) ) {
697759 contribution . withdrawable = true ;
698- } else if ( i . equals ( request . numberOfRounds . minus ( BigInt . fromI32 ( 1 ) ) ) ) {
760+ } else if ( i . equals ( safeDecrement ( request . numberOfRounds ) ) ) {
699761 // Contribution was made to the challenger (loser) and this
700762 // is the last round.
701763 contribution . withdrawable = true ;
@@ -709,7 +771,7 @@ export function handleStatusUpdated(event: ItemStatusChange): void {
709771 // the contributors get to withdraw.
710772 if ( contribution . side == BigInt . fromI32 ( CHALLENGER_CODE ) ) {
711773 contribution . withdrawable = true ;
712- } else if ( i . equals ( request . numberOfRounds . minus ( BigInt . fromI32 ( 1 ) ) ) ) {
774+ } else if ( i . equals ( safeDecrement ( request . numberOfRounds ) ) ) {
713775 // Contribution was made to the requester (loser) and this
714776 // is the last round.
715777 contribution . withdrawable = true ;
@@ -892,7 +954,7 @@ export function handleRuling(event: Ruling): void {
892954 }
893955
894956 let requestID =
895- item . id + '-' + item . numberOfRequests . minus ( BigInt . fromI32 ( 1 ) ) . toString ( ) ;
957+ item . id + '-' + safeDecrement ( item . numberOfRequests ) . toString ( ) ;
896958 let request = LRequest . load ( requestID ) ;
897959 if ( ! request ) {
898960 log . error ( `Ruling LRequest {} not found. tx {}` , [
0 commit comments