diff --git a/packages/viem/src/actions/relayL2ToL2Message.spec.ts b/packages/viem/src/actions/relayL2ToL2Message.spec.ts index 86ffb905..51b6b630 100644 --- a/packages/viem/src/actions/relayL2ToL2Message.spec.ts +++ b/packages/viem/src/actions/relayL2ToL2Message.spec.ts @@ -2,7 +2,13 @@ import { encodeFunctionData } from 'viem' import { describe, expect, it } from 'vitest' import { supersimL2A, supersimL2B } from '@/chains/supersim.js' -import { publicClientA, publicClientB, testAccount, walletClientA, walletClientB } from '@/test/clients.js' +import { + publicClientA, + publicClientB, + testAccount, + walletClientA, + walletClientB, +} from '@/test/clients.js' import { ticTacToeABI, ticTacToeAddress } from '@/test/setupTicTacToe.js' import { createInteropSentL2ToL2Messages, @@ -28,7 +34,10 @@ describe('relayL2ToL2Message', () => { }) const receipt = await publicClientA.waitForTransactionReceipt({ hash }) - const { sentMessages } = await createInteropSentL2ToL2Messages(publicClientA, { receipt }) + const { sentMessages } = await createInteropSentL2ToL2Messages( + publicClientA, + { receipt }, + ) expect(sentMessages).length(1) const gas = await publicClientB.estimateRelayL2ToL2MessageGas({ @@ -51,14 +60,17 @@ describe('relayL2ToL2Message', () => { }) const receipt = await publicClientA.waitForTransactionReceipt({ hash }) - const { sentMessages } = await createInteropSentL2ToL2Messages(publicClientA, { receipt }) + const { sentMessages } = await createInteropSentL2ToL2Messages( + publicClientA, + { receipt }, + ) expect(sentMessages).length(1) expect(() => publicClientB.simulateRelayL2ToL2Message({ account: testAccount, sentMessageId: sentMessages[0].id, - sentMessagePayload: sentMessages[0].payload + sentMessagePayload: sentMessages[0].payload, }), ).not.throw() }) @@ -73,20 +85,28 @@ describe('relayL2ToL2Message', () => { message: calldata, }) - const sendReceipt = await publicClientA.waitForTransactionReceipt({ hash: sendTxHash }) - const { sentMessages } = await createInteropSentL2ToL2Messages(publicClientA, { receipt: sendReceipt }) + const sendReceipt = await publicClientA.waitForTransactionReceipt({ + hash: sendTxHash, + }) + const { sentMessages } = await createInteropSentL2ToL2Messages( + publicClientA, + { receipt: sendReceipt }, + ) expect(sentMessages).length(1) const relayTxHash = await walletClientB.relayL2ToL2Message({ sentMessageId: sentMessages[0].id, - sentMessagePayload: sentMessages[0].payload + sentMessagePayload: sentMessages[0].payload, }) expect(relayTxHash).toBeDefined() // succesfully relayed - const relayReceipt = await publicClientB.waitForTransactionReceipt({ hash: relayTxHash }) - const { successfulMessages, failedMessages } = decodeRelayedL2ToL2Messages({ receipt: relayReceipt }) + const relayReceipt = await publicClientB.waitForTransactionReceipt({ + hash: relayTxHash, + }) + const { successfulMessages, failedMessages } = + decodeRelayedL2ToL2Messages({ receipt: relayReceipt }) expect(successfulMessages).length(1) expect(failedMessages).length(0) @@ -95,7 +115,14 @@ describe('relayL2ToL2Message', () => { expect(messages).length(1) const { destination, messageNonce, sender, target, message } = messages[0] - const msgHash = hashL2ToL2Message(destination, BigInt(supersimL2A.id), messageNonce, sender, target, message) + const msgHash = hashL2ToL2Message( + destination, + BigInt(supersimL2A.id), + messageNonce, + sender, + target, + message, + ) expect(msgHash).toEqual(successfulMessages[0].messageHash) }) }) diff --git a/packages/viem/src/actions/relayL2ToL2Message.ts b/packages/viem/src/actions/relayL2ToL2Message.ts index 2632e240..a774a45c 100644 --- a/packages/viem/src/actions/relayL2ToL2Message.ts +++ b/packages/viem/src/actions/relayL2ToL2Message.ts @@ -15,7 +15,10 @@ import { estimateContractGas, simulateContract } from 'viem/actions' import { l2ToL2CrossDomainMessengerABI } from '@/abis.js' import { contracts } from '@/contracts.js' -import { baseWriteAction, type BaseWriteContractActionParameters } from '@/core/baseWriteAction.js' +import { + baseWriteAction, + type BaseWriteContractActionParameters, +} from '@/core/baseWriteAction.js' import type { MessageIdentifier, MessagePayload } from '@/types/interop.js' import type { ErrorType } from '@/types/utils.js' diff --git a/packages/viem/src/actions/sendL2ToL2Message.spec.ts b/packages/viem/src/actions/sendL2ToL2Message.spec.ts index 6b6cf7c2..5e5d52e5 100644 --- a/packages/viem/src/actions/sendL2ToL2Message.spec.ts +++ b/packages/viem/src/actions/sendL2ToL2Message.spec.ts @@ -25,7 +25,9 @@ describe('sendL2ToL2Message', () => { expect(txHash).toBeDefined() // SentMessage event - const receipt = await publicClientA.waitForTransactionReceipt({ hash: txHash }) + const receipt = await publicClientA.waitForTransactionReceipt({ + hash: txHash, + }) const { messages } = decodeSentL2ToL2Messages({ receipt }) expect(messages).length(1) diff --git a/packages/viem/src/decorators/publicL2.ts b/packages/viem/src/decorators/publicL2.ts index 96e65909..08dafbf2 100644 --- a/packages/viem/src/decorators/publicL2.ts +++ b/packages/viem/src/decorators/publicL2.ts @@ -32,11 +32,7 @@ export type PublicActionsL2< estimateRelayL2ToL2MessageGas: < TChainOverride extends Chain | undefined = undefined, >( - parameters: RelayL2ToL2MessageParameters< - TChain, - TAccount, - TChainOverride - >, + parameters: RelayL2ToL2MessageParameters, ) => Promise simulateSendL2ToL2Message: < @@ -48,11 +44,7 @@ export type PublicActionsL2< simulateRelayL2ToL2Message: < TChainOverride extends Chain | undefined = undefined, >( - parameters: RelayL2ToL2MessageParameters< - TChain, - TAccount, - TChainOverride - >, + parameters: RelayL2ToL2MessageParameters, ) => Promise } @@ -66,10 +58,14 @@ export function publicActionsL2() { ) => { return { ...upstreamPublicActionsL2(), - estimateSendL2ToL2MessageGas: (args) => estimateSendL2ToL2MessageGas(client, args), - estimateRelayL2ToL2MessageGas: (args) => estimateRelayL2ToL2MessageGas(client, args), - simulateSendL2ToL2Message: (args) => simulateSendL2ToL2Message(client, args), - simulateRelayL2ToL2Message: (args) => simulateRelayL2ToL2Message(client, args), + estimateSendL2ToL2MessageGas: (args) => + estimateSendL2ToL2MessageGas(client, args), + estimateRelayL2ToL2MessageGas: (args) => + estimateRelayL2ToL2MessageGas(client, args), + simulateSendL2ToL2Message: (args) => + simulateSendL2ToL2Message(client, args), + simulateRelayL2ToL2Message: (args) => + simulateRelayL2ToL2Message(client, args), } as PublicActionsL2 } } diff --git a/packages/viem/src/index.ts b/packages/viem/src/index.ts index 846e058d..e3931f85 100644 --- a/packages/viem/src/index.ts +++ b/packages/viem/src/index.ts @@ -53,7 +53,7 @@ export type { DecodeSentL2ToL2MessagesParameters, DecodeSentL2ToL2MessagesReturnType, } from '@/utils/l2ToL2CrossDomainMessenger.js' -export { +export { createInteropSentL2ToL2Messages, decodeRelayedL2ToL2Messages, decodeSentL2ToL2Messages, diff --git a/packages/viem/src/test/clients.ts b/packages/viem/src/test/clients.ts index 4100f6d3..af67ba07 100644 --- a/packages/viem/src/test/clients.ts +++ b/packages/viem/src/test/clients.ts @@ -15,7 +15,7 @@ export const testAccount = privateKeyToAccount( '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', ) -/** Chain A **/ +/** Chain A **/ export const publicClientA = createPublicClient({ chain: supersimL2A, diff --git a/packages/viem/src/test/e2e/interop.spec.ts b/packages/viem/src/test/e2e/interop.spec.ts index 9838a622..236d3777 100644 --- a/packages/viem/src/test/e2e/interop.spec.ts +++ b/packages/viem/src/test/e2e/interop.spec.ts @@ -2,9 +2,18 @@ import { encodeFunctionData } from 'viem' import { describe, expect, it } from 'vitest' import { supersimL2B } from '@/chains/supersim.js' -import { publicClientA, publicClientB, testAccount, walletClientA, walletClientB } from '@/test/clients.js' +import { + publicClientA, + publicClientB, + testAccount, + walletClientA, + walletClientB, +} from '@/test/clients.js' import { ticTacToeABI, ticTacToeAddress } from '@/test/setupTicTacToe.js' -import { createInteropSentL2ToL2Messages, decodeRelayedL2ToL2Messages } from '@/utils/l2ToL2CrossDomainMessenger.js' +import { + createInteropSentL2ToL2Messages, + decodeRelayedL2ToL2Messages, +} from '@/utils/l2ToL2CrossDomainMessenger.js' describe('Generic Interop Flow', () => { it('should send and execute cross chain message', async () => { @@ -21,18 +30,27 @@ describe('Generic Interop Flow', () => { message: calldata, }) - const receipt = await publicClientA.waitForTransactionReceipt({ hash: sentMessageTxHash }) - const { sentMessages } = await createInteropSentL2ToL2Messages(publicClientA, { receipt }) + const receipt = await publicClientA.waitForTransactionReceipt({ + hash: sentMessageTxHash, + }) + const { sentMessages } = await createInteropSentL2ToL2Messages( + publicClientA, + { receipt }, + ) expect(sentMessages).length(1) // message was relayed on the other side const relayMessageTxHash = await walletClientB.relayL2ToL2Message({ sentMessageId: sentMessages[0].id, - sentMessagePayload: sentMessages[0].payload + sentMessagePayload: sentMessages[0].payload, }) - const relayMessageReceipt = await publicClientB.waitForTransactionReceipt({ hash: relayMessageTxHash }) - const { successfulMessages, failedMessages } = decodeRelayedL2ToL2Messages({ receipt: relayMessageReceipt }) + const relayMessageReceipt = await publicClientB.waitForTransactionReceipt({ + hash: relayMessageTxHash, + }) + const { successfulMessages, failedMessages } = decodeRelayedL2ToL2Messages({ + receipt: relayMessageReceipt, + }) expect(successfulMessages).length(1) expect(failedMessages).length(0) }) diff --git a/packages/viem/src/test/setupTicTacToe.ts b/packages/viem/src/test/setupTicTacToe.ts index 85c67c0b..287fc283 100644 --- a/packages/viem/src/test/setupTicTacToe.ts +++ b/packages/viem/src/test/setupTicTacToe.ts @@ -305,6 +305,12 @@ export const ticTacToeABI = [ ] as const export async function setupTicTacToe() { - await testClientA.setCode({ address: ticTacToeAddress, bytecode: ticTacToeBytecode }) - await testClientB.setCode({ address: ticTacToeAddress, bytecode: ticTacToeBytecode }) + await testClientA.setCode({ + address: ticTacToeAddress, + bytecode: ticTacToeBytecode, + }) + await testClientB.setCode({ + address: ticTacToeAddress, + bytecode: ticTacToeBytecode, + }) } diff --git a/packages/viem/src/utils/interop.ts b/packages/viem/src/utils/interop.ts index cd4772f7..cb90f74f 100644 --- a/packages/viem/src/utils/interop.ts +++ b/packages/viem/src/utils/interop.ts @@ -20,7 +20,7 @@ export type CreateInteropMessageReturnType = { export type DecodeExecutingMessagesParameters = { receipt: TransactionReceipt } export type DecodeExecutingMessagesReturnType = { - executingMessages: Array<{ id: MessageIdentifier, msgHash: Hash, log: Log }> + executingMessages: Array<{ id: MessageIdentifier; msgHash: Hash; log: Log }> } /** @@ -34,14 +34,14 @@ export async function createInteropMessage< account extends Account | undefined, >( client: PublicClient, - params: CreateInteropMessageParameters + params: CreateInteropMessageParameters, ): Promise { const { log } = params if (log.blockNumber === undefined || log.logIndex === undefined) { - throw new Error("pending log cannot be constructed into an interop message") + throw new Error('pending log cannot be constructed into an interop message') } if (!client.chain) { - throw new Error("define chain required to construct an interop message") + throw new Error('define chain required to construct an interop message') } const block = await client.getBlock({ blockHash: log.blockHash as Hash }) @@ -64,7 +64,7 @@ export async function createInteropMessage< * @returns Decoded cross-chain calls {@link DecodeExecutingMessagesReturnType } */ export function decodeExecutingMessages( - params: DecodeExecutingMessagesParameters + params: DecodeExecutingMessagesParameters, ): DecodeExecutingMessagesReturnType { const logs = parseEventLogs({ abi: crossL2InboxABI, @@ -72,6 +72,8 @@ export function decodeExecutingMessages( logs: params.receipt.logs, }) - const executingMessages = logs.map((log) => { return { ...log.args, log }}) + const executingMessages = logs.map((log) => { + return { ...log.args, log } + }) return { executingMessages } } diff --git a/packages/viem/src/utils/l2ToL2CrossDomainMessenger.ts b/packages/viem/src/utils/l2ToL2CrossDomainMessenger.ts index 3ccfada7..396f4363 100644 --- a/packages/viem/src/utils/l2ToL2CrossDomainMessenger.ts +++ b/packages/viem/src/utils/l2ToL2CrossDomainMessenger.ts @@ -14,16 +14,18 @@ import { keccak256, parseAbiParameters, parseEventLogs, - toHex + toHex, } from 'viem' import { l2ToL2CrossDomainMessengerABI } from '@/abis.js' -import { createInteropMessage } from '@/utils/interop.js' import type { MessageIdentifier, MessagePayload } from '@/types/interop.js' +import { createInteropMessage } from '@/utils/interop.js' -export type CreateInteropSentL2ToL2MessagesParameters = { receipt: TransactionReceipt } +export type CreateInteropSentL2ToL2MessagesParameters = { + receipt: TransactionReceipt +} export type CreateInteropSentL2ToL2MessagesReturnType = { - sentMessages: Array<{ id: MessageIdentifier, payload: MessagePayload }> + sentMessages: Array<{ id: MessageIdentifier; payload: MessagePayload }> } export type DecodeSentL2ToL2MessagesParameters = { receipt: TransactionReceipt } @@ -38,14 +40,24 @@ export type DecodeSentL2ToL2MessagesReturnType = { }> } -export type DecodeRelayedL2ToL2MessagesParameters = { receipt: TransactionReceipt } +export type DecodeRelayedL2ToL2MessagesParameters = { + receipt: TransactionReceipt +} export type DecodeRelayedL2ToL2MessagesReturnType = { - successfulMessages: Array<{ source: bigint, messageNonce: bigint, messageHash: Hash }> - failedMessages: Array<{ source: bigint, messageNonce: bigint, messageHash: Hash }> + successfulMessages: Array<{ + source: bigint + messageNonce: bigint + messageHash: Hash + }> + failedMessages: Array<{ + source: bigint + messageNonce: bigint + messageHash: Hash + }> } /** - * Utility for creating interoperable messages for the SentMessage event + * Utility for creating interoperable messages for the SentMessage event * @category Utils * @param params {@link CreateInteropL2ToL2SentMessagesParameters} * @returns Decoded interop messages {@link CreateInteropSentL2ToL2MessagesReturnType } @@ -55,11 +67,17 @@ export async function createInteropSentL2ToL2Messages< account extends Account | undefined, >( client: PublicClient, - params: CreateInteropSentL2ToL2MessagesParameters + params: CreateInteropSentL2ToL2MessagesParameters, ): Promise { - const selector = keccak256(toHex('SentMessage(uint256,address,uint256,address,bytes)')) - const logs = params.receipt.logs.filter((log) => log.topics.length > 0 || log.topics[0] === selector) - const messages = await Promise.all(logs.map((log) => createInteropMessage(client, { log }))) + const selector = keccak256( + toHex('SentMessage(uint256,address,uint256,address,bytes)'), + ) + const logs = params.receipt.logs.filter( + (log) => log.topics.length > 0 || log.topics[0] === selector, + ) + const messages = await Promise.all( + logs.map((log) => createInteropMessage(client, { log })), + ) return { sentMessages: messages } } @@ -70,15 +88,17 @@ export async function createInteropSentL2ToL2Messages< * @returns Decoded cross-chain calls {@link DecodeSentL2ToL2MessagesReturnType } */ export function decodeSentL2ToL2Messages( - params: DecodeSentL2ToL2MessagesParameters + params: DecodeSentL2ToL2MessagesParameters, ): DecodeSentL2ToL2MessagesReturnType { const sentMessages = parseEventLogs({ - abi: l2ToL2CrossDomainMessengerABI, - eventName: 'SentMessage', - logs: params.receipt.logs, + abi: l2ToL2CrossDomainMessengerABI, + eventName: 'SentMessage', + logs: params.receipt.logs, }) - const messages = sentMessages.map((log) => { return { ...log.args, log }}) + const messages = sentMessages.map((log) => { + return { ...log.args, log } + }) return { messages } } @@ -89,22 +109,30 @@ export function decodeSentL2ToL2Messages( * @returns Identified relayed messages {@link DecodeRelayedL2ToL2MessagesReturnType } */ export function decodeRelayedL2ToL2Messages( - params: DecodeRelayedL2ToL2MessagesParameters + params: DecodeRelayedL2ToL2MessagesParameters, ): DecodeRelayedL2ToL2MessagesReturnType { const RelayedMessageEventName = 'RelayedMessage' const FailedRelayedMessageEventName = 'FailedRelayedMessage' const relayedMessages = parseEventLogs({ - abi: l2ToL2CrossDomainMessengerABI, - eventName: [RelayedMessageEventName, FailedRelayedMessageEventName], - logs: params.receipt.logs, - strict: true, + abi: l2ToL2CrossDomainMessengerABI, + eventName: [RelayedMessageEventName, FailedRelayedMessageEventName], + logs: params.receipt.logs, + strict: true, }) - const successfulMessages = relayedMessages.filter((log) => log.eventName === RelayedMessageEventName) - const failedMessages = relayedMessages.filter((log) => log.eventName === FailedRelayedMessageEventName) + const successfulMessages = relayedMessages.filter( + (log) => log.eventName === RelayedMessageEventName, + ) + const failedMessages = relayedMessages.filter( + (log) => log.eventName === FailedRelayedMessageEventName, + ) return { - successfulMessages: successfulMessages.map((log) => { return { ...log.args, log }}), - failedMessages: failedMessages.map((log) => { return { ...log.args, log }}) + successfulMessages: successfulMessages.map((log) => { + return { ...log.args, log } + }), + failedMessages: failedMessages.map((log) => { + return { ...log.args, log } + }), } } @@ -118,10 +146,14 @@ export function hashL2ToL2Message( messageNonce: bigint, sender: Address, target: Address, - message: Hex + message: Hex, ): Hash { - return keccak256(encodeAbiParameters( - parseAbiParameters('uint256 _dest, uint256 _src, uint256 _nonce, address _sender, address _target, bytes _msg'), - [destination, source, messageNonce, sender, target, message] - )) + return keccak256( + encodeAbiParameters( + parseAbiParameters( + 'uint256 _dest, uint256 _src, uint256 _nonce, address _sender, address _target, bytes _msg', + ), + [destination, source, messageNonce, sender, target, message], + ), + ) }