Skip to content

Commit

Permalink
fix: Replay transactions that can be finalized
Browse files Browse the repository at this point in the history
  • Loading branch information
Will Cory authored and Will Cory committed Mar 25, 2024
1 parent 8411493 commit ed619f0
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 17 deletions.
9 changes: 9 additions & 0 deletions .changeset/warm-pets-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@eth-optimism/sdk': major
---

Added new message status enum READY_TO_REPLAY. Previously the sdk returned READY_FOR_RELAY when messages were replayable.

- This new message status represents a very minor breaking change in what gets returned for errored transactions
- allows users of the sdk to discriminate between replayable messages and messages that are ready to be finalized for the first time.
- All other enum MessageStatus values are unchanged
5 changes: 5 additions & 0 deletions .changeset/young-gorillas-roll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@eth-optimism/sdk': patch
---

Fixed bug where replayable transactions would fail `finalize` if they previously were marked as errors but replayable.
59 changes: 42 additions & 17 deletions packages/sdk/src/cross-chain-messenger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,7 @@ export class CrossChainMessenger {
}
} else {
if (failure) {
return MessageStatus.READY_FOR_RELAY
return MessageStatus.READY_FOR_REPLAY
} else {
let timestamp: number
if (this.bedrock) {
Expand Down Expand Up @@ -1243,13 +1243,13 @@ export class CrossChainMessenger {
const challengePeriod =
oracleVersion === '1.0.0'
? // The ABI in the SDK does not contain FINALIZATION_PERIOD_SECONDS
// in OptimismPortal, so making an explicit call instead.
BigNumber.from(
await this.contracts.l1.OptimismPortal.provider.call({
to: this.contracts.l1.OptimismPortal.address,
data: '0xf4daa291', // FINALIZATION_PERIOD_SECONDS
})
)
// in OptimismPortal, so making an explicit call instead.
BigNumber.from(
await this.contracts.l1.OptimismPortal.provider.call({
to: this.contracts.l1.OptimismPortal.address,
data: '0xf4daa291', // FINALIZATION_PERIOD_SECONDS
})
)
: await this.contracts.l1.L2OutputOracle.FINALIZATION_PERIOD_SECONDS()
return challengePeriod.toNumber()
}
Expand Down Expand Up @@ -1490,7 +1490,7 @@ export class CrossChainMessenger {
// latest games are all invalid and the SDK would be forced to make a bunch of archive calls.
for (let i = matches.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
;[matches[i], matches[j]] = [matches[j], matches[i]]
;[matches[i], matches[j]] = [matches[j], matches[i]]
}

// Now we verify the proposals in the matches array.
Expand Down Expand Up @@ -2301,15 +2301,40 @@ export class CrossChainMessenger {
}

if (this.bedrock) {
const withdrawal = await this.toLowLevelMessage(resolved, messageIndex)
return this.contracts.l1.OptimismPortal.populateTransaction.finalizeWithdrawalTransaction(
// get everything we need to finalize
const [status, withdrawal] = await Promise.allSettled([this.getMessageStatus(message), this.toLowLevelMessage(resolved, messageIndex)])

// handle errors
if (status.status === 'rejected' || withdrawal.status === 'rejected') {
const errors: Error[] = []
for (const res of [status, withdrawal]) {
if (res.status === 'rejected') {
errors.push(res.reason)
}
}
throw errors.length > 1 ? new AggregateError(errors) : errors[0]
}

// pick the correct finalize method based on if we are relaying or replaying a failed relay
const finalizeMethod = (() => {
switch (status.value) {
case MessageStatus.READY_FOR_RELAY:
return this.contracts.l1.OptimismPortal.populateTransaction.finalizeWithdrawalTransaction
case MessageStatus.READY_FOR_REPLAY:
return this.contracts.l1.L1CrossDomainMessenger.populateTransaction.relayMessage
default:
throw new Error(`Message is not ready for finalization: ${status.value}`)
}
})()

return finalizeMethod(
[
withdrawal.messageNonce,
withdrawal.sender,
withdrawal.target,
withdrawal.value,
withdrawal.minGasLimit,
withdrawal.message,
withdrawal.value.messageNonce,
withdrawal.value.sender,
withdrawal.value.target,
withdrawal.value.value,
withdrawal.value.minGasLimit,
withdrawal.value.message,
],
opts?.overrides || {}
)
Expand Down
6 changes: 6 additions & 0 deletions packages/sdk/src/interfaces/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ export enum MessageStatus {
* Message has been relayed.
*/
RELAYED,

/**
* Message has errored before but is ready to be replayed.
* This is similar to READY_FOR_RELAY
*/
READY_FOR_REPLAY,
}

/**
Expand Down
1 change: 1 addition & 0 deletions packages/sdk/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"lib": ["ES2021"],
"rootDir": "./src",
"outDir": "./dist"
},
Expand Down

0 comments on commit ed619f0

Please sign in to comment.