Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
f3faa8e2
Unverified
Commit
f3faa8e2
authored
Dec 16, 2021
by
Kelvin Fichter
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(sdk): add tests for getTokenBridgeMessagesByAddress
parent
afa24d4a
Changes
9
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
658 additions
and
39 deletions
+658
-39
cross-chain-provider.ts
packages/sdk/src/cross-chain-provider.ts
+140
-3
cross-chain-provider.ts
packages/sdk/src/interfaces/cross-chain-provider.ts
+45
-1
types.ts
packages/sdk/src/interfaces/types.ts
+38
-10
contracts.ts
packages/sdk/src/utils/contracts.ts
+70
-0
message-encoding.ts
packages/sdk/src/utils/message-encoding.ts
+3
-3
MockBridge.sol
packages/sdk/test/contracts/MockBridge.sol
+113
-0
MockMessenger.sol
packages/sdk/test/contracts/MockMessenger.sol
+13
-7
cross-chain-provider.spec.ts
packages/sdk/test/cross-chain-provider.spec.ts
+233
-9
message-encoding.spec.ts
packages/sdk/test/utils/message-encoding.spec.ts
+3
-6
No files found.
packages/sdk/src/cross-chain-provider.ts
View file @
f3faa8e2
...
...
@@ -4,7 +4,7 @@ import {
BlockTag
,
TransactionReceipt
,
}
from
'
@ethersproject/abstract-provider
'
import
{
BigNumber
}
from
'
ethers
'
import
{
ethers
,
BigNumber
,
Event
}
from
'
ethers
'
import
{
ICrossChainProvider
,
OEContracts
,
...
...
@@ -19,6 +19,8 @@ import {
MessageStatus
,
TokenBridgeMessage
,
MessageReceipt
,
CustomBridges
,
CustomBridgesLike
,
}
from
'
./interfaces
'
import
{
toProvider
,
...
...
@@ -26,6 +28,7 @@ import {
toTransactionHash
,
DeepPartial
,
getAllOEContracts
,
getCustomBridges
,
}
from
'
./utils
'
export
class
CrossChainProvider
implements
ICrossChainProvider
{
...
...
@@ -33,6 +36,7 @@ export class CrossChainProvider implements ICrossChainProvider {
public
l2Provider
:
Provider
public
l1ChainId
:
number
public
contracts
:
OEContracts
public
bridges
:
CustomBridges
/**
* Creates a new CrossChainProvider instance.
...
...
@@ -48,6 +52,7 @@ export class CrossChainProvider implements ICrossChainProvider {
l2Provider
:
ProviderLike
l1ChainId
:
NumberLike
contracts
?:
DeepPartial
<
OEContractsLike
>
bridges
?:
Partial
<
CustomBridgesLike
>
})
{
this
.
l1Provider
=
toProvider
(
opts
.
l1Provider
)
this
.
l2Provider
=
toProvider
(
opts
.
l2Provider
)
...
...
@@ -57,6 +62,11 @@ export class CrossChainProvider implements ICrossChainProvider {
l2SignerOrProvider
:
this
.
l2Provider
,
overrides
:
opts
.
contracts
,
})
this
.
bridges
=
getCustomBridges
(
this
.
l1ChainId
,
{
l1SignerOrProvider
:
this
.
l1Provider
,
l2SignerOrProvider
:
this
.
l2Provider
,
overrides
:
opts
.
bridges
,
})
}
public
async
getMessagesByTransaction
(
...
...
@@ -115,6 +125,9 @@ export class CrossChainProvider implements ICrossChainProvider {
sender
:
parsed
.
args
.
sender
,
message
:
parsed
.
args
.
message
,
messageNonce
:
parsed
.
args
.
messageNonce
,
logIndex
:
log
.
logIndex
,
blockNumber
:
log
.
blockNumber
,
transactionHash
:
log
.
transactionHash
,
}
})
}
...
...
@@ -132,13 +145,137 @@ export class CrossChainProvider implements ICrossChainProvider {
public
async
getTokenBridgeMessagesByAddress
(
address
:
AddressLike
,
opts
?
:
{
opts
:
{
direction
?:
MessageDirection
fromBlock
?:
BlockTag
toBlock
?:
BlockTag
}
=
{}
):
Promise
<
TokenBridgeMessage
[]
>
{
const
parseTokenEvent
=
(
event
:
Event
,
dir
:
MessageDirection
):
TokenBridgeMessage
=>
{
return
{
direction
:
dir
,
from
:
event
.
args
.
_from
,
to
:
event
.
args
.
_to
,
l1Token
:
event
.
args
.
_l1Token
||
ethers
.
constants
.
AddressZero
,
l2Token
:
event
.
args
.
_l2Token
||
this
.
contracts
.
l2
.
OVM_ETH
.
address
,
amount
:
event
.
args
.
_amount
,
data
:
event
.
args
.
_data
,
logIndex
:
event
.
logIndex
,
blockNumber
:
event
.
blockNumber
,
transactionHash
:
event
.
transactionHash
,
}
}
// Make sure you provide a direction if you specify a block range. Block ranges don't make
// sense to use on both chains at the same time.
if
(
opts
.
fromBlock
!==
undefined
||
opts
.
toBlock
!==
undefined
)
{
if
(
opts
.
direction
===
undefined
)
{
throw
new
Error
(
'
direction must be specified when using a block range
'
)
}
}
// Keep track of all of the messages triggered by the address in question.
// We'll add messages to this list as we find them, based on the direction that the user has
// requested we find messages in. If the user hasn't requested a direction, we find messages in
// both directions.
const
messages
:
TokenBridgeMessage
[]
=
[]
// First find all messages in the L1 to L2 direction.
if
(
opts
.
direction
===
undefined
||
opts
.
direction
===
MessageDirection
.
L1_TO_L2
)
{
// Find all ETH deposit events and push them into the messages array.
const
ethDepositEvents
=
await
this
.
contracts
.
l1
.
L1StandardBridge
.
queryFilter
(
this
.
contracts
.
l1
.
L1StandardBridge
.
filters
.
ETHDepositInitiated
(
address
),
opts
.
fromBlock
,
opts
.
toBlock
)
for
(
const
event
of
ethDepositEvents
)
{
messages
.
push
(
parseTokenEvent
(
event
,
MessageDirection
.
L1_TO_L2
))
}
// Send an event query for every L1 bridge, this will return an array of arrays.
const
erc20DepositEventSets
=
await
Promise
.
all
(
[
this
.
contracts
.
l1
.
L1StandardBridge
,
...
Object
.
values
(
this
.
bridges
.
l1
),
].
map
(
async
(
bridge
)
=>
{
return
bridge
.
queryFilter
(
bridge
.
filters
.
ERC20DepositInitiated
(
undefined
,
undefined
,
address
),
opts
.
fromBlock
,
opts
.
toBlock
)
})
)
for
(
const
erc20DepositEvents
of
erc20DepositEventSets
)
{
for
(
const
event
of
erc20DepositEvents
)
{
messages
.
push
(
parseTokenEvent
(
event
,
MessageDirection
.
L1_TO_L2
))
}
}
}
// Next find all messages in the L2 to L1 direction.
if
(
opts
.
direction
===
undefined
||
opts
.
direction
===
MessageDirection
.
L2_TO_L1
)
{
// ETH withdrawals and ERC20 withdrawals are the same event on L2.
// Send an event query for every L2 bridge, this will return an array of arrays.
const
withdrawalEventSets
=
await
Promise
.
all
(
[
this
.
contracts
.
l2
.
L2StandardBridge
,
...
Object
.
values
(
this
.
bridges
.
l2
),
].
map
(
async
(
bridge
)
=>
{
return
bridge
.
queryFilter
(
bridge
.
filters
.
WithdrawalInitiated
(
undefined
,
undefined
,
address
),
opts
.
fromBlock
,
opts
.
toBlock
)
})
)
for
(
const
withdrawalEvents
of
withdrawalEventSets
)
{
for
(
const
event
of
withdrawalEvents
)
{
messages
.
push
(
parseTokenEvent
(
event
,
MessageDirection
.
L2_TO_L1
))
}
}
}
return
messages
}
public
async
getDepositsByAddress
(
address
:
AddressLike
,
opts
:
{
fromBlock
?:
BlockTag
toBlock
?:
BlockTag
}
=
{}
):
Promise
<
TokenBridgeMessage
[]
>
{
throw
new
Error
(
'
Not implemented
'
)
return
this
.
getTokenBridgeMessagesByAddress
(
address
,
{
...
opts
,
direction
:
MessageDirection
.
L1_TO_L2
,
})
}
public
async
getWithdrawalsByAddress
(
address
:
AddressLike
,
opts
:
{
fromBlock
?:
BlockTag
toBlock
?:
BlockTag
}
=
{}
):
Promise
<
TokenBridgeMessage
[]
>
{
return
this
.
getTokenBridgeMessagesByAddress
(
address
,
{
...
opts
,
direction
:
MessageDirection
.
L2_TO_L1
,
})
}
public
async
getMessageStatus
(
message
:
MessageLike
):
Promise
<
MessageStatus
>
{
...
...
packages/sdk/src/interfaces/cross-chain-provider.ts
View file @
f3faa8e2
...
...
@@ -11,6 +11,7 @@ import {
TokenBridgeMessage
,
OEContracts
,
MessageReceipt
,
CustomBridges
,
}
from
'
./types
'
/**
...
...
@@ -38,6 +39,11 @@ export interface ICrossChainProvider {
*/
contracts
:
OEContracts
/**
* List of custom bridges for the given network.
*/
bridges
:
CustomBridges
/**
* Retrieves all cross chain messages sent within a given transaction.
*
...
...
@@ -80,7 +86,7 @@ export interface ICrossChainProvider {
/**
* Finds all cross chain messages that correspond to token deposits or withdrawals sent by a
* particular address. Useful for finding deposits/withdrawals because the sender of the message
* will appear to be the StandardBridge contract and not the actual end user.
Returns
* will appear to be the StandardBridge contract and not the actual end user.
*
* @param address Address to search for messages from.
* @param opts Options object.
...
...
@@ -101,6 +107,44 @@ export interface ICrossChainProvider {
}
):
Promise
<
TokenBridgeMessage
[]
>
/**
* Alias for getTokenBridgeMessagesByAddress with a drection of L1_TO_L2.
*
* @param address Address to search for messages from.
* @param opts Options object.
* @param opts.fromBlock Block to start searching for messages from. If not provided, will start
* from the first block (block #0).
* @param opts.toBlock Block to stop searching for messages at. If not provided, will stop at the
* latest known block ("latest").
* @returns All deposit token bridge messages sent by the given address.
*/
getDepositsByAddress
(
address
:
AddressLike
,
opts
?:
{
fromBlock
?:
BlockTag
toBlock
?:
BlockTag
}
):
Promise
<
TokenBridgeMessage
[]
>
/**
* Alias for getTokenBridgeMessagesByAddress with a drection of L2_TO_L1.
*
* @param address Address to search for messages from.
* @param opts Options object.
* @param opts.fromBlock Block to start searching for messages from. If not provided, will start
* from the first block (block #0).
* @param opts.toBlock Block to stop searching for messages at. If not provided, will stop at the
* latest known block ("latest").
* @returns All withdrawal token bridge messages sent by the given address.
*/
getWithdrawalsByAddress
(
address
:
AddressLike
,
opts
?:
{
fromBlock
?:
BlockTag
toBlock
?:
BlockTag
}
):
Promise
<
TokenBridgeMessage
[]
>
/**
* Retrieves the status of a particular message as an enum.
*
...
...
packages/sdk/src/interfaces/types.ts
View file @
f3faa8e2
...
...
@@ -67,6 +67,30 @@ export interface OEContractsLike {
l2
:
OEL2ContractsLike
}
/**
* Represents list of custom bridges.
*/
export
interface
CustomBridges
{
l1
:
{
[
name
:
string
]:
Contract
}
l2
:
{
[
name
:
string
]:
Contract
}
}
/**
* Something that looks like the list of custom bridges.
*/
export
interface
CustomBridgesLike
{
l1
:
{
[
K
in
keyof
CustomBridges
[
'
l1
'
]]:
AddressLike
}
l2
:
{
[
K
in
keyof
CustomBridges
[
'
l2
'
]]:
AddressLike
}
}
/**
* Enum describing the status of a message.
*/
...
...
@@ -123,11 +147,9 @@ export interface CrossChainMessageRequest {
}
/**
* Describes a message that is sent between L1 and L2. Direction determines where the message was
* sent from and where it's being sent to.
* Core components of a cross chain message.
*/
export
interface
CrossChainMessage
{
direction
:
MessageDirection
export
interface
CoreCrossChainMessage
{
sender
:
string
target
:
string
message
:
string
...
...
@@ -135,12 +157,15 @@ export interface CrossChainMessage {
}
/**
* Convenience type for when you don't care which direction the message is going in.
* Describes a message that is sent between L1 and L2. Direction determines where the message was
* sent from and where it's being sent to.
*/
export
type
DirectionlessCrossChainMessage
=
Omit
<
CrossChainMessage
,
'
direction
'
>
export
interface
CrossChainMessage
extends
CoreCrossChainMessage
{
direction
:
MessageDirection
logIndex
:
number
blockNumber
:
number
transactionHash
:
string
}
/**
* Describes a token withdrawal or deposit, along with the underlying raw cross chain message
...
...
@@ -153,7 +178,10 @@ export interface TokenBridgeMessage {
l1Token
:
string
l2Token
:
string
amount
:
BigNumber
raw
:
CrossChainMessage
data
:
string
logIndex
:
number
blockNumber
:
number
transactionHash
:
string
}
/**
...
...
packages/sdk/src/utils/contracts.ts
View file @
f3faa8e2
...
...
@@ -7,6 +7,8 @@ import {
OEContractsLike
,
OEL2ContractsLike
,
AddressLike
,
CustomBridges
,
CustomBridgesLike
,
}
from
'
../interfaces
'
import
{
toAddress
}
from
'
./coercion
'
import
{
DeepPartial
}
from
'
./type-utils
'
...
...
@@ -36,6 +38,18 @@ const NAME_REMAPPING = {
WETH
:
'
WETH9
'
,
}
/**
* Mapping of L1 chain IDs to the list of custom bridge addresses for each chain.
*/
export
const
CUSTOM_BRIDGE_ADDRESSES
:
{
[
l1ChainId
:
number
]:
CustomBridgesLike
}
=
{
1
:
{
l1
:
{},
l2
:
{},
},
}
/**
* Mapping of L1 chain IDs to the appropriate contract addresses for the OE deployments to the
* given network. Simplifies the process of getting the correct contract addresses for a given
...
...
@@ -190,3 +204,59 @@ export const getAllOEContracts = (
l2
:
l2Contracts
,
}
}
/**
* Gets a series of custom bridges for the given L1 chain ID.
*
* @param l1ChainId L1 chain ID for the L1 network where the custom bridges are deployed.
* @param opts Additional options for connecting to the custom bridges.
* @param opts.l1SignerOrProvider Signer or provider to connect to the L1 contracts.
* @param opts.l2SignerOrProvider Signer or provider to connect to the L2 contracts.
* @param opts.overrides Custom contract address overrides for L1 or L2 contracts.
* @returns An object containing ethers.Contract objects connected to the appropriate addresses on
* both L1 and L2.
*/
export
const
getCustomBridges
=
(
l1ChainId
:
number
,
opts
:
{
l1SignerOrProvider
?:
ethers
.
Signer
|
ethers
.
providers
.
Provider
l2SignerOrProvider
?:
ethers
.
Signer
|
ethers
.
providers
.
Provider
overrides
?:
Partial
<
CustomBridgesLike
>
}
=
{}
):
CustomBridges
=>
{
const
addresses
=
CUSTOM_BRIDGE_ADDRESSES
[
l1ChainId
]
||
{
l1
:
{},
l2
:
{},
}
for
(
const
[
contractName
,
contractAddress
]
of
Object
.
entries
(
opts
.
overrides
?.
l1
||
{}
))
{
addresses
.
l1
[
contractName
]
=
contractAddress
}
for
(
const
[
contractName
,
contractAddress
]
of
Object
.
entries
(
opts
.
overrides
?.
l2
||
{}
))
{
addresses
.
l2
[
contractName
]
=
contractAddress
}
const
bridges
=
{
l1
:
{},
l2
:
{},
}
for
(
const
[
contractName
,
contractAddress
]
of
Object
.
entries
(
addresses
.
l1
))
{
bridges
.
l1
[
contractName
]
=
new
Contract
(
toAddress
(
contractAddress
),
getContractInterface
(
'
IL1ERC20Bridge
'
),
opts
.
l1SignerOrProvider
)
}
for
(
const
[
contractName
,
contractAddress
]
of
Object
.
entries
(
addresses
.
l2
))
{
bridges
.
l2
[
contractName
]
=
new
Contract
(
toAddress
(
contractAddress
),
getContractInterface
(
'
IL2ERC20Bridge
'
),
opts
.
l2SignerOrProvider
)
}
return
bridges
}
packages/sdk/src/utils/message-encoding.ts
View file @
f3faa8e2
import
{
getContractInterface
}
from
'
@eth-optimism/contracts
'
import
{
ethers
}
from
'
ethers
'
import
{
Directionless
CrossChainMessage
}
from
'
../interfaces
'
import
{
Core
CrossChainMessage
}
from
'
../interfaces
'
/**
* Returns the canonical encoding of a cross chain message. This encoding is used in various
...
...
@@ -10,7 +10,7 @@ import { DirectionlessCrossChainMessage } from '../interfaces'
* @returns Canonical encoding of the message.
*/
export
const
encodeCrossChainMessage
=
(
message
:
Directionless
CrossChainMessage
message
:
Core
CrossChainMessage
):
string
=>
{
return
getContractInterface
(
'
L2CrossDomainMessenger
'
).
encodeFunctionData
(
'
relayMessage
'
,
...
...
@@ -27,7 +27,7 @@ export const encodeCrossChainMessage = (
* @returns Canonical hash of the message.
*/
export
const
hashCrossChainMessage
=
(
message
:
Directionless
CrossChainMessage
message
:
Core
CrossChainMessage
):
string
=>
{
return
ethers
.
utils
.
solidityKeccak256
(
[
'
bytes
'
],
...
...
packages/sdk/test/contracts/MockBridge.sol
0 → 100644
View file @
f3faa8e2
pragma solidity ^0.8.9;
import { MockMessenger } from "./MockMessenger.sol";
contract MockBridge {
event ERC20DepositInitiated(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event ERC20WithdrawalFinalized(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event WithdrawalInitiated(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event DepositFinalized(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event DepositFailed(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
struct TokenEventStruct {
address l1Token;
address l2Token;
address from;
address to;
uint256 amount;
bytes data;
}
MockMessenger public messenger;
constructor(MockMessenger _messenger) {
messenger = _messenger;
}
function emitERC20DepositInitiated(
TokenEventStruct memory _params
) public {
emit ERC20DepositInitiated(_params.l1Token, _params.l2Token, _params.from, _params.to, _params.amount, _params.data);
messenger.triggerSentMessageEvent(
MockMessenger.SentMessageEventParams(
address(0),
address(0),
hex"1234",
1234,
12345678
)
);
}
function emitERC20WithdrawalFinalized(
TokenEventStruct memory _params
) public {
emit ERC20WithdrawalFinalized(_params.l1Token, _params.l2Token, _params.from, _params.to, _params.amount, _params.data);
}
function emitWithdrawalInitiated(
TokenEventStruct memory _params
) public {
emit WithdrawalInitiated(_params.l1Token, _params.l2Token, _params.from, _params.to, _params.amount, _params.data);
messenger.triggerSentMessageEvent(
MockMessenger.SentMessageEventParams(
address(0),
address(0),
hex"1234",
1234,
12345678
)
);
}
function emitDepositFinalized(
TokenEventStruct memory _params
) public {
emit DepositFinalized(_params.l1Token, _params.l2Token, _params.from, _params.to, _params.amount, _params.data);
}
function emitDepositFailed(
TokenEventStruct memory _params
) public {
emit DepositFailed(_params.l1Token, _params.l2Token, _params.from, _params.to, _params.amount, _params.data);
}
}
packages/sdk/test/contracts/MockMessenger.sol
View file @
f3faa8e2
...
...
@@ -28,17 +28,23 @@ contract MockMessenger is ICrossDomainMessenger {
return;
}
function triggerSentMessageEvent(
SentMessageEventParams memory _params
) public {
emit SentMessage(
_params.target,
_params.sender,
_params.message,
_params.messageNonce,
_params.gasLimit
);
}
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
);
triggerSentMessageEvent(_params[i]);
}
}
...
...
packages/sdk/test/cross-chain-provider.spec.ts
View file @
f3faa8e2
This diff is collapsed.
Click to expand it.
packages/sdk/test/utils/message-encoding.spec.ts
View file @
f3faa8e2
...
...
@@ -3,8 +3,7 @@ import { Contract, Signer } from 'ethers'
import
{
ethers
}
from
'
hardhat
'
import
{
getContractFactory
}
from
'
@eth-optimism/contracts
'
import
{
CrossChainMessage
,
MessageDirection
,
CoreCrossChainMessage
,
encodeCrossChainMessage
,
hashCrossChainMessage
,
}
from
'
../../src
'
...
...
@@ -25,8 +24,7 @@ describe('message encoding utils', () => {
})
it
(
'
should properly encode a message
'
,
async
()
=>
{
const
message
:
CrossChainMessage
=
{
direction
:
MessageDirection
.
L1_TO_L2
,
const
message
:
CoreCrossChainMessage
=
{
target
:
'
0x
'
+
'
11
'
.
repeat
(
20
),
sender
:
'
0x
'
+
'
22
'
.
repeat
(
20
),
message
:
'
0x
'
+
'
1234
'
.
repeat
(
32
),
...
...
@@ -53,8 +51,7 @@ describe('message encoding utils', () => {
})
it
(
'
should properly hash a message
'
,
async
()
=>
{
const
message
:
CrossChainMessage
=
{
direction
:
MessageDirection
.
L1_TO_L2
,
const
message
:
CoreCrossChainMessage
=
{
target
:
'
0x
'
+
'
11
'
.
repeat
(
20
),
sender
:
'
0x
'
+
'
22
'
.
repeat
(
20
),
message
:
'
0x
'
+
'
1234
'
.
repeat
(
32
),
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment