Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"typechain": "7.0.0"
},
"devDependencies": {
"@arbitrum/nitro-contracts": "1.0.0-beta.5",
"@arbitrum/nitro-contracts": "1.0.0-beta.6",
"@nomiclabs/hardhat-ethers": "^2.0.4",
"@types/chai": "^4.2.11",
"@types/mocha": "^9.0.0",
Expand Down
4 changes: 3 additions & 1 deletion src/lib/inbox/inbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,9 @@ export class InboxTools {
return null
}

const delayedAcc = await bridge.inboxAccs(eventInfo.event.messageIndex)
const delayedAcc = await bridge.delayedInboxAccs(
eventInfo.event.messageIndex
)

return { ...eventInfo, delayedAcc: delayedAcc }
}
Expand Down
72 changes: 50 additions & 22 deletions src/lib/message/L1ToL2Message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,15 @@ export abstract class L1ToL2Message {
* The submit retryable transactions use the typed transaction envelope 2718.
* The id of these transactions is the hash of the RLP encoded transaction.
* @param l2ChainId
* @param fromAddress
* @param fromAddress the address that called the L1 inbox. This is expected to be the non-aliased address, since this function will apply the alias.
* @param messageNumber
* @param l1BaseFee
* @param destAddress
* @param l2CallValue
* @param l1Value
* @param maxSubmissionFee
* @param excessFeeRefundAddress
* @param callValueRefundAddress
* @param excessFeeRefundAddress refund address specified in the retryable creation. Note the L1 inbox aliases this address if it is a L1 smart contract. The user is expected to provide this value already aliased when needed.
* @param callValueRefundAddress refund address specified in the retryable creation. Note the L1 inbox aliases this address if it is a L1 smart contract. The user is expected to provide this value already aliased when needed.
* @param gasLimit
* @param maxFeePerGas
* @param data
Expand All @@ -112,7 +112,6 @@ export abstract class L1ToL2Message {
fromAddress: string,
messageNumber: BigNumber,
l1BaseFee: BigNumber,

destAddress: string,
l2CallValue: BigNumber,
l1Value: BigNumber,
Expand Down Expand Up @@ -294,16 +293,27 @@ export class L1ToL2MessageReader extends L1ToL2Message {

/**
* Receipt for the successful l2 transaction created by this message.
* @returns TransactionReceipt of the first successful redeem if exists, otherwise null
* @returns TransactionReceipt of the first successful redeem if exists, otherwise null.
* Returns expired true if retryable expired without being redeemed, otherwise false.
*/
public async getSuccessfulRedeem(): Promise<TransactionReceipt | null> {
public async getSuccessfulRedeem(): Promise<
| { redeemReceipt: TransactionReceipt | null; expired: false }
| { redeemReceipt: null; expired: true }
> {
const l2Network = await getL2Network(this.l2Provider)
const eventFetcher = new EventFetcher(this.l2Provider)
const creationReceipt = await this.getRetryableCreationReceipt()
// if retryable was created but no longer exists then we know that it was either
// redeemed or expired
const redeemWasSuccessfulOrExpired = creationReceipt && !await this.retryableExists()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const redeemWasSuccessfulOrExpired = creationReceipt && !await this.retryableExists()
const redeemWasSuccessfulOrExpired = creationReceipt && !await this.retryableExists()
if(!redeemWasSuccessfulOrExpired){
// either created but not redeemed yet -> not expired
// or not created -> not expired(?)
return { redeemReceipt: null, expired: false }
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change the return ~L399 too


// check the auto redeem, if that worked we dont need to do costly log queries
const autoRedeem = await this.getAutoRedeemAttempt()
if (autoRedeem && autoRedeem.status === 1) return autoRedeem
if (autoRedeem && autoRedeem.status === 1)
return {
redeemReceipt: autoRedeem,
expired: false,
}

// the auto redeem didnt exist or wasnt successful, look for a later manual redeem
// to do this we need to filter through the whole lifetime of the ticket looking
Expand Down Expand Up @@ -344,7 +354,8 @@ export class L1ToL2MessageReader extends L1ToL2Message {
throw new ArbSdkError(
`Unexpected number of successful redeems. Expected only one redeem for ticket ${this.retryableCreationId}, but found ${successfulRedeem.length}.`
)
if (successfulRedeem.length == 1) return successfulRedeem[0]
if (successfulRedeem.length == 1)
return { redeemReceipt: successfulRedeem[0], expired: false }

const toBlock = await this.l2Provider.getBlock(toBlockNumber)
if (toBlock.timestamp > timeout) {
Expand Down Expand Up @@ -381,28 +392,47 @@ export class L1ToL2MessageReader extends L1ToL2Message {

fromBlock = toBlock
}

// we didnt find a redeem transaction
if(redeemWasSuccessfulOrExpired) {
return {
expired: true,
redeemReceipt: null
}
}
}
return null
return { redeemReceipt: null, expired: false }
}

/**
* Has this message expired. Once expired the retryable ticket can no longer be redeemed.
* @deprecated Will be removed in v3.0.0
* @returns
*/
public async isExpired(): Promise<boolean> {
const currentTimestamp = BigNumber.from(
(await this.l2Provider.getBlock('latest')).timestamp
)
const timeoutTimestamp = await this.getTimeout()
return await this.retryableExists()
}

private async retryableExists(): Promise<boolean> {
try {
const currentTimestamp = BigNumber.from(
(await this.l2Provider.getBlock('latest')).timestamp
)
const timeoutTimestamp = await this.getTimeout()
Comment thread
yahgwai marked this conversation as resolved.
Outdated

// timeoutTimestamp returns the timestamp at which the retryable ticket expires
// it can also return 0 if the ticket l2Tx does not exist
return currentTimestamp.gte(timeoutTimestamp)
// timeoutTimestamp returns the timestamp at which the retryable ticket expires
// it can also return 0 if the ticket l2Tx does not exist
return currentTimestamp.gte(timeoutTimestamp)
} catch (err) {
return false
}
}

protected async receiptsToStatus(
retryableCreationReceipt: TransactionReceipt | null,
successfulRedeemReceipt: TransactionReceipt | null
redeemInfo:
| { redeemReceipt: TransactionReceipt | null; expired: false }
| { redeemReceipt: null; expired: true }
): Promise<L1ToL2MessageStatus> {
// happy path for non auto redeemable messages
// NOT_YET_CREATED -> FUNDS_DEPOSITED
Expand All @@ -428,14 +458,12 @@ export class L1ToL2MessageReader extends L1ToL2Message {
}

// ticket created, has it been auto redeemed?
if (successfulRedeemReceipt && successfulRedeemReceipt.status === 1) {
if (redeemInfo.redeemReceipt && redeemInfo.redeemReceipt.status === 1) {
return L1ToL2MessageStatus.REDEEMED
}

// not redeemed, has it now expired
if (await this.isExpired()) {
return L1ToL2MessageStatus.EXPIRED
}
if (redeemInfo.expired) return L1ToL2MessageStatus.EXPIRED

// ticket was created but not redeemed
// this could be because
Expand Down Expand Up @@ -491,7 +519,7 @@ export class L1ToL2MessageReader extends L1ToL2Message {
if (status === L1ToL2MessageStatus.REDEEMED) {
return {
// if the status is redeemed we know the l2TxReceipt must exist
l2TxReceipt: l2TxReceipt!,
l2TxReceipt: l2TxReceipt.redeemReceipt!,
status,
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/message/L2ToL1Message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ export class L2ToL1MessageReader extends L2ToL1Message {
this.l1Provider
)

return outbox.callStatic.spent(this.event.position)
return outbox.callStatic.isSpent(this.event.position)
}

/**
Expand Down
10 changes: 5 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
"@jridgewell/gen-mapping" "^0.1.0"
"@jridgewell/trace-mapping" "^0.3.9"

"@arbitrum/nitro-contracts@1.0.0-beta.5":
version "1.0.0-beta.5"
resolved "https://registry.yarnpkg.com/@arbitrum/nitro-contracts/-/nitro-contracts-1.0.0-beta.5.tgz#b4ca2a8d99b8a108e7dc412f6c2cb74818d5099a"
integrity sha512-hEpRXIL8S6AfTt6dqJsKCa84JufhuXuRgm2BnximdRrhJ8ukqR6Pt/mndYMa4nWRRTBMYkIxy6EA5iYxDwxWAQ==
"@arbitrum/nitro-contracts@1.0.0-beta.6":
version "1.0.0-beta.6"
resolved "https://registry.yarnpkg.com/@arbitrum/nitro-contracts/-/nitro-contracts-1.0.0-beta.6.tgz#86b4997ea373a5410fbc42c6a9097f0ab07dfa34"
integrity sha512-UyCqUsd5ew0DUZ+MxgfP7fdyGBDoOujeXbjhdg4agJW1ld3lg+E7dgKfN6S0j2v2l/+Yd63HqEp24O5bgSzxTg==
dependencies:
"@openzeppelin/contracts" "4.5.0"
"@openzeppelin/contracts-upgradeable" "4.5.2"
Expand Down Expand Up @@ -1288,7 +1288,7 @@ arb-bridge-peripherals@1.0.10:
optionalDependencies:
"@openzeppelin/upgrades-core" "^1.7.6"

arbos-precompiles@1.0.2, arbos-precompiles@^1.0.2:
arbos-precompiles@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/arbos-precompiles/-/arbos-precompiles-1.0.2.tgz#7bebd5963aef972cd259eb41f3116ea065013ea6"
integrity sha512-1dOFYFJUN0kKoofh6buZJ8qCqTs+oLGSsGzHI0trA/Pka/TCERflCRsNVxez2lihOvK7MT/a2RA8AepKtBXdPQ==
Expand Down