1111
1212import { ethers } from 'ethers'
1313import { SETTLEMENT_FEE , Synapse } from '../packages/synapse-sdk/src/index.ts'
14+ import { getCurrentEpoch } from '../packages/synapse-sdk/src/utils/index.ts'
1415import { WarmStorageService } from '../packages/synapse-sdk/src/warm-storage/index.ts'
1516
17+ // ANSI color codes
18+ const RESET = '\x1b[0m'
19+ const BOLD = '\x1b[1m'
20+ const DIM = '\x1b[2m'
21+ const GREEN = '\x1b[32m'
22+ const YELLOW = '\x1b[33m'
23+ const CYAN = '\x1b[36m'
24+ const RED = '\x1b[31m'
25+
26+ // Configuration for batch settlement
27+ // Maximum tested batch size to avoid gas limit in validator's epoch loop
28+ const DAYS = 6n // Number of days per batch (7 days currently exceeds block gas limit)
29+ const EPOCHS_PER_DAY = 2880n
30+ const BATCH_SIZE = EPOCHS_PER_DAY * DAYS
31+
32+ /**
33+ * Execute a settlement for a rail, optionally to a specific epoch
34+ * @returns {Object|null } Settlement result with preview, tx, receipt, or null if nothing to settle
35+ */
36+ async function executeSettlement ( synapse , rail , targetEpoch = null , batchNumber = null ) {
37+ const indent = batchNumber ? ' ' : ' '
38+ const batchLabel = batchNumber ? ` (Batch ${ batchNumber } )` : ''
39+
40+ // Get settlement preview
41+ const preview = targetEpoch
42+ ? await synapse . payments . getSettlementAmounts ( rail . id , targetEpoch )
43+ : await synapse . payments . getSettlementAmounts ( rail . id )
44+
45+ // Log settlement amounts
46+ if ( batchNumber ) {
47+ console . log ( `${ indent } Amount: ${ ethers . formatUnits ( preview . totalSettledAmount , 18 ) } USDFC` )
48+ } else {
49+ console . log ( `${ indent } Total to settle: ${ ethers . formatUnits ( preview . totalSettledAmount , 18 ) } USDFC` )
50+ console . log ( `${ indent } Payee receives: ${ ethers . formatUnits ( preview . totalNetPayeeAmount , 18 ) } USDFC` )
51+ console . log ( `${ indent } Commission: ${ ethers . formatUnits ( preview . totalOperatorCommission , 18 ) } USDFC` )
52+ }
53+
54+ // Nothing to settle
55+ if ( preview . totalSettledAmount === 0n ) {
56+ console . log ( `${ indent } ${ DIM } Nothing to settle${ RESET } ` )
57+ console . log ( '' )
58+ return null
59+ }
60+
61+ // Execute settlement
62+ console . log ( `${ indent } Settling...` )
63+ const tx = targetEpoch ? await synapse . payments . settle ( rail . id , targetEpoch ) : await synapse . payments . settle ( rail . id )
64+ console . log ( `${ indent } Tx: ${ tx . hash } ` )
65+
66+ // Wait for confirmation
67+ const receipt = await tx . wait ( )
68+ console . log ( `${ indent } ${ GREEN } Confirmed in block ${ receipt . blockNumber } ${ RESET } ` )
69+ console . log ( '' )
70+
71+ return {
72+ preview,
73+ tx,
74+ receipt,
75+ type : `${ rail . type } ${ batchLabel } ` ,
76+ }
77+ }
78+
1679async function main ( ) {
1780 // Parse arguments
1881 const dataSetId = process . argv [ 2 ]
@@ -30,8 +93,8 @@ async function main() {
3093 process . exit ( 1 )
3194 }
3295
33- console . log ( '🔗 Connecting to:' , rpcUrl )
34- console . log ( '📊 Data Set ID:' , dataSetId )
96+ console . log ( ` ${ CYAN } Connecting to:${ RESET } ${ rpcUrl } ` )
97+ console . log ( ` ${ CYAN } Data Set ID:${ RESET } ${ dataSetId } ` )
3598 console . log ( '' )
3699
37100 // Initialize SDK at the top level so we can access it later
@@ -48,21 +111,21 @@ async function main() {
48111 const warmStorageAddress = await synapse . getWarmStorageAddress ( )
49112 const warmStorage = await WarmStorageService . create ( synapse . getProvider ( ) , warmStorageAddress )
50113
51- console . log ( '📋 Fetching data set information...' )
114+ console . log ( 'Fetching data set information...' )
52115
53116 // Get data set info to find rail IDs
54117 const dataSet = await warmStorage . getDataSet ( Number ( dataSetId ) )
55118
56119 if ( ! dataSet ) {
57- console . error ( `❌ Data set ${ dataSetId } not found` )
120+ console . error ( `${ RED } Error: Data set ${ dataSetId } not found${ RESET } ` )
58121 process . exit ( 1 )
59122 }
60- console . log ( '✅ Data set found:' )
61- console . log ( ` Client: ${ dataSet . payer } ` )
62- console . log ( ` Provider: ${ dataSet . serviceProvider } ` )
63- console . log ( ` PDP Rail ID: ${ dataSet . pdpRailId } ` )
64- console . log ( ` CDN Rail ID: ${ dataSet . cdnRailId } ` )
65- console . log ( ` Cache Miss Rail ID : ${ dataSet . cacheMissRailId } ` )
123+ console . log ( ` ${ GREEN } Data set found${ RESET } ` )
124+ console . log ( ` Client: ${ dataSet . payer } ` )
125+ console . log ( ` Provider: ${ dataSet . serviceProvider } ` )
126+ console . log ( ` PDP Rail ID: ${ dataSet . pdpRailId } ` )
127+ console . log ( ` CDN Rail ID: ${ dataSet . cdnRailId } ` )
128+ console . log ( ` Cache Miss Rail: ${ dataSet . cacheMissRailId } ` )
66129 console . log ( '' )
67130
68131 // Collect all rail IDs to settle
@@ -79,94 +142,122 @@ async function main() {
79142 }
80143
81144 if ( railsToSettle . length === 0 ) {
82- console . log ( '⚠️ No rails found for this data set' )
145+ console . log ( ` ${ YELLOW } No rails found for this data set${ RESET } ` )
83146 process . exit ( 0 )
84147 }
85148
86- console . log ( `💰 Checking settlement amounts for ${ railsToSettle . length } rail(s)...` )
87-
88- // Display settlement fee
89- console . log ( `📍 Settlement fee per settlement: ${ ethers . formatEther ( SETTLEMENT_FEE ) } FIL` )
149+ console . log ( `Checking settlement amounts for ${ railsToSettle . length } rail(s)...` )
150+ console . log ( `${ DIM } Settlement fee: ${ ethers . formatEther ( SETTLEMENT_FEE ) } FIL per transaction${ RESET } ` )
90151 console . log ( '' )
91152
92153 let totalSettled = 0n
93154 let totalPayeeAmount = 0n
94155 let totalCommission = 0n
95156 const transactions = [ ]
96157
158+ // Get current epoch
159+ const currentEpoch = await getCurrentEpoch ( synapse . getProvider ( ) )
160+ console . log ( `Current epoch: ${ currentEpoch } ` )
161+ console . log ( '' )
162+
97163 // Process each rail
98164 for ( const rail of railsToSettle ) {
99- console . log ( `📊 ${ rail . type } Rail (ID: ${ rail . id } ): ` )
165+ console . log ( `${ BOLD } ${ rail . type } Rail (ID: ${ rail . id } )${ RESET } ` )
100166
101167 try {
102- // Preview settlement amounts
103- const preview = await synapse . payments . getSettlementAmounts ( rail . id )
104-
105- console . log ( ` Total to settle: ${ ethers . formatUnits ( preview . totalSettledAmount , 18 ) } USDFC` )
106- console . log ( ` Payee receives: ${ ethers . formatUnits ( preview . totalNetPayeeAmount , 18 ) } USDFC` )
107- console . log ( ` Commission: ${ ethers . formatUnits ( preview . totalOperatorCommission , 18 ) } USDFC` )
168+ // Get rail info to check settled epoch
169+ const railInfo = await synapse . payments . getRail ( rail . id )
170+ const settledUpTo = railInfo . settledUpTo
171+ const epochGap = currentEpoch - settledUpTo
108172
109- if ( preview . totalSettledAmount === 0n ) {
110- console . log ( ` ⏭️ Nothing to settle, skipping...` )
111- console . log ( '' )
112- continue
113- }
173+ console . log ( ` Settled up to: ${ settledUpTo } ` )
174+ console . log ( ` Epoch gap: ${ epochGap } epochs` )
114175
115- // Settle the rail
116- console . log ( ` 🔄 Settling rail...` )
117- const tx = await synapse . payments . settle ( rail . id )
118- console . log ( ` 📝 Transaction: ${ tx . hash } ` )
176+ // Determine if we need to batch
177+ const needsBatching = epochGap > BATCH_SIZE
119178
120- // Wait for confirmation
121- console . log ( ` ⏳ Waiting for confirmation...` )
122- const receipt = await tx . wait ( )
123- console . log ( ` ✅ Confirmed in block ${ receipt . blockNumber } ` )
124- console . log ( '' )
179+ if ( needsBatching ) {
180+ console . log ( ` ${ YELLOW } Large gap - settling in batches of ${ BATCH_SIZE } epochs (${ DAYS } days)${ RESET } ` )
181+ console . log ( '' )
125182
126- // Track totals
127- totalSettled += preview . totalSettledAmount
128- totalPayeeAmount += preview . totalNetPayeeAmount
129- totalCommission += preview . totalOperatorCommission
130- transactions . push ( {
131- type : rail . type ,
132- railId : rail . id ,
133- txHash : tx . hash ,
134- amount : preview . totalSettledAmount ,
135- } )
183+ // Settle in batches
184+ let batchStart = settledUpTo
185+ let batchNumber = 1
186+
187+ while ( batchStart < currentEpoch ) {
188+ const batchEnd = batchStart + BATCH_SIZE
189+ const targetEpoch = batchEnd > currentEpoch ? currentEpoch : batchEnd
190+
191+ console . log ( ` ${ CYAN } Batch ${ batchNumber } :${ RESET } Epochs ${ batchStart } → ${ targetEpoch } ` )
192+
193+ const result = await executeSettlement ( synapse , rail , targetEpoch , batchNumber )
194+
195+ if ( result ) {
196+ // Track totals
197+ totalSettled += result . preview . totalSettledAmount
198+ totalPayeeAmount += result . preview . totalNetPayeeAmount
199+ totalCommission += result . preview . totalOperatorCommission
200+ transactions . push ( {
201+ type : result . type ,
202+ railId : rail . id ,
203+ txHash : result . tx . hash ,
204+ amount : result . preview . totalSettledAmount ,
205+ } )
206+ }
207+
208+ batchStart = targetEpoch
209+ batchNumber ++
210+ }
211+ } else {
212+ // Normal single settlement
213+ const result = await executeSettlement ( synapse , rail )
214+
215+ if ( result ) {
216+ // Track totals
217+ totalSettled += result . preview . totalSettledAmount
218+ totalPayeeAmount += result . preview . totalNetPayeeAmount
219+ totalCommission += result . preview . totalOperatorCommission
220+ transactions . push ( {
221+ type : result . type ,
222+ railId : rail . id ,
223+ txHash : result . tx . hash ,
224+ amount : result . preview . totalSettledAmount ,
225+ } )
226+ }
227+ }
136228 } catch ( error ) {
137- console . error ( ` ❌ Error settling ${ rail . type } rail:` , error . message )
229+ console . error ( ` ${ RED } Error settling ${ rail . type } rail: ${ error . message } ${ RESET } ` )
138230
139231 // Check if it's the InsufficientNativeTokenForBurn error
140232 if ( error . message . includes ( 'InsufficientNativeTokenForBurn' ) ) {
141- console . log ( ` ℹ️ This error means your wallet doesn't have enough FIL for the network fee` )
142- console . log ( ` ℹ️ Settlement requires ${ ethers . formatEther ( networkFee ) } FIL as a network fee` )
143- console . log ( ` ℹ️ Please ensure your wallet has sufficient FIL balance` )
233+ console . log ( ` ${ YELLOW } Insufficient FIL for network fee${ RESET } ` )
234+ console . log ( ` Required: ${ ethers . formatEther ( SETTLEMENT_FEE ) } FIL` )
144235 }
145236
146237 console . log ( '' )
147238 }
148239 }
149240
150241 // Summary
151- console . log ( '═══════════════════════════════════════════ ' )
152- console . log ( '📊 SETTLEMENT SUMMARY' )
153- console . log ( '═══════════════════════════════════════════' )
242+ console . log ( '' )
243+ console . log ( ` ${ BOLD } Settlement Summary ${ RESET } ` )
244+ console . log ( '─' . repeat ( 60 ) )
154245 console . log ( `Total Settled: ${ ethers . formatUnits ( totalSettled , 18 ) } USDFC` )
155246 console . log ( `Payee Received: ${ ethers . formatUnits ( totalPayeeAmount , 18 ) } USDFC` )
156247 console . log ( `Total Commission: ${ ethers . formatUnits ( totalCommission , 18 ) } USDFC` )
157248 console . log ( '' )
158249
159250 if ( transactions . length > 0 ) {
160- console . log ( '📝 Transactions:' )
251+ console . log ( 'Transactions:' )
161252 for ( const tx of transactions ) {
162- console . log ( ` ${ tx . type } : ${ tx . txHash } ` )
253+ console . log ( ` ${ tx . type } : ${ tx . txHash } ` )
163254 }
164255 }
165256
166257 console . log ( '' )
167- console . log ( '✅ Settlement complete!' )
258+ console . log ( ` ${ GREEN } Settlement complete${ RESET } ` )
168259 } catch ( error ) {
169- console . error ( '❌ Error:' , error . message )
260+ console . error ( ` ${ RED } Error: ${ error . message } ${ RESET } ` )
170261 hasError = true
171262 } finally {
172263 // Always close the WebSocket connection if it exists
@@ -184,6 +275,6 @@ async function main() {
184275
185276// Run the script
186277main ( ) . catch ( ( error ) => {
187- console . error ( '❌ Fatal error:' , error . message )
278+ console . error ( ` ${ RED } Fatal error: ${ error . message } ${ RESET } ` )
188279 process . exit ( 1 )
189280} )
0 commit comments