Commit c9991823 authored by Kelvin Fichter's avatar Kelvin Fichter

feat: tests for CrossChainProvider constructor and getMessagesByTransaction

parent 654a4907
/* eslint-disable @typescript-eslint/no-unused-vars */
import {
Provider,
BlockTag,
TransactionReceipt,
} from '@ethersproject/abstract-provider'
import { BigNumber } from 'ethers'
import {
ICrossChainProvider,
OEContracts,
OEContractsLike,
MessageLike,
TransactionLike,
AddressLike,
NumberLike,
ProviderLike,
CrossChainMessage,
MessageDirection,
MessageStatus,
TokenBridgeMessage,
MessageReceipt,
} from './interfaces'
import {
toProvider,
toBigNumber,
toTransactionHash,
DeepPartial,
getAllOEContracts,
} from './utils'
export class CrossChainProvider implements ICrossChainProvider {
public l1Provider: Provider
public l2Provider: Provider
public l1ChainId: number
public contracts: OEContracts
/**
* Creates a new CrossChainProvider instance.
*
* @param opts Options for the provider.
* @param opts.l1Provider Provider for the L1 chain, or a JSON-RPC url.
* @param opts.l2Provider Provider for the L2 chain, or a JSON-RPC url.
* @param opts.l1ChainId Chain ID for the L1 chain.
* @param opts.contracts Optional contract address overrides.
*/
constructor(opts: {
l1Provider: ProviderLike
l2Provider: ProviderLike
l1ChainId: NumberLike
contracts?: DeepPartial<OEContractsLike>
}) {
this.l1Provider = toProvider(opts.l1Provider)
this.l2Provider = toProvider(opts.l2Provider)
this.l1ChainId = toBigNumber(opts.l1ChainId).toNumber()
this.contracts = getAllOEContracts(this.l1ChainId, {
l1SignerOrProvider: this.l1Provider,
l2SignerOrProvider: this.l2Provider,
overrides: opts.contracts,
})
}
public async getMessagesByTransaction(
transaction: TransactionLike,
opts: {
direction?: MessageDirection
} = {}
): Promise<CrossChainMessage[]> {
const txHash = toTransactionHash(transaction)
let receipt: TransactionReceipt
if (opts.direction !== undefined) {
// Get the receipt for the requested direction.
if (opts.direction === MessageDirection.L1_TO_L2) {
receipt = await this.l1Provider.getTransactionReceipt(txHash)
} else {
receipt = await this.l2Provider.getTransactionReceipt(txHash)
}
} else {
// Try both directions, starting with L1 => L2.
receipt = await this.l1Provider.getTransactionReceipt(txHash)
if (receipt) {
opts.direction = MessageDirection.L1_TO_L2
} else {
receipt = await this.l2Provider.getTransactionReceipt(txHash)
opts.direction = MessageDirection.L2_TO_L1
}
}
if (!receipt) {
throw new Error(`unable to find transaction receipt for ${txHash}`)
}
// By this point opts.direction will always be defined.
const messenger =
opts.direction === MessageDirection.L1_TO_L2
? this.contracts.l1.L1CrossDomainMessenger
: this.contracts.l2.L2CrossDomainMessenger
return receipt.logs
.filter((log) => {
// Only look at logs emitted by the messenger address
return log.address === messenger.address
})
.filter((log) => {
// Only look at SentMessage logs specifically
const parsed = messenger.interface.parseLog(log)
return parsed.name === 'SentMessage'
})
.map((log) => {
// Convert each SentMessage log into a message object
const parsed = messenger.interface.parseLog(log)
return {
direction: opts.direction,
target: parsed.args.target,
sender: parsed.args.sender,
message: parsed.args.message,
messageNonce: parsed.args.messageNonce,
}
})
}
public async getMessagesByAddress(
address: AddressLike,
opts?: {
direction?: MessageDirection
fromBlock?: NumberLike
toBlock?: NumberLike
}
): Promise<CrossChainMessage[]> {
throw new Error('Not implemented')
}
public async getTokenBridgeMessagesByAddress(
address: AddressLike,
opts?: {
direction?: MessageDirection
fromBlock?: BlockTag
toBlock?: BlockTag
}
): Promise<TokenBridgeMessage[]> {
throw new Error('Not implemented')
}
public async getMessageStatus(message: MessageLike): Promise<MessageStatus> {
throw new Error('Not implemented')
}
public async getMessageReceipt(
message: MessageLike
): Promise<MessageReceipt> {
throw new Error('Not implemented')
}
public async waitForMessageReciept(
message: MessageLike,
opts?: {
confirmations?: number
pollIntervalMs?: number
timeoutMs?: number
}
): Promise<MessageReceipt> {
throw new Error('Not implemented')
}
public async estimateL2MessageGasLimit(
message: MessageLike
): Promise<BigNumber> {
throw new Error('Not implemented')
}
public async estimateMessageWaitTimeSeconds(
message: MessageLike
): Promise<number> {
throw new Error('Not implemented')
}
public async estimateMessageWaitTimeBlocks(
message: MessageLike
): Promise<number> {
throw new Error('Not implemented')
}
}
export * from './interfaces' export * from './interfaces'
export * from './utils' export * from './utils'
export * from './cross-chain-provider'
...@@ -80,6 +80,20 @@ export const CONTRACT_ADDRESSES: { ...@@ -80,6 +80,20 @@ export const CONTRACT_ADDRESSES: {
}, },
l2: DEFAULT_L2_CONTRACT_ADDRESSES, l2: DEFAULT_L2_CONTRACT_ADDRESSES,
}, },
// Hardhat local
// TODO: Get the actual addresses for this, temporary addresses here are fine for now until we
// start using this package in the integration tests.
31337: {
l1: {
AddressManager: '0x2F7E3cAC91b5148d336BbffB224B4dC79F09f01D',
L1CrossDomainMessenger: '0xEcC89b9EDD804850C4F343A278Be902be11AaF42',
L1StandardBridge: '0x73298186A143a54c20ae98EEE5a025bD5979De02',
StateCommitmentChain: '0x1afcA918eff169eE20fF8AB6Be75f3E872eE1C1A',
CanonicalTransactionChain: '0x2ebA8c4EfDB39A8Cd8f9eD65c50ec079f7CEBD81',
BondManager: '0xE5AE60bD6F8DEe4D0c2BC9268e23B92F1cacC58F',
},
l2: DEFAULT_L2_CONTRACT_ADDRESSES,
},
} }
/** /**
......
pragma solidity ^0.8.9;
// Right now this is copy/pasted from the contracts package. We need to do this because we don't
// currently copy the contracts into the root of the contracts package in the correct way until
// we bundle the contracts package for publication. As a result, we can't properly use the
// package the way we want to from inside the monorepo (yet). Needs to be fixed as part of a
// separate pull request.
interface ICrossDomainMessenger {
/**********
* Events *
**********/
event SentMessage(
address indexed target,
address sender,
bytes message,
uint256 messageNonce,
uint256 gasLimit
);
event RelayedMessage(bytes32 indexed msgHash);
event FailedRelayedMessage(bytes32 indexed msgHash);
/*************
* Variables *
*************/
function xDomainMessageSender() external view returns (address);
/********************
* Public Functions *
********************/
/**
* Sends a cross domain message to the target messenger.
* @param _target Target contract address.
* @param _message Message to send to the target.
* @param _gasLimit Gas limit for the provided message.
*/
function sendMessage(
address _target,
bytes calldata _message,
uint32 _gasLimit
) external;
}
pragma solidity ^0.8.9;
import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol";
contract MockMessenger is ICrossDomainMessenger {
function xDomainMessageSender() public view returns (address) {
return address(0);
}
// Empty function to satisfy the interface.
function sendMessage(
address _target,
bytes calldata _message,
uint32 _gasLimit
) public {
return;
}
struct SentMessageEventParams {
address target;
address sender;
bytes message;
uint256 messageNonce;
uint256 gasLimit;
}
function doNothing() public {
return;
}
function triggerSentMessageEvents(
SentMessageEventParams[] memory _params
) public {
for (uint256 i = 0; i < _params.length; i++) {
emit SentMessage(
_params[i].target,
_params[i].sender,
_params[i].message,
_params[i].messageNonce,
_params[i].gasLimit
);
}
}
function triggerRelayedMessageEvents(
bytes32[] memory _params
) public {
for (uint256 i = 0; i < _params.length; i++) {
emit RelayedMessage(_params[i]);
}
}
function triggerFailedRelayedMessageEvents(
bytes32[] memory _params
) public {
for (uint256 i = 0; i < _params.length; i++) {
emit FailedRelayedMessage(_params[i]);
}
}
}
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