@@ -217,6 +217,38 @@ const getDisputesWithContributionsNotYetWithdrawn = async (): Promise<Dispute[]>
217
217
return getUniqueDisputes ( disputes ) ;
218
218
} ;
219
219
220
+ const getUnstakedJurors = async ( disputeId : string ) : Promise < string [ ] > => {
221
+ const { gql, request } = await import ( "graphql-request" ) ; // workaround for ESM import
222
+ const query = gql `
223
+ query UnstakedJurors($disputeId: String!) {
224
+ dispute(id: $disputeId) {
225
+ currentRound {
226
+ drawnJurors(where: { juror_: { totalStake: 0 } }) {
227
+ juror {
228
+ userAddress
229
+ }
230
+ }
231
+ }
232
+ }
233
+ }
234
+ ` ;
235
+ type UnstakedJurors = {
236
+ dispute : {
237
+ currentRound : {
238
+ drawnJurors : { juror : { userAddress : string } } [ ] ;
239
+ } ;
240
+ } ;
241
+ } ;
242
+ const { dispute } = await request < UnstakedJurors > ( SUBGRAPH_URL , query , { disputeId } ) ;
243
+ if ( ! dispute || ! dispute . currentRound ) {
244
+ return [ ] ;
245
+ }
246
+ const uniqueAddresses = [
247
+ ...new Set ( dispute . currentRound . drawnJurors . map ( ( drawnJuror ) => drawnJuror . juror . userAddress ) ) ,
248
+ ] ;
249
+ return uniqueAddresses ;
250
+ } ;
251
+
220
252
const handleError = ( e : any ) => {
221
253
logger . error ( e , "Failure" ) ;
222
254
} ;
@@ -386,6 +418,29 @@ const executeRuling = async (dispute: { id: string }) => {
386
418
return success ;
387
419
} ;
388
420
421
+ const withdrawLeftoverPNK = async ( juror : string ) => {
422
+ const { sortition } = await getContracts ( ) ;
423
+ let success = false ;
424
+ try {
425
+ await sortition . withdrawLeftoverPNK . staticCall ( juror ) ;
426
+ } catch ( e ) {
427
+ const error = e as CustomError ;
428
+ const errorDescription = sortition . interface . parseError ( error . data ) ?. signature ;
429
+ logger . info ( `WithdrawLeftoverPNK: failed for juror ${ juror } because of ${ errorDescription } , skipping` ) ;
430
+ return success ;
431
+ }
432
+ try {
433
+ const gas = ( ( await sortition . withdrawLeftoverPNK . estimateGas ( juror ) ) * 150n ) / 100n ; // 50% extra gas
434
+ const tx = await ( await sortition . withdrawLeftoverPNK ( juror , { gasLimit : gas } ) ) . wait ( ) ;
435
+ logger . info ( `WithdrawLeftoverPNK txID: ${ tx ?. hash } ` ) ;
436
+ } catch ( e ) {
437
+ handleError ( e ) ;
438
+ } finally {
439
+ success = true ;
440
+ }
441
+ return success ;
442
+ } ;
443
+
389
444
const withdrawAppealContribution = async (
390
445
coreDisputeId : string ,
391
446
coreRoundId : string ,
@@ -726,7 +781,7 @@ async function main() {
726
781
do {
727
782
const executeIterations = Math . min ( MAX_EXECUTE_ITERATIONS , numberOfMissingRepartitions ) ;
728
783
if ( executeIterations === 0 ) {
729
- continue ;
784
+ break ;
730
785
}
731
786
logger . info (
732
787
`Executing ${ executeIterations } out of ${ numberOfMissingRepartitions } repartitions needed for dispute #${ dispute . id } `
@@ -739,6 +794,19 @@ async function main() {
739
794
await delay ( ITERATIONS_COOLDOWN_PERIOD ) ; // To avoid spiking the gas price
740
795
} while ( numberOfMissingRepartitions != 0 ) ;
741
796
797
+ // ----------------------------------------------- //
798
+ // WITHDRAW LEFTOVER PNK //
799
+ // ----------------------------------------------- //
800
+ const unstakedJurors = await getUnstakedJurors ( dispute . id ) ;
801
+ logger . info ( `Unstaked jurors: ${ unstakedJurors . map ( ( juror ) => juror ) } ` ) ;
802
+ for ( const juror of unstakedJurors ) {
803
+ const leftoverPNK = await sortition . getJurorLeftoverPNK ( juror ) ;
804
+ if ( leftoverPNK > 0n ) {
805
+ logger . info ( `Leftover PNK for juror ${ juror } : ${ leftoverPNK } , withdrawing...` ) ;
806
+ await withdrawLeftoverPNK ( juror ) ;
807
+ }
808
+ }
809
+
742
810
// ----------------------------------------------- //
743
811
// RULING EXECUTION //
744
812
// ----------------------------------------------- //
0 commit comments