Commit f5758b1a authored by Will Cory's avatar Will Cory

fix(sdk): Fix multicall3 support for sdk

mul gas by 1.5

lint

delete dead code

changeset

fix: gas estimate without decimals

lint again

fix: Fix the allowance bug that happens if you don't override the signer

lint again

fix: Use isSigner instead

fix: missing a ?
parent 13c710c7
......@@ -329,9 +329,16 @@ export class CrossChainMessenger {
* @returns Bedrock representation of the message.
*/
public async toBedrockCrossChainMessage(
message: MessageLike
message: MessageLike,
/**
* The index of the withdrawal if multiple are made with multicall
*/
multiWithdrawalIndex = 0
): Promise<CrossChainMessage> {
const resolved = await this.toCrossChainMessage(message)
const resolved = await this.toCrossChainMessage(
message,
multiWithdrawalIndex
)
// Bedrock messages are already in the correct format.
const { version } = decodeVersionedNonce(resolved.messageNonce)
......@@ -367,6 +374,25 @@ export class CrossChainMessenger {
}
}
public async getWithdrawalsFromMessage(message: MessageLike) {
const resolved = await this.toCrossChainMessage(message)
const receipt = await this.l2Provider.getTransactionReceipt(
resolved.transactionHash
)
const withdrawals: any[] = []
for (const log of receipt.logs) {
if (log.address === this.contracts.l2.BedrockMessagePasser.address) {
const decoded =
this.contracts.l2.L2ToL1MessagePasser.interface.parseLog(log)
if (decoded.name === 'MessagePassed') {
withdrawals.push(decoded.args)
}
}
}
return withdrawals
}
/**
* Transforms a CrossChainMessenger message into its low-level representation inside the
* L2ToL1MessagePasser contract on L2.
......@@ -375,9 +401,16 @@ export class CrossChainMessenger {
* @return Transformed message.
*/
public async toLowLevelMessage(
message: MessageLike
message: MessageLike,
/**
* The index of the withdrawal if multiple are made with multicall
*/
multiWithdrawalIndex = 0
): Promise<LowLevelMessage> {
const resolved = await this.toCrossChainMessage(message)
const resolved = await this.toCrossChainMessage(
message,
multiWithdrawalIndex
)
if (resolved.direction === MessageDirection.L1_TO_L2) {
throw new Error(`can only convert L2 to L1 messages to low level`)
}
......@@ -386,7 +419,10 @@ export class CrossChainMessenger {
const { version } = decodeVersionedNonce(resolved.messageNonce)
let updated: CrossChainMessage
if (version.eq(0)) {
updated = await this.toBedrockCrossChainMessage(resolved)
updated = await this.toBedrockCrossChainMessage(
resolved,
multiWithdrawalIndex
)
} else {
updated = resolved
}
......@@ -411,32 +447,14 @@ export class CrossChainMessenger {
gasLimit = migratedWithdrawalGasLimit(encoded, chainID)
messageNonce = resolved.messageNonce
} else {
const receipt = await this.l2Provider.getTransactionReceipt(
resolved.transactionHash
)
const withdrawals: any[] = []
for (const log of receipt.logs) {
if (log.address === this.contracts.l2.BedrockMessagePasser.address) {
const decoded =
this.contracts.l2.L2ToL1MessagePasser.interface.parseLog(log)
if (decoded.name === 'MessagePassed') {
withdrawals.push(decoded.args)
}
}
}
const withdrawals = await this.getWithdrawalsFromMessage(message)
// Should not happen.
if (withdrawals.length === 0) {
throw new Error(`no withdrawals found in receipt`)
}
// TODO: Add support for multiple withdrawals.
if (withdrawals.length > 1) {
throw new Error(`multiple withdrawals found in receipt`)
}
const withdrawal = withdrawals[0]
const withdrawal = withdrawals[multiWithdrawalIndex]
messageNonce = withdrawal.nonce
gasLimit = withdrawal.gasLimit
}
......@@ -577,7 +595,11 @@ export class CrossChainMessenger {
* @returns Message coerced into a CrossChainMessage.
*/
public async toCrossChainMessage(
message: MessageLike
message: MessageLike,
/**
* The index of the withdrawal if multiple are made with multicall
*/
multiWithdrawalIndex = 0
): Promise<CrossChainMessage> {
if (!message) {
throw new Error('message is undefined')
......@@ -621,14 +643,7 @@ export class CrossChainMessenger {
message as TransactionLike
)
// We only want to treat TransactionLike objects as MessageLike if they only emit a single
// message (very common). It's unintuitive to treat a TransactionLike as a MessageLike if
// they emit more than one message (which message do you pick?), so we throw an error.
if (messages.length !== 1) {
throw new Error(`expected 1 message, got ${messages.length}`)
}
return messages[0]
return messages[multiWithdrawalIndex]
}
}
......@@ -638,9 +653,19 @@ export class CrossChainMessenger {
* @param message Cross chain message to check the status of.
* @returns Status of the message.
*/
public async getMessageStatus(message: MessageLike): Promise<MessageStatus> {
const resolved = await this.toCrossChainMessage(message)
const receipt = await this.getMessageReceipt(resolved)
public async getMessageStatus(
message: MessageLike,
/**
* The index of the withdrawal if multiple are made with multicall
*/
multiWithdrawalIndex = 0
): Promise<MessageStatus> {
const resolved = await this.toCrossChainMessage(
message,
multiWithdrawalIndex
)
const receipt = await this.getMessageReceipt(resolved, multiWithdrawalIndex)
if (resolved.direction === MessageDirection.L1_TO_L2) {
if (receipt === null) {
......@@ -656,13 +681,19 @@ export class CrossChainMessenger {
if (receipt === null) {
let timestamp: number
if (this.bedrock) {
const output = await this.getMessageBedrockOutput(resolved)
const output = await this.getMessageBedrockOutput(
resolved,
multiWithdrawalIndex
)
if (output === null) {
return MessageStatus.STATE_ROOT_NOT_PUBLISHED
}
// Convert the message to the low level message that was proven.
const withdrawal = await this.toLowLevelMessage(resolved)
const withdrawal = await this.toLowLevelMessage(
resolved,
multiWithdrawalIndex
)
// Attempt to fetch the proven withdrawal.
const provenWithdrawal =
......@@ -679,7 +710,10 @@ export class CrossChainMessenger {
// Set the timestamp to the provenWithdrawal's timestamp
timestamp = provenWithdrawal.timestamp.toNumber()
} else {
const stateRoot = await this.getMessageStateRoot(resolved)
const stateRoot = await this.getMessageStateRoot(
resolved,
multiWithdrawalIndex
)
if (stateRoot === null) {
return MessageStatus.STATE_ROOT_NOT_PUBLISHED
}
......@@ -715,9 +749,17 @@ export class CrossChainMessenger {
* given message.
*/
public async getMessageReceipt(
message: MessageLike
message: MessageLike,
/**
* The index of the withdrawal if multiple are made with multicall
*/
multiWithdrawalIndex = 0
): Promise<MessageReceipt> {
const resolved = await this.toCrossChainMessage(message)
const resolved = await this.toCrossChainMessage(
message,
multiWithdrawalIndex
)
// legacy withdrawals relayed prebedrock are v1
const messageHashV0 = hashCrossDomainMessagev0(
resolved.target,
......@@ -819,15 +861,26 @@ export class CrossChainMessenger {
confirmations?: number
pollIntervalMs?: number
timeoutMs?: number
} = {}
} = {},
/**
* The index of the withdrawal if multiple are made with multicall
*/
multiWithdrawalIndex = 0
): Promise<MessageReceipt> {
// Resolving once up-front is slightly more efficient.
const resolved = await this.toCrossChainMessage(message)
const resolved = await this.toCrossChainMessage(
message,
multiWithdrawalIndex
)
let totalTimeMs = 0
while (totalTimeMs < (opts.timeoutMs || Infinity)) {
const tick = Date.now()
const receipt = await this.getMessageReceipt(resolved)
const receipt = await this.getMessageReceipt(
resolved,
multiWithdrawalIndex
)
if (receipt !== null) {
return receipt
} else {
......@@ -857,15 +910,26 @@ export class CrossChainMessenger {
opts: {
pollIntervalMs?: number
timeoutMs?: number
} = {}
} = {},
/**
* The index of the withdrawal if multiple are made with multicall
*/
multiWithdrawalIndex = 0
): Promise<void> {
// Resolving once up-front is slightly more efficient.
const resolved = await this.toCrossChainMessage(message)
const resolved = await this.toCrossChainMessage(
message,
multiWithdrawalIndex
)
let totalTimeMs = 0
while (totalTimeMs < (opts.timeoutMs || Infinity)) {
const tick = Date.now()
const currentStatus = await this.getMessageStatus(resolved)
const currentStatus = await this.getMessageStatus(
resolved,
multiWithdrawalIndex
)
// Handle special cases for L1 to L2 messages.
if (resolved.direction === MessageDirection.L1_TO_L2) {
......@@ -933,7 +997,8 @@ export class CrossChainMessenger {
opts?: {
bufferPercent?: number
from?: string
}
},
multiWithdrawalIndex = 0
): Promise<BigNumber> {
let resolved: CrossChainMessage | CrossChainMessageRequest
let from: string
......@@ -941,7 +1006,10 @@ export class CrossChainMessenger {
resolved = message as CrossChainMessageRequest
from = opts?.from
} else {
resolved = await this.toCrossChainMessage(message as MessageLike)
resolved = await this.toCrossChainMessage(
message as MessageLike,
multiWithdrawalIndex
)
from = opts?.from || (resolved as CrossChainMessage).sender
}
......@@ -971,10 +1039,18 @@ export class CrossChainMessenger {
* @returns Estimated amount of time remaining (in seconds) before the message can be executed.
*/
public async estimateMessageWaitTimeSeconds(
message: MessageLike
message: MessageLike,
/**
* The index of the withdrawal if multiple are made with multicall
*/
multiWithdrawalIndex = 0
): Promise<number> {
const resolved = await this.toCrossChainMessage(message)
const status = await this.getMessageStatus(resolved)
const resolved = await this.toCrossChainMessage(
message,
multiWithdrawalIndex
)
const status = await this.getMessageStatus(resolved, multiWithdrawalIndex)
if (resolved.direction === MessageDirection.L1_TO_L2) {
if (
status === MessageStatus.RELAYED ||
......@@ -1012,7 +1088,10 @@ export class CrossChainMessenger {
// If the message is still within the challenge period, then we need to estimate exactly
// the amount of time left until the challenge period expires. The challenge period starts
// when the state root is published.
const stateRoot = await this.getMessageStateRoot(resolved)
const stateRoot = await this.getMessageStateRoot(
resolved,
multiWithdrawalIndex
)
const challengePeriod = await this.getChallengePeriodSeconds()
const targetBlock = await this.l1Provider.getBlock(
stateRoot.batch.blockNumber
......@@ -1082,9 +1161,17 @@ export class CrossChainMessenger {
* @returns Bedrock output root.
*/
public async getMessageBedrockOutput(
message: MessageLike
message: MessageLike,
/**
* The index of the withdrawal if multiple are made with multicall
*/
multiWithdrawalIndex = 0
): Promise<BedrockOutputData | null> {
const resolved = await this.toCrossChainMessage(message)
const resolved = await this.toCrossChainMessage(
message,
multiWithdrawalIndex
)
// Outputs are only a thing for L2 to L1 messages.
if (resolved.direction === MessageDirection.L1_TO_L2) {
......@@ -1133,9 +1220,17 @@ export class CrossChainMessenger {
* @returns State root for the block in which the message was created.
*/
public async getMessageStateRoot(
message: MessageLike
message: MessageLike,
/**
* The index of the withdrawal if multiple are made with multicall
*/
multiWithdrawalIndex = 0
): Promise<StateRoot | null> {
const resolved = await this.toCrossChainMessage(message)
const resolved = await this.toCrossChainMessage(
message,
multiWithdrawalIndex
)
// State roots are only a thing for L2 to L1 messages.
if (resolved.direction === MessageDirection.L1_TO_L2) {
......@@ -1318,14 +1413,25 @@ export class CrossChainMessenger {
* @returns Proof that can be used to finalize the message.
*/
public async getMessageProof(
message: MessageLike
message: MessageLike,
/**
* The index of the withdrawal if multiple are made with multicall
*/
multiWithdrawalIndex = 0
): Promise<CrossChainMessageProof> {
const resolved = await this.toCrossChainMessage(message)
const resolved = await this.toCrossChainMessage(
message,
multiWithdrawalIndex
)
if (resolved.direction === MessageDirection.L1_TO_L2) {
throw new Error(`can only generate proofs for L2 to L1 messages`)
}
const stateRoot = await this.getMessageStateRoot(resolved)
const stateRoot = await this.getMessageStateRoot(
resolved,
multiWithdrawalIndex
)
if (stateRoot === null) {
throw new Error(`state root for message not yet published`)
}
......@@ -1376,19 +1482,33 @@ export class CrossChainMessenger {
* @returns Proof that can be used to finalize the message.
*/
public async getBedrockMessageProof(
message: MessageLike
message: MessageLike,
/**
* The index of the withdrawal if multiple are made with multicall
*/
multiWithdrawalIndex = 0
): Promise<BedrockCrossChainMessageProof> {
const resolved = await this.toCrossChainMessage(message)
const resolved = await this.toCrossChainMessage(
message,
multiWithdrawalIndex
)
if (resolved.direction === MessageDirection.L1_TO_L2) {
throw new Error(`can only generate proofs for L2 to L1 messages`)
}
const output = await this.getMessageBedrockOutput(resolved)
const output = await this.getMessageBedrockOutput(
resolved,
multiWithdrawalIndex
)
if (output === null) {
throw new Error(`state root for message not yet published`)
}
const withdrawal = await this.toLowLevelMessage(resolved)
const withdrawal = await this.toLowLevelMessage(
resolved,
multiWithdrawalIndex
)
const hash = hashLowLevelMessage(withdrawal)
const messageSlot = hashMessageHash(hash)
......@@ -1734,9 +1854,17 @@ export class CrossChainMessenger {
messageGasLimit: NumberLike,
opts?: {
overrides?: Overrides
}
},
/**
* The index of the withdrawal if multiple are made with multicall
*/
multiWithdrawalIndex = 0
): Promise<TransactionRequest> => {
const resolved = await this.toCrossChainMessage(message)
const resolved = await this.toCrossChainMessage(
message,
multiWithdrawalIndex
)
if (resolved.direction === MessageDirection.L2_TO_L1) {
throw new Error(`cannot resend L2 to L1 message`)
}
......@@ -1780,9 +1908,17 @@ export class CrossChainMessenger {
message: MessageLike,
opts?: {
overrides?: PayableOverrides
}
},
/**
* The index of the withdrawal if multiple are made with multicall
*/
multiWithdrawalIndex = 0
): Promise<TransactionRequest> => {
const resolved = await this.toCrossChainMessage(message)
const resolved = await this.toCrossChainMessage(
message,
multiWithdrawalIndex
)
if (resolved.direction === MessageDirection.L1_TO_L2) {
throw new Error('cannot finalize L1 to L2 message')
}
......@@ -1793,8 +1929,14 @@ export class CrossChainMessenger {
)
}
const withdrawal = await this.toLowLevelMessage(resolved)
const proof = await this.getBedrockMessageProof(resolved)
const withdrawal = await this.toLowLevelMessage(
resolved,
multiWithdrawalIndex
)
const proof = await this.getBedrockMessageProof(
resolved,
multiWithdrawalIndex
)
const args = [
[
......@@ -1835,15 +1977,26 @@ export class CrossChainMessenger {
message: MessageLike,
opts?: {
overrides?: PayableOverrides
}
},
/**
* The index of the withdrawal if multiple are made with multicall
*/
multiWithdrawalIndex = 0
): Promise<TransactionRequest> => {
const resolved = await this.toCrossChainMessage(message)
const resolved = await this.toCrossChainMessage(
message,
multiWithdrawalIndex
)
if (resolved.direction === MessageDirection.L1_TO_L2) {
throw new Error(`cannot finalize L1 to L2 message`)
}
if (this.bedrock) {
const withdrawal = await this.toLowLevelMessage(resolved)
const withdrawal = await this.toLowLevelMessage(
resolved,
multiWithdrawalIndex
)
return this.contracts.l1.OptimismPortal.populateTransaction.finalizeWithdrawalTransaction(
[
withdrawal.messageNonce,
......@@ -1859,7 +2012,7 @@ export class CrossChainMessenger {
// L1CrossDomainMessenger relayMessage is the only method that isn't fully backwards
// compatible, so we need to use the legacy interface. When we fully upgrade to Bedrock we
// should be able to remove this code.
const proof = await this.getMessageProof(resolved)
const proof = await this.getMessageProof(resolved, multiWithdrawalIndex)
const legacyL1XDM = new ethers.Contract(
this.contracts.l1.L1CrossDomainMessenger.address,
getContractInterface('L1CrossDomainMessenger'),
......@@ -2116,10 +2269,18 @@ export class CrossChainMessenger {
message: MessageLike,
opts?: {
overrides?: CallOverrides
}
},
/**
* The index of the withdrawal if multiple are made with multicall
*/
multiWithdrawalIndex = 0
): Promise<BigNumber> => {
return this.l1Provider.estimateGas(
await this.populateTransaction.proveMessage(message, opts)
await this.populateTransaction.proveMessage(
message,
opts,
multiWithdrawalIndex
)
)
},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment