@@ -84,7 +84,7 @@ export class AgentBotRedemption {
8484
8585 async redemptionDefault ( rootEm : EM , args : EventArgs < RedemptionDefault > ) {
8686 await this . bot . runInTransaction ( rootEm , async ( em ) => {
87- const redemption = await this . findRedemption ( em , { requestId : toBN ( args . requestId ) } ) ;
87+ const redemption = await this . findRedemption ( em , { requestId : toBN ( args . requestId ) } , LockMode . PESSIMISTIC_WRITE ) ;
8888 redemption . defaulted = true ;
8989 if ( redemption . state === AgentRedemptionState . UNPAID || redemption . state === AgentRedemptionState . REJECTED ) {
9090 redemption . finalState = this . getFinalState ( redemption ) ;
@@ -101,7 +101,7 @@ export class AgentBotRedemption {
101101 * @param agentVault agent's vault address
102102 */
103103 private async finishRedemption ( rootEm : EM , rd : { requestId : BNish } , finalState : AgentRedemptionFinalState ) {
104- await this . updateRedemption ( rootEm , { requestId : toBN ( rd . requestId ) } , {
104+ await this . updateRedemption ( rootEm , { requestId : toBN ( rd . requestId ) } , null , {
105105 state : AgentRedemptionState . DONE ,
106106 finalState : finalState ,
107107 } ) ;
@@ -125,7 +125,7 @@ export class AgentBotRedemption {
125125 /* istanbul ignore next */
126126 if ( this . bot . stopRequested ( ) ) return ;
127127 try {
128- await this . handleOpenRedemption ( rootEm , state , redemption ) ;
128+ await this . handleActiveRedemptionInState ( rootEm , state , redemption ) ;
129129 } catch ( error ) {
130130 logger . error ( `Error handling redemption ${ redemption . requestId } in state ${ state } ` , error ) ;
131131 }
@@ -181,7 +181,7 @@ export class AgentBotRedemption {
181181 . getResultList ( ) ;
182182 }
183183
184- async handleOpenRedemption ( rootEm : EM , state : AgentRedemptionState , redemption : Readonly < AgentRedemption > ) {
184+ async handleActiveRedemptionInState ( rootEm : EM , state : AgentRedemptionState , redemption : Readonly < AgentRedemption > ) {
185185 switch ( state ) {
186186 case AgentRedemptionState . STARTED :
187187 await this . checkBeforeRedemptionPayment ( rootEm , redemption ) ;
@@ -228,7 +228,7 @@ export class AgentBotRedemption {
228228 throw error ;
229229 }
230230 }
231- redemption = await this . updateRedemption ( rootEm , redemption , {
231+ redemption = await this . updateRedemption ( rootEm , redemption , null , {
232232 state : AgentRedemptionState . DONE ,
233233 finalState : finalState ?? this . getFinalState ( redemption ) ,
234234 } ) ;
@@ -272,7 +272,7 @@ export class AgentBotRedemption {
272272 Time expired on underlying chain. Last block for payment was ${ redemption . lastUnderlyingBlock }
273273 with timestamp ${ redemption . lastUnderlyingTimestamp } . Current block is ${ lastFinalizedBlock . number }
274274 with timestamp ${ lastFinalizedBlock . timestamp } .` ) ;
275- redemption = await this . updateRedemption ( rootEm , redemption , {
275+ redemption = await this . updateRedemption ( rootEm , redemption , AgentRedemptionState . STARTED , {
276276 state : AgentRedemptionState . UNPAID ,
277277 } ) ;
278278 }
@@ -288,11 +288,11 @@ export class AgentBotRedemption {
288288 const poolFeeUBA = redemptionFee . mul ( redemptionPoolFeeShareBIPS ) . divn ( MAX_BIPS ) ;
289289 let maxRedemptionFee = redemptionFee . sub ( poolFeeUBA ) ;
290290
291- // special redemption ticket - `transferToCoreVault`
292- if ( maxRedemptionFee . eq ( BN_ZERO ) ) {
293- const coreVaultSourceAddress = await requireNotNull ( this . context . coreVaultManager ) . coreVaultAddress ( ) ;
294- if ( redemption . paymentAddress === coreVaultSourceAddress ) { // additional check
295- maxRedemptionFee = await this . bot . getTransferToCoreVaultMaxFee ( redemption . paymentAddress , redemption . valueUBA )
291+ // for transfers to core vault, check the free underlying can cover transaction fee, since there is no redemption fee
292+ if ( maxRedemptionFee . eq ( BN_ZERO ) && this . context . coreVaultManager != null ) {
293+ const coreVaultSourceAddress = await this . context . coreVaultManager . coreVaultAddress ( ) ;
294+ if ( redemption . paymentAddress === coreVaultSourceAddress ) { // is it transfer to core vault?
295+ maxRedemptionFee = await this . bot . getTransferToCoreVaultMaxFee ( redemption . paymentAddress , redemption . valueUBA ) ;
296296 if ( maxRedemptionFee . eq ( BN_ZERO ) ) {
297297 logger . error ( `Cannot pay for redemption ${ redemption . requestId } , current fee is greater than agent can spend.` ) ;
298298 await this . notifier . sendTransferToCVRedemptionNoFreeUnderlying ( redemption . requestId ) ;
@@ -304,7 +304,7 @@ export class AgentBotRedemption {
304304 }
305305 }
306306
307- redemption = await this . updateRedemption ( rootEm , redemption , {
307+ redemption = await this . updateRedemption ( rootEm , redemption , AgentRedemptionState . STARTED , {
308308 state : AgentRedemptionState . PAYING ,
309309 } ) ;
310310 try {
@@ -319,7 +319,7 @@ export class AgentBotRedemption {
319319 feeSourceAddress : feeSourceAddress
320320 } ) ;
321321 } ) ;
322- redemption = await this . updateRedemption ( rootEm , redemption , {
322+ redemption = await this . updateRedemption ( rootEm , redemption , AgentRedemptionState . PAYING , {
323323 txDbId : txDbId ,
324324 state : AgentRedemptionState . PAID ,
325325 } ) ;
@@ -345,7 +345,7 @@ export class AgentBotRedemption {
345345 const request = await this . bot . locks . nativeChainLock ( this . bot . requestSubmitterAddress ( ) ) . lockAndRun ( async ( ) => {
346346 return await this . context . attestationProvider . requestAddressValidityProof ( redemption . paymentAddress ) ;
347347 } ) ;
348- redemption = await this . updateRedemption ( rootEm , redemption , {
348+ redemption = await this . updateRedemption ( rootEm , redemption , AgentRedemptionState . STARTED , {
349349 state : AgentRedemptionState . REQUESTED_REJECTION_PROOF ,
350350 proofRequestRound : request . round ,
351351 proofRequestData : request . data ,
@@ -382,7 +382,7 @@ export class AgentBotRedemption {
382382 await this . bot . locks . nativeChainLock ( this . bot . owner . workAddress ) . lockAndRun ( async ( ) => {
383383 await this . context . assetManager . rejectInvalidRedemption ( web3DeepNormalize ( proof ) , redemption . requestId , { from : this . agent . owner . workAddress } ) ;
384384 } ) ;
385- redemption = await this . updateRedemption ( rootEm , redemption , {
385+ redemption = await this . updateRedemption ( rootEm , redemption , AgentRedemptionState . REQUESTED_REJECTION_PROOF , {
386386 state : AgentRedemptionState . DONE ,
387387 finalState : AgentRedemptionFinalState . REJECTED ,
388388 } ) ;
@@ -403,7 +403,7 @@ export class AgentBotRedemption {
403403 await this . notifier . sendRedemptionAddressValidationNoProof ( redemption . requestId ,
404404 redemption . proofRequestRound , redemption . proofRequestData , redemption . paymentAddress ) ;
405405 logger . info ( `Agent ${ this . agent . vaultAddress } will retry obtaining address validation proof for redemption ${ redemption . requestId } .` ) ;
406- redemption = await this . updateRedemption ( rootEm , redemption , {
406+ redemption = await this . updateRedemption ( rootEm , redemption , AgentRedemptionState . REQUESTED_REJECTION_PROOF , {
407407 state : AgentRedemptionState . STARTED ,
408408 proofRequestRound : undefined ,
409409 proofRequestData : undefined ,
@@ -436,7 +436,7 @@ export class AgentBotRedemption {
436436 const info = await this . context . wallet . checkTransactionStatus ( redemption . txDbId ) ;
437437 if ( info . status == TransactionStatus . TX_SUCCESS || info . status == TransactionStatus . TX_FAILED ) {
438438 if ( info . transactionHash ) {
439- redemption = await this . updateRedemption ( rootEm , redemption , {
439+ redemption = await this . updateRedemption ( rootEm , redemption , null , {
440440 txHash : info . transactionHash
441441 } ) ;
442442 assertNotNull ( redemption . txHash ) ;
@@ -448,7 +448,7 @@ export class AgentBotRedemption {
448448 info . replacedByStatus == TransactionStatus . TX_SUCCESS || info . replacedByStatus == TransactionStatus . TX_FAILED
449449 ) ) {
450450 if ( info . replacedByHash ) {
451- redemption = await this . updateRedemption ( rootEm , redemption , {
451+ redemption = await this . updateRedemption ( rootEm , redemption , null , {
452452 txHash : info . replacedByHash
453453 } ) ;
454454 assertNotNull ( redemption . txHash ) ;
@@ -471,7 +471,7 @@ export class AgentBotRedemption {
471471 const request = await this . bot . locks . nativeChainLock ( this . bot . requestSubmitterAddress ( ) ) . lockAndRun ( async ( ) => {
472472 return await this . context . attestationProvider . requestPaymentProof ( txHash , this . agent . underlyingAddress , redemption . paymentAddress ) ;
473473 } ) ;
474- redemption = await this . updateRedemption ( rootEm , redemption , {
474+ redemption = await this . updateRedemption ( rootEm , redemption , AgentRedemptionState . PAID , {
475475 state : AgentRedemptionState . REQUESTED_PROOF ,
476476 proofRequestRound : request . round ,
477477 proofRequestData : request . data ,
@@ -518,7 +518,7 @@ export class AgentBotRedemption {
518518 throw error ;
519519 }
520520 }
521- redemption = await this . updateRedemption ( rootEm , redemption , {
521+ redemption = await this . updateRedemption ( rootEm , redemption , AgentRedemptionState . REQUESTED_PROOF , {
522522 state : AgentRedemptionState . DONE ,
523523 } ) ;
524524 logger . info ( `Agent ${ this . agent . vaultAddress } confirmed redemption payment for redemption ${ redemption . requestId } with proof ${ JSON . stringify ( web3DeepNormalize ( proof ) ) } .` ) ;
@@ -528,7 +528,7 @@ export class AgentBotRedemption {
528528 if ( await this . bot . enoughTimePassedToObtainProof ( redemption ) ) {
529529 await this . notifier . sendRedemptionNoProofObtained ( redemption . requestId , redemption . proofRequestRound , redemption . proofRequestData ) ;
530530 logger . info ( `Agent ${ this . agent . vaultAddress } will retry obtaining proof of payment for redemption ${ redemption . requestId } .` ) ;
531- redemption = await this . updateRedemption ( rootEm , redemption , {
531+ redemption = await this . updateRedemption ( rootEm , redemption , AgentRedemptionState . REQUESTED_PROOF , {
532532 state : AgentRedemptionState . PAID ,
533533 proofRequestRound : undefined ,
534534 proofRequestData : undefined ,
@@ -539,10 +539,14 @@ export class AgentBotRedemption {
539539
540540 /**
541541 * Load and update redemption object in its own transaction.
542+ * If expectedState is not null, it checks in the same transaction that redemption state matches expectedState.
542543 */
543- async updateRedemption ( rootEm : EM , rd : RedemptionId , modifications : Partial < AgentRedemption > ) : Promise < AgentRedemption > {
544+ async updateRedemption ( rootEm : EM , rd : RedemptionId , expectedState : AgentRedemptionState | null , modifications : Partial < AgentRedemption > ) : Promise < AgentRedemption > {
544545 return await this . bot . runInTransaction ( rootEm , async ( em ) => {
545546 const redemption = await this . findRedemption ( em , rd , LockMode . PESSIMISTIC_WRITE ) ;
547+ if ( expectedState != null && redemption . state !== expectedState ) {
548+ throw new Error ( `Expected redemption ${ redemption . requestId } to be in state ${ expectedState } , but it is in state ${ redemption . state } ` ) ;
549+ }
546550 Object . assign ( redemption , modifications ) ;
547551 return redemption ;
548552 } ) ;
0 commit comments