@@ -77,6 +77,11 @@ export interface ReallocateTransactionParams {
77
77
proof : BytesLike
78
78
}
79
79
80
+ export interface PreparedTransaction {
81
+ action : Action
82
+ result : PopulateTransactionResult
83
+ }
84
+
80
85
// An Action with resolved Allocation and Unallocation values
81
86
export interface ActionStakeUsageSummary {
82
87
action : Action
@@ -91,11 +96,21 @@ export type PopulateTransactionResult =
91
96
| PopulatedTransaction [ ]
92
97
| ActionFailure
93
98
99
+ // Result when we attempt to execute a batch
100
+ export interface BatchExecutionResult {
101
+ receipt : ContractReceipt | 'paused' | 'unauthorized'
102
+ prepared : PreparedTransaction [ ] // Actions that were prepared and ready to execute
103
+ failed : PreparedTransaction [ ] // Actions that failed during preparation
104
+ }
105
+
94
106
export type TransactionResult =
95
- | ContractReceipt
96
- | 'paused'
97
- | 'unauthorized'
98
- | ActionFailure [ ]
107
+ | BatchExecutionResult // We attempted execution (even if paused/unauthorized)
108
+ | ActionFailure [ ] // All failed during preparation, nothing to execute
109
+
110
+ // Type guard to check if all preparations failed
111
+ const isAllFailures = ( result : TransactionResult ) : result is ActionFailure [ ] => {
112
+ return Array . isArray ( result )
113
+ }
99
114
100
115
export class AllocationManager {
101
116
constructor (
@@ -112,11 +127,48 @@ export class AllocationManager {
112
127
const logger = this . logger . child ( { function : 'executeBatch' } )
113
128
logger . trace ( 'Executing action batch' , { actions } )
114
129
const result = await this . executeTransactions ( actions , onFinishedDeploying )
115
- if ( Array . isArray ( result ) ) {
116
- logger . trace ( 'Execute batch transaction failed' , { actionBatchResult : result } )
117
- return result as ActionFailure [ ]
130
+
131
+ // Handle the case where all preparations failed
132
+ if ( isAllFailures ( result ) ) {
133
+ logger . trace ( 'All transaction preparations failed' , { failures : result } )
134
+ return result
135
+ }
136
+
137
+ // Handle BatchExecutionResult - we have some prepared transactions
138
+ const allResults : AllocationResult [ ] = [ ]
139
+
140
+ // Add failures to results
141
+ for ( const failed of result . failed ) {
142
+ allResults . push ( failed . result as ActionFailure )
143
+ }
144
+
145
+ // Process prepared transactions if we have a valid receipt
146
+ if ( result . receipt !== 'paused' && result . receipt !== 'unauthorized' ) {
147
+ const confirmedResults = await this . confirmTransactions (
148
+ result . receipt ,
149
+ result . prepared . map ( ( p ) => p . action ) ,
150
+ )
151
+ allResults . push ( ...confirmedResults )
152
+ } else {
153
+ // If paused or unauthorized, mark prepared actions as failed
154
+ logger . info ( `Execution skipped: ${ result . receipt } ` , {
155
+ preparedCount : result . prepared . length ,
156
+ failedCount : result . failed . length ,
157
+ } )
158
+
159
+ for ( const prepared of result . prepared ) {
160
+ allResults . push ( {
161
+ actionID : prepared . action . id ,
162
+ failureReason :
163
+ result . receipt === 'paused'
164
+ ? 'Network is paused'
165
+ : 'Not authorized as operator' ,
166
+ protocolNetwork : prepared . action . protocolNetwork ,
167
+ } as ActionFailure )
168
+ }
118
169
}
119
- return await this . confirmTransactions ( result , actions )
170
+
171
+ return allResults
120
172
}
121
173
122
174
private async executeTransactions (
@@ -135,37 +187,72 @@ export class AllocationManager {
135
187
await this . deployBeforeAllocating ( logger , validatedActions )
136
188
await onFinishedDeploying ( validatedActions )
137
189
138
- const populateTransactionsResults = await this . prepareTransactions ( validatedActions )
190
+ const preparedTransactions = await this . prepareTransactions ( validatedActions )
191
+
192
+ // Separate successful and failed preparations
193
+ const failedPreparations = preparedTransactions . filter ( ( prepared ) =>
194
+ isActionFailure ( prepared . result ) ,
195
+ )
196
+ const successfulPreparations = preparedTransactions . filter (
197
+ ( prepared ) => ! isActionFailure ( prepared . result ) ,
198
+ )
139
199
140
- const failedTransactionPreparations = populateTransactionsResults
141
- . filter ( ( result ) => isActionFailure ( result ) )
142
- . map ( ( result ) => result as ActionFailure )
200
+ // Log the preparation results
201
+ logger . info ( 'Transaction preparation complete' , {
202
+ total : preparedTransactions . length ,
203
+ successful : successfulPreparations . length ,
204
+ failed : failedPreparations . length ,
205
+ } )
143
206
144
- if ( failedTransactionPreparations . length > 0 ) {
145
- logger . trace ( 'Failed to prepare transactions' , { failedTransactionPreparations } )
146
- return failedTransactionPreparations
207
+ // If all failed, return early with failures
208
+ if ( successfulPreparations . length === 0 ) {
209
+ logger . warn ( 'All transaction preparations failed' , {
210
+ failures : failedPreparations . map ( ( f ) => ( {
211
+ actionId : f . action . id ,
212
+ reason : ( f . result as ActionFailure ) . failureReason ,
213
+ } ) ) ,
214
+ } )
215
+ return failedPreparations . map ( ( f ) => f . result as ActionFailure )
147
216
}
148
217
149
- logger . trace ( 'Prepared transactions ' , {
150
- preparedTransactions : populateTransactionsResults ,
151
- } )
218
+ // If some failed, log details
219
+ if ( failedPreparations . length > 0 ) {
220
+ logger . warn ( 'Some transaction preparations failed' , {
221
+ failures : failedPreparations . map ( ( f ) => ( {
222
+ actionId : f . action . id ,
223
+ type : f . action . type ,
224
+ deploymentId : f . action . deploymentID ,
225
+ reason : ( f . result as ActionFailure ) . failureReason ,
226
+ } ) ) ,
227
+ } )
228
+ }
152
229
153
- const callData = populateTransactionsResults
230
+ // Build multicall only for successful preparations
231
+ const callData = successfulPreparations
232
+ . map ( ( prepared ) => prepared . result )
154
233
. flat ( )
155
234
. map ( ( tx ) => tx as PopulatedTransaction )
156
235
. filter ( ( tx : PopulatedTransaction ) => ! ! tx . data )
157
236
. map ( ( tx ) => tx . data as string )
158
237
logger . trace ( 'Prepared transaction calldata' , { callData } )
159
238
160
- return await this . network . transactionManager . executeTransaction (
239
+ // Execute multicall for successful preparations only
240
+ const receipt = await this . network . transactionManager . executeTransaction (
161
241
async ( ) => this . network . contracts . staking . estimateGas . multicall ( callData ) ,
162
242
async ( gasLimit ) =>
163
243
this . network . contracts . staking . multicall ( callData , { gasLimit } ) ,
164
244
this . logger . child ( {
165
- actions : ` ${ JSON . stringify ( validatedActions . map ( ( action ) => action . id ) ) } ` ,
245
+ actions : successfulPreparations . map ( ( p ) => p . action . id ) ,
166
246
function : 'staking.multicall' ,
167
247
} ) ,
168
248
)
249
+
250
+ // Return result with both prepared and failed transactions
251
+ return {
252
+ receipt,
253
+ prepared : successfulPreparations ,
254
+ failed : failedPreparations ,
255
+ } as BatchExecutionResult
169
256
}
170
257
171
258
async confirmTransactions (
@@ -246,7 +333,7 @@ export class AllocationManager {
246
333
}
247
334
}
248
335
249
- async prepareTransactions ( actions : Action [ ] ) : Promise < PopulateTransactionResult [ ] > {
336
+ async prepareTransactions ( actions : Action [ ] ) : Promise < PreparedTransaction [ ] > {
250
337
const currentEpoch = await this . network . contracts . epochManager . currentEpoch ( )
251
338
const context : TransactionPreparationContext = {
252
339
activeAllocations : await this . network . networkMonitor . allocations (
@@ -264,7 +351,10 @@ export class AllocationManager {
264
351
}
265
352
return await pMap (
266
353
actions ,
267
- async ( action : Action ) => await this . prepareTransaction ( action , context ) ,
354
+ async ( action : Action ) => ( {
355
+ action,
356
+ result : await this . prepareTransaction ( action , context ) ,
357
+ } ) ,
268
358
{
269
359
stopOnError : false ,
270
360
} ,
0 commit comments