@@ -217,6 +217,38 @@ const getDisputesWithContributionsNotYetWithdrawn = async (): Promise<Dispute[]>
217217 return getUniqueDisputes ( disputes ) ;
218218} ;
219219
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+
220252const handleError = ( e : any ) => {
221253 logger . error ( e , "Failure" ) ;
222254} ;
@@ -386,6 +418,29 @@ const executeRuling = async (dispute: { id: string }) => {
386418 return success ;
387419} ;
388420
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+
389444const withdrawAppealContribution = async (
390445 coreDisputeId : string ,
391446 coreRoundId : string ,
@@ -726,7 +781,7 @@ async function main() {
726781 do {
727782 const executeIterations = Math . min ( MAX_EXECUTE_ITERATIONS , numberOfMissingRepartitions ) ;
728783 if ( executeIterations === 0 ) {
729- continue ;
784+ break ;
730785 }
731786 logger . info (
732787 `Executing ${ executeIterations } out of ${ numberOfMissingRepartitions } repartitions needed for dispute #${ dispute . id } `
@@ -739,6 +794,19 @@ async function main() {
739794 await delay ( ITERATIONS_COOLDOWN_PERIOD ) ; // To avoid spiking the gas price
740795 } while ( numberOfMissingRepartitions != 0 ) ;
741796
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+
742810 // ----------------------------------------------- //
743811 // RULING EXECUTION //
744812 // ----------------------------------------------- //
0 commit comments