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
40ffd24d
Unverified
Commit
40ffd24d
authored
Feb 01, 2022
by
smartcontracts
Committed by
GitHub
Feb 01, 2022
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2105 from ethereum-optimism/sc/sdk-bridge-adapters
feat(sdk): introduce token bridge adapters
parents
137f7765
75822019
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
1267 additions
and
410 deletions
+1267
-410
dai-bridge.ts
packages/sdk/src/adapters/dai-bridge.ts
+63
-0
eth-bridge.ts
packages/sdk/src/adapters/eth-bridge.ts
+142
-0
index.ts
packages/sdk/src/adapters/index.ts
+3
-0
standard-bridge.ts
packages/sdk/src/adapters/standard-bridge.ts
+308
-0
cross-chain-messenger.ts
packages/sdk/src/cross-chain-messenger.ts
+124
-24
cross-chain-provider.ts
packages/sdk/src/cross-chain-provider.ts
+54
-115
index.ts
packages/sdk/src/index.ts
+1
-0
bridge-adapter.ts
packages/sdk/src/interfaces/bridge-adapter.ts
+239
-0
cross-chain-messenger.ts
packages/sdk/src/interfaces/cross-chain-messenger.ts
+132
-7
cross-chain-provider.ts
packages/sdk/src/interfaces/cross-chain-provider.ts
+16
-6
index.ts
packages/sdk/src/interfaces/index.ts
+1
-1
types.ts
packages/sdk/src/interfaces/types.ts
+15
-14
contracts.ts
packages/sdk/src/utils/contracts.ts
+115
-83
cross-chain-erc20-pair.spec.ts
packages/sdk/test/cross-chain-erc20-pair.spec.ts
+0
-95
cross-chain-messenger.spec.ts
packages/sdk/test/cross-chain-messenger.spec.ts
+39
-0
cross-chain-provider.spec.ts
packages/sdk/test/cross-chain-provider.spec.ts
+15
-65
No files found.
packages/sdk/src/adapters/dai-bridge.ts
0 → 100644
View file @
40ffd24d
/* eslint-disable @typescript-eslint/no-unused-vars */
import
{
Contract
}
from
'
ethers
'
import
{
hexStringEquals
}
from
'
@eth-optimism/core-utils
'
import
{
AddressLike
}
from
'
../interfaces
'
import
{
toAddress
}
from
'
../utils
'
import
{
StandardBridgeAdapter
}
from
'
./standard-bridge
'
/**
* Bridge adapter for DAI.
*/
export
class
DAIBridgeAdapter
extends
StandardBridgeAdapter
{
public
async
supportsTokenPair
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
):
Promise
<
boolean
>
{
// Just need access to this ABI for this one function.
const
l1Bridge
=
new
Contract
(
this
.
l1Bridge
.
address
,
[
{
inputs
:
[],
name
:
'
l1Token
'
,
outputs
:
[
{
internalType
:
'
address
'
,
name
:
''
,
type
:
'
address
'
,
},
],
stateMutability
:
'
view
'
,
type
:
'
function
'
,
},
{
inputs
:
[],
name
:
'
l2Token
'
,
outputs
:
[
{
internalType
:
'
address
'
,
name
:
''
,
type
:
'
address
'
,
},
],
stateMutability
:
'
view
'
,
type
:
'
function
'
,
},
],
this
.
provider
.
l1Provider
)
const
allowedL1Token
=
await
l1Bridge
.
l1Token
()
if
(
!
hexStringEquals
(
allowedL1Token
,
toAddress
(
l1Token
)))
{
return
false
}
const
allowedL2Token
=
await
l1Bridge
.
l2Token
()
if
(
!
hexStringEquals
(
allowedL2Token
,
toAddress
(
l2Token
)))
{
return
false
}
return
true
}
}
packages/sdk/src/adapters/eth-bridge.ts
0 → 100644
View file @
40ffd24d
/* eslint-disable @typescript-eslint/no-unused-vars */
import
{
ethers
,
Overrides
}
from
'
ethers
'
import
{
TransactionRequest
,
BlockTag
}
from
'
@ethersproject/abstract-provider
'
import
{
predeploys
}
from
'
@eth-optimism/contracts
'
import
{
hexStringEquals
}
from
'
@eth-optimism/core-utils
'
import
{
NumberLike
,
AddressLike
,
TokenBridgeMessage
,
MessageDirection
,
}
from
'
../interfaces
'
import
{
toAddress
,
omit
}
from
'
../utils
'
import
{
StandardBridgeAdapter
}
from
'
./standard-bridge
'
/**
* Bridge adapter for the ETH bridge.
*/
export
class
ETHBridgeAdapter
extends
StandardBridgeAdapter
{
public
async
getDepositsByAddress
(
address
:
AddressLike
,
opts
?:
{
fromBlock
?:
BlockTag
toBlock
?:
BlockTag
}
):
Promise
<
TokenBridgeMessage
[]
>
{
const
events
=
await
this
.
l1Bridge
.
queryFilter
(
this
.
l1Bridge
.
filters
.
ETHDepositInitiated
(
address
),
opts
?.
fromBlock
,
opts
?.
toBlock
)
return
events
.
map
((
event
)
=>
{
return
{
direction
:
MessageDirection
.
L1_TO_L2
,
from
:
event
.
args
.
_from
,
to
:
event
.
args
.
_to
,
l1Token
:
ethers
.
constants
.
AddressZero
,
l2Token
:
predeploys
.
OVM_ETH
,
amount
:
event
.
args
.
_amount
,
data
:
event
.
args
.
_data
,
logIndex
:
event
.
logIndex
,
blockNumber
:
event
.
blockNumber
,
transactionHash
:
event
.
transactionHash
,
}
})
}
public
async
getWithdrawalsByAddress
(
address
:
AddressLike
,
opts
?:
{
fromBlock
?:
BlockTag
toBlock
?:
BlockTag
}
):
Promise
<
TokenBridgeMessage
[]
>
{
const
events
=
await
this
.
l2Bridge
.
queryFilter
(
this
.
l2Bridge
.
filters
.
WithdrawalInitiated
(
undefined
,
undefined
,
address
),
opts
?.
fromBlock
,
opts
?.
toBlock
)
return
events
.
filter
((
event
)
=>
{
// Only find ETH withdrawals.
return
(
hexStringEquals
(
event
.
args
.
_l1Token
,
ethers
.
constants
.
AddressZero
)
&&
hexStringEquals
(
event
.
args
.
_l2Token
,
predeploys
.
OVM_ETH
)
)
})
.
map
((
event
)
=>
{
return
{
direction
:
MessageDirection
.
L2_TO_L1
,
from
:
event
.
args
.
_from
,
to
:
event
.
args
.
_to
,
l1Token
:
event
.
args
.
_l1Token
,
l2Token
:
event
.
args
.
_l2Token
,
amount
:
event
.
args
.
_amount
,
data
:
event
.
args
.
_data
,
logIndex
:
event
.
logIndex
,
blockNumber
:
event
.
blockNumber
,
transactionHash
:
event
.
transactionHash
,
}
})
}
public
async
supportsTokenPair
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
):
Promise
<
boolean
>
{
// Only support ETH deposits and withdrawals.
return
(
hexStringEquals
(
toAddress
(
l1Token
),
ethers
.
constants
.
AddressZero
)
&&
hexStringEquals
(
toAddress
(
l2Token
),
predeploys
.
OVM_ETH
)
)
}
populateTransaction
=
{
deposit
:
async
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
l2GasLimit
?:
NumberLike
overrides
?:
Overrides
}
):
Promise
<
TransactionRequest
>
=>
{
if
(
!
(
await
this
.
supportsTokenPair
(
l1Token
,
l2Token
)))
{
throw
new
Error
(
`token pair not supported by bridge`
)
}
return
this
.
l1Bridge
.
populateTransaction
.
depositETH
(
opts
?.
l2GasLimit
||
200
_000
,
// Default to 200k gas limit.
'
0x
'
,
// No data.
{
...
omit
(
opts
?.
overrides
||
{},
'
value
'
),
value
:
amount
,
}
)
},
withdraw
:
async
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
overrides
?:
Overrides
}
):
Promise
<
TransactionRequest
>
=>
{
if
(
!
(
await
this
.
supportsTokenPair
(
l1Token
,
l2Token
)))
{
throw
new
Error
(
`token pair not supported by bridge`
)
}
return
this
.
l2Bridge
.
populateTransaction
.
withdraw
(
toAddress
(
l2Token
),
amount
,
0
,
// L1 gas not required.
'
0x
'
,
// No data.
opts
?.
overrides
||
{}
)
},
}
}
packages/sdk/src/adapters/index.ts
0 → 100644
View file @
40ffd24d
export
*
from
'
./standard-bridge
'
export
*
from
'
./eth-bridge
'
export
*
from
'
./dai-bridge
'
packages/sdk/src/adapters/standard-bridge.ts
0 → 100644
View file @
40ffd24d
/* eslint-disable @typescript-eslint/no-unused-vars */
import
{
ethers
,
Contract
,
Overrides
,
Signer
,
BigNumber
}
from
'
ethers
'
import
{
TransactionRequest
,
TransactionResponse
,
BlockTag
,
}
from
'
@ethersproject/abstract-provider
'
import
{
getContractInterface
,
predeploys
}
from
'
@eth-optimism/contracts
'
import
{
hexStringEquals
}
from
'
@eth-optimism/core-utils
'
import
{
IBridgeAdapter
,
ICrossChainProvider
,
NumberLike
,
AddressLike
,
TokenBridgeMessage
,
MessageDirection
,
}
from
'
../interfaces
'
import
{
toAddress
}
from
'
../utils
'
/**
* Bridge adapter for any token bridge that uses the standard token bridge interface.
*/
export
class
StandardBridgeAdapter
implements
IBridgeAdapter
{
public
provider
:
ICrossChainProvider
public
l1Bridge
:
Contract
public
l2Bridge
:
Contract
/**
* Creates a StandardBridgeAdapter instance.
*
* @param opts Options for the adapter.
* @param opts.provider Provider used to make queries related to cross-chain interactions.
* @param opts.l1Bridge L1 bridge contract.
* @param opts.l2Bridge L2 bridge contract.
*/
constructor
(
opts
:
{
provider
:
ICrossChainProvider
l1Bridge
:
AddressLike
l2Bridge
:
AddressLike
})
{
this
.
provider
=
opts
.
provider
this
.
l1Bridge
=
new
Contract
(
toAddress
(
opts
.
l1Bridge
),
getContractInterface
(
'
L1StandardBridge
'
),
this
.
provider
.
l1Provider
)
this
.
l2Bridge
=
new
Contract
(
toAddress
(
opts
.
l2Bridge
),
getContractInterface
(
'
IL2ERC20Bridge
'
),
this
.
provider
.
l2Provider
)
}
public
async
getTokenBridgeMessagesByAddress
(
address
:
AddressLike
,
opts
?:
{
direction
?:
MessageDirection
}
):
Promise
<
TokenBridgeMessage
[]
>
{
let
messages
:
TokenBridgeMessage
[]
=
[]
if
(
opts
?.
direction
===
undefined
||
opts
?.
direction
===
MessageDirection
.
L1_TO_L2
)
{
messages
=
messages
.
concat
(
await
this
.
getDepositsByAddress
(
address
))
}
if
(
opts
?.
direction
===
undefined
||
opts
?.
direction
===
MessageDirection
.
L2_TO_L1
)
{
messages
=
messages
.
concat
(
await
this
.
getWithdrawalsByAddress
(
address
))
}
return
messages
}
public
async
getDepositsByAddress
(
address
:
AddressLike
,
opts
?:
{
fromBlock
?:
BlockTag
toBlock
?:
BlockTag
}
):
Promise
<
TokenBridgeMessage
[]
>
{
const
events
=
await
this
.
l1Bridge
.
queryFilter
(
this
.
l1Bridge
.
filters
.
ERC20DepositInitiated
(
undefined
,
undefined
,
address
),
opts
?.
fromBlock
,
opts
?.
toBlock
)
return
events
.
filter
((
event
)
=>
{
// Specifically filter out ETH. ETH deposits and withdrawals are handled by the ETH bridge
// adapter. Bridges that are not the ETH bridge should not be able to handle or even
// present ETH deposits or withdrawals.
return
(
!
hexStringEquals
(
event
.
args
.
_l1Token
,
ethers
.
constants
.
AddressZero
)
&&
!
hexStringEquals
(
event
.
args
.
_l2Token
,
predeploys
.
OVM_ETH
)
)
})
.
map
((
event
)
=>
{
return
{
direction
:
MessageDirection
.
L1_TO_L2
,
from
:
event
.
args
.
_from
,
to
:
event
.
args
.
_to
,
l1Token
:
event
.
args
.
_l1Token
,
l2Token
:
event
.
args
.
_l2Token
,
amount
:
event
.
args
.
_amount
,
data
:
event
.
args
.
_data
,
logIndex
:
event
.
logIndex
,
blockNumber
:
event
.
blockNumber
,
transactionHash
:
event
.
transactionHash
,
}
})
}
public
async
getWithdrawalsByAddress
(
address
:
AddressLike
,
opts
?:
{
fromBlock
?:
BlockTag
toBlock
?:
BlockTag
}
):
Promise
<
TokenBridgeMessage
[]
>
{
const
events
=
await
this
.
l2Bridge
.
queryFilter
(
this
.
l2Bridge
.
filters
.
WithdrawalInitiated
(
undefined
,
undefined
,
address
),
opts
?.
fromBlock
,
opts
?.
toBlock
)
return
events
.
filter
((
event
)
=>
{
// Specifically filter out ETH. ETH deposits and withdrawals are handled by the ETH bridge
// adapter. Bridges that are not the ETH bridge should not be able to handle or even
// present ETH deposits or withdrawals.
return
(
!
hexStringEquals
(
event
.
args
.
_l1Token
,
ethers
.
constants
.
AddressZero
)
&&
!
hexStringEquals
(
event
.
args
.
_l2Token
,
predeploys
.
OVM_ETH
)
)
})
.
map
((
event
)
=>
{
return
{
direction
:
MessageDirection
.
L2_TO_L1
,
from
:
event
.
args
.
_from
,
to
:
event
.
args
.
_to
,
l1Token
:
event
.
args
.
_l1Token
,
l2Token
:
event
.
args
.
_l2Token
,
amount
:
event
.
args
.
_amount
,
data
:
event
.
args
.
_data
,
logIndex
:
event
.
logIndex
,
blockNumber
:
event
.
blockNumber
,
transactionHash
:
event
.
transactionHash
,
}
})
}
public
async
supportsTokenPair
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
):
Promise
<
boolean
>
{
try
{
const
contract
=
new
Contract
(
toAddress
(
l2Token
),
getContractInterface
(
'
L2StandardERC20
'
),
this
.
provider
.
l2Provider
)
// Don't support ETH deposits or withdrawals via this bridge.
if
(
hexStringEquals
(
toAddress
(
l1Token
),
ethers
.
constants
.
AddressZero
)
||
hexStringEquals
(
toAddress
(
l2Token
),
predeploys
.
OVM_ETH
)
)
{
return
false
}
// Make sure the L1 token matches.
const
remoteL1Token
=
await
contract
.
l1Token
()
if
(
!
hexStringEquals
(
remoteL1Token
,
toAddress
(
l1Token
)))
{
return
false
}
// Make sure the L2 bridge matches.
const
remoteL2Bridge
=
await
contract
.
l2Bridge
()
if
(
!
hexStringEquals
(
remoteL2Bridge
,
this
.
l2Bridge
.
address
))
{
return
false
}
return
true
}
catch
(
err
)
{
// If the L2 token is not an L2StandardERC20, it may throw an error. If there's a call
// exception then we assume that the token is not supported. Other errors are thrown.
if
(
err
.
message
.
toString
().
includes
(
'
CALL_EXCEPTION
'
))
{
return
false
}
else
{
throw
err
}
}
}
public
async
deposit
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
signer
:
Signer
,
opts
?:
{
l2GasLimit
?:
NumberLike
overrides
?:
Overrides
}
):
Promise
<
TransactionResponse
>
{
return
signer
.
sendTransaction
(
await
this
.
populateTransaction
.
deposit
(
l1Token
,
l2Token
,
amount
,
opts
)
)
}
public
async
withdraw
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
signer
:
Signer
,
opts
?:
{
overrides
?:
Overrides
}
):
Promise
<
TransactionResponse
>
{
return
signer
.
sendTransaction
(
await
this
.
populateTransaction
.
withdraw
(
l1Token
,
l2Token
,
amount
,
opts
)
)
}
populateTransaction
=
{
deposit
:
async
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
l2GasLimit
?:
NumberLike
overrides
?:
Overrides
}
):
Promise
<
TransactionRequest
>
=>
{
if
(
!
(
await
this
.
supportsTokenPair
(
l1Token
,
l2Token
)))
{
throw
new
Error
(
`token pair not supported by bridge`
)
}
return
this
.
l1Bridge
.
depositERC20
(
toAddress
(
l1Token
),
toAddress
(
l2Token
),
amount
,
opts
?.
l2GasLimit
||
200
_000
,
// Default to 200k gas limit.
'
0x
'
,
// No data.
opts
?.
overrides
||
{}
)
},
withdraw
:
async
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
overrides
?:
Overrides
}
):
Promise
<
TransactionRequest
>
=>
{
if
(
!
(
await
this
.
supportsTokenPair
(
l1Token
,
l2Token
)))
{
throw
new
Error
(
`token pair not supported by bridge`
)
}
return
this
.
l2Bridge
.
populateTransaction
.
withdraw
(
toAddress
(
l2Token
),
amount
,
0
,
// L1 gas not required.
'
0x
'
,
// No data.
opts
?.
overrides
||
{}
)
},
}
estimateGas
=
{
deposit
:
async
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
l2GasLimit
?:
NumberLike
overrides
?:
Overrides
}
):
Promise
<
BigNumber
>
=>
{
return
this
.
provider
.
l1Provider
.
estimateGas
(
await
this
.
populateTransaction
.
deposit
(
l1Token
,
l2Token
,
amount
,
opts
)
)
},
withdraw
:
async
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
overrides
?:
Overrides
}
):
Promise
<
BigNumber
>
=>
{
return
this
.
provider
.
l2Provider
.
estimateGas
(
await
this
.
populateTransaction
.
withdraw
(
l1Token
,
l2Token
,
amount
,
opts
)
)
},
}
}
packages/sdk/src/cross-chain-messenger.ts
View file @
40ffd24d
/* eslint-disable @typescript-eslint/no-unused-vars */
import
{
Overrides
,
Signer
,
BigNumber
}
from
'
ethers
'
import
{
ethers
,
Overrides
,
Signer
,
BigNumber
}
from
'
ethers
'
import
{
TransactionRequest
,
TransactionResponse
,
...
...
@@ -10,11 +10,12 @@ import {
CrossChainMessageRequest
,
ICrossChainMessenger
,
ICrossChainProvider
,
IBridgeAdapter
,
MessageLike
,
NumberLike
,
AddressLike
,
MessageDirection
,
}
from
'
./interfaces
'
import
{
omit
}
from
'
./utils
'
export
class
CrossChainMessenger
implements
ICrossChainMessenger
{
provider
:
ICrossChainProvider
...
...
@@ -102,6 +103,43 @@ export class CrossChainMessenger implements ICrossChainMessenger {
)
}
public
async
depositERC20
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
l2GasLimit
?:
NumberLike
overrides
?:
Overrides
}
):
Promise
<
TransactionResponse
>
{
return
this
.
l1Signer
.
sendTransaction
(
await
this
.
populateTransaction
.
depositERC20
(
l1Token
,
l2Token
,
amount
,
opts
)
)
}
public
async
withdrawERC20
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
overrides
?:
Overrides
}
):
Promise
<
TransactionResponse
>
{
return
this
.
l2Signer
.
sendTransaction
(
await
this
.
populateTransaction
.
withdrawERC20
(
l1Token
,
l2Token
,
amount
,
opts
)
)
}
populateTransaction
=
{
sendMessage
:
async
(
message
:
CrossChainMessageRequest
,
...
...
@@ -118,7 +156,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
message
.
message
,
opts
?.
l2GasLimit
||
(
await
this
.
provider
.
estimateL2MessageGasLimit
(
message
)),
o
mit
(
opts
?.
overrides
||
{},
'
l2GasLimit
'
)
o
pts
?.
overrides
||
{}
)
}
else
{
return
this
.
provider
.
contracts
.
l2
.
L2CrossDomainMessenger
.
connect
(
...
...
@@ -127,7 +165,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
message
.
target
,
message
.
message
,
0
,
// Gas limit goes unused when sending from L2 to L1
o
mit
(
opts
?.
overrides
||
{},
'
l2GasLimit
'
)
o
pts
?.
overrides
||
{}
)
}
},
...
...
@@ -173,13 +211,11 @@ export class CrossChainMessenger implements ICrossChainMessenger {
overrides
?:
Overrides
}
):
Promise
<
TransactionRequest
>
=>
{
return
this
.
provider
.
contracts
.
l1
.
L1StandardBridge
.
populateTransaction
.
depositETH
(
opts
?.
l2GasLimit
||
200000
,
// 200k gas is fine as a default
'
0x
'
,
// No data
{
...
omit
(
opts
?.
overrides
||
{},
'
l2GasLimit
'
,
'
value
'
),
value
:
amount
,
}
return
this
.
provider
.
bridges
.
ETH
.
populateTransaction
.
deposit
(
ethers
.
constants
.
AddressZero
,
predeploys
.
OVM_ETH
,
amount
,
opts
)
},
...
...
@@ -189,14 +225,38 @@ export class CrossChainMessenger implements ICrossChainMessenger {
overrides
?:
Overrides
}
):
Promise
<
TransactionRequest
>
=>
{
return
this
.
provider
.
contracts
.
l2
.
L2StandardBridge
.
populateTransaction
.
withdraw
(
return
this
.
provider
.
bridges
.
ETH
.
populateTransaction
.
withdraw
(
ethers
.
constants
.
AddressZero
,
predeploys
.
OVM_ETH
,
amount
,
0
,
// No need to supply gas here
'
0x
'
,
// No data,
opts
?.
overrides
||
{}
opts
)
},
depositERC20
:
async
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
l2GasLimit
?:
NumberLike
overrides
?:
Overrides
}
):
Promise
<
TransactionRequest
>
=>
{
const
bridge
=
await
this
.
provider
.
getBridgeForTokenPair
(
l1Token
,
l2Token
)
return
bridge
.
populateTransaction
.
deposit
(
l1Token
,
l2Token
,
amount
,
opts
)
},
withdrawERC20
:
async
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
overrides
?:
Overrides
}
):
Promise
<
TransactionRequest
>
=>
{
const
bridge
=
await
this
.
provider
.
getBridgeForTokenPair
(
l1Token
,
l2Token
)
return
bridge
.
populateTransaction
.
withdraw
(
l1Token
,
l2Token
,
amount
,
opts
)
},
}
estimateGas
=
{
...
...
@@ -222,12 +282,13 @@ export class CrossChainMessenger implements ICrossChainMessenger {
overrides
?:
Overrides
}
):
Promise
<
BigNumber
>
=>
{
const
tx
=
await
this
.
populateTransaction
.
resendMessage
(
message
,
messageGasLimit
,
opts
return
this
.
provider
.
l1Provider
.
estimateGas
(
await
this
.
populateTransaction
.
resendMessage
(
message
,
messageGasLimit
,
opts
)
)
return
this
.
provider
.
l1Provider
.
estimateGas
(
tx
)
},
finalizeMessage
:
async
(
...
...
@@ -246,8 +307,9 @@ export class CrossChainMessenger implements ICrossChainMessenger {
overrides
?:
Overrides
}
):
Promise
<
BigNumber
>
=>
{
const
tx
=
await
this
.
populateTransaction
.
depositETH
(
amount
,
opts
)
return
this
.
provider
.
l1Provider
.
estimateGas
(
tx
)
return
this
.
provider
.
l1Provider
.
estimateGas
(
await
this
.
populateTransaction
.
depositETH
(
amount
,
opts
)
)
},
withdrawETH
:
async
(
...
...
@@ -256,8 +318,46 @@ export class CrossChainMessenger implements ICrossChainMessenger {
overrides
?:
Overrides
}
):
Promise
<
BigNumber
>
=>
{
const
tx
=
await
this
.
populateTransaction
.
withdrawETH
(
amount
,
opts
)
return
this
.
provider
.
l2Provider
.
estimateGas
(
tx
)
return
this
.
provider
.
l2Provider
.
estimateGas
(
await
this
.
populateTransaction
.
withdrawETH
(
amount
,
opts
)
)
},
depositERC20
:
async
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
l2GasLimit
?:
NumberLike
overrides
?:
Overrides
}
):
Promise
<
BigNumber
>
=>
{
return
this
.
provider
.
l2Provider
.
estimateGas
(
await
this
.
populateTransaction
.
depositERC20
(
l1Token
,
l2Token
,
amount
,
opts
)
)
},
withdrawERC20
:
async
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
overrides
?:
Overrides
}
):
Promise
<
BigNumber
>
=>
{
return
this
.
provider
.
l2Provider
.
estimateGas
(
await
this
.
populateTransaction
.
withdrawERC20
(
l1Token
,
l2Token
,
amount
,
opts
)
)
},
}
}
packages/sdk/src/cross-chain-provider.ts
View file @
40ffd24d
...
...
@@ -4,7 +4,7 @@ import {
BlockTag
,
TransactionReceipt
,
}
from
'
@ethersproject/abstract-provider
'
import
{
ethers
,
BigNumber
,
Event
}
from
'
ethers
'
import
{
ethers
,
BigNumber
}
from
'
ethers
'
import
{
sleep
}
from
'
@eth-optimism/core-utils
'
import
{
...
...
@@ -24,10 +24,11 @@ import {
TokenBridgeMessage
,
MessageReceipt
,
MessageReceiptStatus
,
CustomBridges
,
CustomBridgesLike
,
BridgeAdapterData
,
BridgeAdapters
,
StateRoot
,
StateRootBatch
,
IBridgeAdapter
,
}
from
'
./interfaces
'
import
{
toProvider
,
...
...
@@ -35,7 +36,7 @@ import {
toTransactionHash
,
DeepPartial
,
getAllOEContracts
,
get
CustomBridge
s
,
get
BridgeAdapter
s
,
hashCrossChainMessage
,
}
from
'
./utils
'
...
...
@@ -44,7 +45,7 @@ export class CrossChainProvider implements ICrossChainProvider {
public
l2Provider
:
Provider
public
l1ChainId
:
number
public
contracts
:
OEContracts
public
bridges
:
CustomBridge
s
public
bridges
:
BridgeAdapter
s
/**
* Creates a new CrossChainProvider instance.
...
...
@@ -61,7 +62,7 @@ export class CrossChainProvider implements ICrossChainProvider {
l2Provider
:
ProviderLike
l1ChainId
:
NumberLike
contracts
?:
DeepPartial
<
OEContractsLike
>
bridges
?:
Partial
<
CustomBridgesLike
>
bridges
?:
BridgeAdapterData
})
{
this
.
l1Provider
=
toProvider
(
opts
.
l1Provider
)
this
.
l2Provider
=
toProvider
(
opts
.
l2Provider
)
...
...
@@ -71,9 +72,7 @@ export class CrossChainProvider implements ICrossChainProvider {
l2SignerOrProvider
:
this
.
l2Provider
,
overrides
:
opts
.
contracts
,
})
this
.
bridges
=
getCustomBridges
(
this
.
l1ChainId
,
{
l1SignerOrProvider
:
this
.
l1Provider
,
l2SignerOrProvider
:
this
.
l2Provider
,
this
.
bridges
=
getBridgeAdapters
(
this
.
l1ChainId
,
this
,
{
overrides
:
opts
.
bridges
,
})
}
...
...
@@ -153,113 +152,43 @@ export class CrossChainProvider implements ICrossChainProvider {
throw
new
Error
(
'
Not implemented
'
)
}
public
async
getTokenBridgeMessagesByAddress
(
address
:
AddressLike
,
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
,
public
async
getBridgeForTokenPair
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
):
Promise
<
IBridgeAdapter
>
{
const
bridges
:
IBridgeAdapter
[]
=
[]
for
(
const
bridge
of
Object
.
values
(
this
.
bridges
))
{
if
(
await
bridge
.
supportsTokenPair
(
l1Token
,
l2Token
))
{
bridges
.
push
(
bridge
)
}
}
// 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
'
)
}
if
(
bridges
.
length
===
0
)
{
throw
new
Error
(
`no supported bridge for token pair`
)
}
// 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
))
}
}
if
(
bridges
.
length
>
1
)
{
throw
new
Error
(
`found more than one bridge for token pair`
)
}
// 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
)
return
bridges
[
0
]
}
public
async
getTokenBridgeMessagesByAddress
(
address
:
AddressLike
,
opts
:
{
direction
?:
MessageDirection
}
=
{}
):
Promise
<
TokenBridgeMessage
[]
>
{
return
(
await
Promise
.
all
(
Object
.
values
(
this
.
bridges
).
map
(
async
(
bridge
)
=>
{
return
bridge
.
getTokenBridgeMessagesByAddress
(
address
,
opts
)
})
)
for
(
const
withdrawalEvents
of
withdrawalEventSets
)
{
for
(
const
event
of
withdrawalEvents
)
{
messages
.
push
(
parseTokenEvent
(
event
,
MessageDirection
.
L2_TO_L1
))
}
}
}
return
messages
).
reduce
((
acc
,
val
)
=>
{
return
acc
.
concat
(
val
)
},
[])
}
public
async
getDepositsByAddress
(
...
...
@@ -269,10 +198,15 @@ export class CrossChainProvider implements ICrossChainProvider {
toBlock
?:
BlockTag
}
=
{}
):
Promise
<
TokenBridgeMessage
[]
>
{
return
this
.
getTokenBridgeMessagesByAddress
(
address
,
{
...
opts
,
direction
:
MessageDirection
.
L1_TO_L2
,
})
return
(
await
Promise
.
all
(
Object
.
values
(
this
.
bridges
).
map
(
async
(
bridge
)
=>
{
return
bridge
.
getDepositsByAddress
(
address
,
opts
)
})
)
).
reduce
((
acc
,
val
)
=>
{
return
acc
.
concat
(
val
)
},
[])
}
public
async
getWithdrawalsByAddress
(
...
...
@@ -282,10 +216,15 @@ export class CrossChainProvider implements ICrossChainProvider {
toBlock
?:
BlockTag
}
=
{}
):
Promise
<
TokenBridgeMessage
[]
>
{
return
this
.
getTokenBridgeMessagesByAddress
(
address
,
{
...
opts
,
direction
:
MessageDirection
.
L2_TO_L1
,
})
return
(
await
Promise
.
all
(
Object
.
values
(
this
.
bridges
).
map
(
async
(
bridge
)
=>
{
return
bridge
.
getWithdrawalsByAddress
(
address
,
opts
)
})
)
).
reduce
((
acc
,
val
)
=>
{
return
acc
.
concat
(
val
)
},
[])
}
public
async
toCrossChainMessage
(
...
...
packages/sdk/src/index.ts
View file @
40ffd24d
...
...
@@ -2,3 +2,4 @@ export * from './interfaces'
export
*
from
'
./utils
'
export
*
from
'
./cross-chain-provider
'
export
*
from
'
./cross-chain-messenger
'
export
*
from
'
./adapters
'
packages/sdk/src/interfaces/
cross-chain-erc20-pai
r.ts
→
packages/sdk/src/interfaces/
bridge-adapte
r.ts
View file @
40ffd24d
import
{
Overrides
,
Contract
}
from
'
ethers
'
import
{
Contract
,
Overrides
,
Signer
,
BigNumber
}
from
'
ethers
'
import
{
TransactionRequest
,
TransactionResponse
,
BlockTag
,
}
from
'
@ethersproject/abstract-provider
'
import
{
NumberLike
}
from
'
./types
'
import
{
ICrossChainMessenger
}
from
'
./cross-chain-messenger
'
import
{
NumberLike
,
AddressLike
,
MessageDirection
,
TokenBridgeMessage
,
}
from
'
./types
'
import
{
ICrossChainProvider
}
from
'
./cross-chain-provider
'
/**
* Represents an L1<>L2 ERC20 token pair.
* Represents an adapter for an L1<>L2 token bridge. Each custom bridge currently needs its own
* adapter because the bridge interface is not standardized. This may change in the future.
*/
export
interface
ICrossChainERC20Pair
{
export
interface
IBridgeAdapter
{
/**
* Provider used to make queries related to cross-chain interactions.
*/
provider
:
ICrossChainProvider
/**
* L1 bridge contract.
*/
l1Bridge
:
Contract
/**
*
Messenger that will be used to carry out cross-chain iteractions
.
*
L2 bridge contract
.
*/
messenger
:
ICrossChainMessenger
l2Bridge
:
Contract
/**
* Ethers Contract object connected to the L1 token.
* 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.
*
* @param address Address to search for messages from.
* @param opts Options object.
* @param opts.direction Direction to search for messages in. If not provided, will attempt to
* find all messages in both directions.
* @returns All token bridge messages sent by the given address.
*/
l1Token
:
Contract
getTokenBridgeMessagesByAddress
(
address
:
AddressLike
,
opts
?:
{
direction
?:
MessageDirection
}
):
Promise
<
TokenBridgeMessage
[]
>
/**
* Ethers Contract object connected to the L2 token.
* Gets all deposits for a given address.
*
* @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.
*/
l2Token
:
Contract
getDepositsByAddress
(
address
:
AddressLike
,
opts
?:
{
fromBlock
?:
BlockTag
toBlock
?:
BlockTag
}
):
Promise
<
TokenBridgeMessage
[]
>
/**
* Gets all withdrawals for a given address.
*
* @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
[]
>
/**
* Checks whether the given token pair is supported by the bridge.
*
* @param l1Token The L1 token address.
* @param l2Token The L2 token address.
* @returns Whether the given token pair is supported by the bridge.
*/
supportsTokenPair
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
):
Promise
<
boolean
>
/**
* Deposits some tokens into the L2 chain.
*
* @param l1Token The L1 token address.
* @param l2Token The L2 token address.
* @param amount Amount of the token to deposit.
* @param signer Signer used to sign and send the transaction.
* @param opts Additional options.
* @param opts.l2GasLimit Optional gas limit to use for the transaction on L2.
* @param opts.overrides Optional transaction overrides.
* @returns Transaction response for the deposit transaction.
*/
deposit
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
signer
:
Signer
,
opts
?:
{
l2GasLimit
?:
NumberLike
overrides
?:
Overrides
...
...
@@ -46,13 +127,19 @@ export interface ICrossChainERC20Pair {
/**
* Withdraws some tokens back to the L1 chain.
*
* @param l1Token The L1 token address.
* @param l2Token The L2 token address.
* @param amount Amount of the token to withdraw.
* @param signer Signer used to sign and send the transaction.
* @param opts Additional options.
* @param opts.overrides Optional transaction overrides.
* @returns Transaction response for the withdraw transaction.
*/
withdraw
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
signer
:
Signer
,
opts
?:
{
overrides
?:
Overrides
}
...
...
@@ -66,6 +153,8 @@ export interface ICrossChainERC20Pair {
/**
* Generates a transaction for depositing some tokens into the L2 chain.
*
* @param l1Token The L1 token address.
* @param l2Token The L2 token address.
* @param amount Amount of the token to deposit.
* @param opts Additional options.
* @param opts.l2GasLimit Optional gas limit to use for the transaction on L2.
...
...
@@ -73,22 +162,28 @@ export interface ICrossChainERC20Pair {
* @returns Transaction that can be signed and executed to deposit the tokens.
*/
deposit
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
l2GasLimit
?:
NumberLike
overrides
?:
Overrides
}
):
Promise
<
TransactionRe
sponse
>
):
Promise
<
TransactionRe
quest
>
/**
* Generates a transaction for withdrawing some tokens back to the L1 chain.
*
* @param l1Token The L1 token address.
* @param l2Token The L2 token address.
* @param amount Amount of the token to withdraw.
* @param opts Additional options.
* @param opts.overrides Optional transaction overrides.
* @returns Transaction that can be signed and executed to withdraw the tokens.
*/
withdraw
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
overrides
?:
Overrides
...
...
@@ -104,33 +199,41 @@ export interface ICrossChainERC20Pair {
/**
* Estimates gas required to deposit some tokens into the L2 chain.
*
* @param l1Token The L1 token address.
* @param l2Token The L2 token address.
* @param amount Amount of the token to deposit.
* @param opts Additional options.
* @param opts.l2GasLimit Optional gas limit to use for the transaction on L2.
* @param opts.overrides Optional transaction overrides.
* @returns
Transaction that can be signed and executed to deposit the tokens
.
* @returns
Gas estimate for the transaction
.
*/
deposit
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
l2GasLimit
?:
NumberLike
overrides
?:
Overrides
}
):
Promise
<
TransactionResponse
>
):
Promise
<
BigNumber
>
/**
* Estimates gas required to withdraw some tokens back to the L1 chain.
*
* @param l1Token The L1 token address.
* @param l2Token The L2 token address.
* @param amount Amount of the token to withdraw.
* @param opts Additional options.
* @param opts.overrides Optional transaction overrides.
* @returns
Transaction that can be signed and executed to withdraw the tokens
.
* @returns
Gas estimate for the transaction
.
*/
withdraw
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
overrides
?:
Overrides
}
):
Promise
<
TransactionRequest
>
):
Promise
<
BigNumber
>
}
}
packages/sdk/src/interfaces/cross-chain-messenger.ts
View file @
40ffd24d
...
...
@@ -4,7 +4,12 @@ import {
TransactionResponse
,
}
from
'
@ethersproject/abstract-provider
'
import
{
MessageLike
,
NumberLike
,
CrossChainMessageRequest
}
from
'
./types
'
import
{
MessageLike
,
NumberLike
,
CrossChainMessageRequest
,
AddressLike
,
}
from
'
./types
'
import
{
ICrossChainProvider
}
from
'
./cross-chain-provider
'
/**
...
...
@@ -110,6 +115,46 @@ export interface ICrossChainMessenger {
}
):
Promise
<
TransactionResponse
>
/**
* Deposits some ERC20 tokens into the L2 chain.
*
* @param l1Token Address of the L1 token.
* @param l2Token Address of the L2 token.
* @param amount Amount to deposit.
* @param opts Additional options.
* @param opts.l2GasLimit Optional gas limit to use for the transaction on L2.
* @param opts.overrides Optional transaction overrides.
* @returns Transaction response for the deposit transaction.
*/
depositERC20
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
l2GasLimit
?:
NumberLike
overrides
?:
Overrides
}
):
Promise
<
TransactionResponse
>
/**
* Withdraws some ERC20 tokens back to the L1 chain.
*
* @param l1Token Address of the L1 token.
* @param l2Token Address of the L2 token.
* @param amount Amount to withdraw.
* @param opts Additional options.
* @param opts.overrides Optional transaction overrides.
* @returns Transaction response for the withdraw transaction.
*/
withdrawERC20
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
overrides
?:
Overrides
}
):
Promise
<
TransactionResponse
>
/**
* Object that holds the functions that generate transactions to be signed by the user.
* Follows the pattern used by ethers.js.
...
...
@@ -191,7 +236,7 @@ export interface ICrossChainMessenger {
* @param amount Amount of ETH to withdraw.
* @param opts Additional options.
* @param opts.overrides Optional transaction overrides.
* @returns Transaction that can be signed and executed to withdraw the
tokens
.
* @returns Transaction that can be signed and executed to withdraw the
ETH
.
*/
withdrawETH
(
amount
:
NumberLike
,
...
...
@@ -199,6 +244,46 @@ export interface ICrossChainMessenger {
overrides
?:
Overrides
}
):
Promise
<
TransactionRequest
>
/**
* Generates a transaction for depositing some ERC20 tokens into the L2 chain.
*
* @param l1Token Address of the L1 token.
* @param l2Token Address of the L2 token.
* @param amount Amount to deposit.
* @param opts Additional options.
* @param opts.l2GasLimit Optional gas limit to use for the transaction on L2.
* @param opts.overrides Optional transaction overrides.
* @returns Transaction that can be signed and executed to deposit the tokens.
*/
depositERC20
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
l2GasLimit
?:
NumberLike
overrides
?:
Overrides
}
):
Promise
<
TransactionRequest
>
/**
* Generates a transaction for withdrawing some ERC20 tokens back to the L1 chain.
*
* @param l1Token Address of the L1 token.
* @param l2Token Address of the L2 token.
* @param amount Amount to withdraw.
* @param opts Additional options.
* @param opts.overrides Optional transaction overrides.
* @returns Transaction that can be signed and executed to withdraw the tokens.
*/
withdrawERC20
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
overrides
?:
Overrides
}
):
Promise
<
TransactionRequest
>
}
/**
...
...
@@ -213,7 +298,7 @@ export interface ICrossChainMessenger {
* @param opts Additional options.
* @param opts.l2GasLimit Optional gas limit to use for the transaction on L2.
* @param opts.overrides Optional transaction overrides.
* @returns
Transaction that can be signed and executed to send the message
.
* @returns
Gas estimate for the transaction
.
*/
sendMessage
:
(
message
:
CrossChainMessageRequest
,
...
...
@@ -230,7 +315,7 @@ export interface ICrossChainMessenger {
* @param messageGasLimit New gas limit to use for the message.
* @param opts Additional options.
* @param opts.overrides Optional transaction overrides.
* @returns
Transaction that can be signed and executed to resend the message
.
* @returns
Gas estimate for the transaction
.
*/
resendMessage
(
message
:
MessageLike
,
...
...
@@ -246,7 +331,7 @@ export interface ICrossChainMessenger {
* @param message Message to generate the finalization transaction for.
* @param opts Additional options.
* @param opts.overrides Optional transaction overrides.
* @returns
Transaction that can be signed and executed to finalize the message
.
* @returns
Gas estimate for the transaction
.
*/
finalizeMessage
(
message
:
MessageLike
,
...
...
@@ -262,7 +347,7 @@ export interface ICrossChainMessenger {
* @param opts Additional options.
* @param opts.l2GasLimit Optional gas limit to use for the transaction on L2.
* @param opts.overrides Optional transaction overrides.
* @returns
Transaction that can be signed and executed to deposit the ETH
.
* @returns
Gas estimate for the transaction
.
*/
depositETH
(
amount
:
NumberLike
,
...
...
@@ -278,7 +363,7 @@ export interface ICrossChainMessenger {
* @param amount Amount of ETH to withdraw.
* @param opts Additional options.
* @param opts.overrides Optional transaction overrides.
* @returns
Transaction that can be signed and executed to withdraw the tokens
.
* @returns
Gas estimate for the transaction
.
*/
withdrawETH
(
amount
:
NumberLike
,
...
...
@@ -286,5 +371,45 @@ export interface ICrossChainMessenger {
overrides
?:
Overrides
}
):
Promise
<
BigNumber
>
/**
* Estimates gas required to deposit some ERC20 tokens into the L2 chain.
*
* @param l1Token Address of the L1 token.
* @param l2Token Address of the L2 token.
* @param amount Amount to deposit.
* @param opts Additional options.
* @param opts.l2GasLimit Optional gas limit to use for the transaction on L2.
* @param opts.overrides Optional transaction overrides.
* @returns Gas estimate for the transaction.
*/
depositERC20
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
l2GasLimit
?:
NumberLike
overrides
?:
Overrides
}
):
Promise
<
BigNumber
>
/**
* Estimates gas required to withdraw some ERC20 tokens back to the L1 chain.
*
* @param l1Token Address of the L1 token.
* @param l2Token Address of the L2 token.
* @param amount Amount to withdraw.
* @param opts Additional options.
* @param opts.overrides Optional transaction overrides.
* @returns Gas estimate for the transaction.
*/
withdrawERC20
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
,
amount
:
NumberLike
,
opts
?:
{
overrides
?:
Overrides
}
):
Promise
<
BigNumber
>
}
}
packages/sdk/src/interfaces/cross-chain-provider.ts
View file @
40ffd24d
...
...
@@ -13,10 +13,11 @@ import {
TokenBridgeMessage
,
OEContracts
,
MessageReceipt
,
CustomBridges
,
StateRoot
,
StateRootBatch
,
BridgeAdapters
,
}
from
'
./types
'
import
{
IBridgeAdapter
}
from
'
./bridge-adapter
'
/**
* Represents the L1/L2 connection. Only handles read requests. If you want to send messages, use
...
...
@@ -46,7 +47,7 @@ export interface ICrossChainProvider {
/**
* List of custom bridges for the given network.
*/
bridges
:
CustomBridge
s
bridges
:
BridgeAdapter
s
/**
* Retrieves all cross chain messages sent within a given transaction.
...
...
@@ -87,6 +88,19 @@ export interface ICrossChainProvider {
}
):
Promise
<
CrossChainMessage
[]
>
/**
* Finds the appropriate bridge adapter for a given L1<>L2 token pair. Will throw if no bridges
* support the token pair or if more than one bridge supports the token pair.
*
* @param l1Token L1 token address.
* @param l2Token L2 token address.
* @returns The appropriate bridge adapter for the given token pair.
*/
getBridgeForTokenPair
(
l1Token
:
AddressLike
,
l2Token
:
AddressLike
):
Promise
<
IBridgeAdapter
>
/**
* 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
...
...
@@ -96,10 +110,6 @@ export interface ICrossChainProvider {
* @param opts Options object.
* @param opts.direction Direction to search for messages in. If not provided, will attempt to
* find all messages in both directions.
* @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 token bridge messages sent by the given address.
*/
getTokenBridgeMessagesByAddress
(
...
...
packages/sdk/src/interfaces/index.ts
View file @
40ffd24d
export
*
from
'
./
cross-chain-erc20-pai
r
'
export
*
from
'
./
bridge-adapte
r
'
export
*
from
'
./cross-chain-messenger
'
export
*
from
'
./cross-chain-provider
'
export
*
from
'
./l2-provider
'
...
...
packages/sdk/src/interfaces/types.ts
View file @
40ffd24d
...
...
@@ -6,6 +6,9 @@ import {
import
{
Signer
}
from
'
@ethersproject/abstract-signer
'
import
{
Contract
,
BigNumber
}
from
'
ethers
'
import
{
ICrossChainProvider
}
from
'
./cross-chain-provider
'
import
{
IBridgeAdapter
}
from
'
./bridge-adapter
'
/**
* L1 contract references.
*/
...
...
@@ -68,27 +71,25 @@ export interface OEContractsLike {
}
/**
*
Represents
list of custom bridges.
*
Something that looks like the
list of custom bridges.
*/
export
interface
CustomBridges
{
l1
:
{
[
name
:
string
]:
Contract
}
l2
:
{
[
name
:
string
]:
Contract
export
interface
BridgeAdapterData
{
[
name
:
string
]:
{
Adapter
:
new
(
opts
:
{
provider
:
ICrossChainProvider
l1Bridge
:
AddressLike
l2Bridge
:
AddressLike
})
=>
IBridgeAdapter
l1Bridge
:
AddressLike
l2Bridge
:
AddressLike
}
}
/**
* 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
}
export
interface
BridgeAdapters
{
[
name
:
string
]:
IBridgeAdapter
}
/**
...
...
packages/sdk/src/utils/contracts.ts
View file @
40ffd24d
...
...
@@ -10,9 +10,15 @@ import {
OEContractsLike
,
OEL2ContractsLike
,
AddressLike
,
CustomBridges
,
CustomBridgesLike
,
BridgeAdapters
,
BridgeAdapterData
,
ICrossChainProvider
,
}
from
'
../interfaces
'
import
{
StandardBridgeAdapter
,
ETHBridgeAdapter
,
DAIBridgeAdapter
,
}
from
'
../adapters
'
/**
* Full list of default L2 contract addresses.
...
...
@@ -39,42 +45,6 @@ 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
}
=
{
// TODO: Maybe we can pull these automatically from the token list?
// Alternatively, check against the token list in CI.
1
:
{
l1
:
{
SNX
:
'
0xCd9D4988C0AE61887B075bA77f08cbFAd2b65068
'
,
DAI
:
'
0x10E6593CDda8c58a1d0f14C5164B376352a55f2F
'
,
BitBTC
:
'
0xaBA2c5F108F7E820C049D5Af70B16ac266c8f128
'
,
},
l2
:
{
SNX
:
'
0x3f87Ff1de58128eF8FCb4c807eFD776E1aC72E51
'
,
DAI
:
'
0x467194771dAe2967Aef3ECbEDD3Bf9a310C76C65
'
,
BitBTC
:
'
0x158F513096923fF2d3aab2BcF4478536de6725e2
'
,
},
},
42
:
{
l1
:
{
SNX
:
'
0xD134Db47DDF5A6feB245452af17cCAf92ee53D3c
'
,
DAI
:
'
0xb415e822C4983ecD6B1c1596e8a5f976cf6CD9e3
'
,
BitBTC
:
'
0x0b651A42F32069d62d5ECf4f2a7e5Bd3E9438746
'
,
USX
:
'
0x40E862341b2416345F02c41Ac70df08525150dC7
'
,
},
l2
:
{
SNX
:
'
0x5C3f51CEd0C2F6157e2be67c029264D6C44bfe42
'
,
DAI
:
'
0x467194771dAe2967Aef3ECbEDD3Bf9a310C76C65
'
,
BitBTC
:
'
0x0CFb46528a7002a7D8877a5F7a69b9AaF1A9058e
'
,
USX
:
'
0xB4d37826b14Cd3CB7257A2A5094507d701fe715f
'
,
},
},
}
/**
* 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
...
...
@@ -135,6 +105,93 @@ export const CONTRACT_ADDRESSES: {
},
}
/**
* Mapping of L1 chain IDs to the list of custom bridge addresses for each chain.
*/
export
const
BRIDGE_ADAPTER_DATA
:
{
[
l1ChainId
:
number
]:
BridgeAdapterData
}
=
{
// TODO: Maybe we can pull these automatically from the token list?
// Alternatively, check against the token list in CI.
1
:
{
Standard
:
{
Adapter
:
StandardBridgeAdapter
,
l1Bridge
:
CONTRACT_ADDRESSES
[
1
].
l1
.
L1StandardBridge
,
l2Bridge
:
predeploys
.
L2StandardBridge
,
},
ETH
:
{
Adapter
:
ETHBridgeAdapter
,
l1Bridge
:
CONTRACT_ADDRESSES
[
1
].
l1
.
L1StandardBridge
,
l2Bridge
:
predeploys
.
L2StandardBridge
,
},
BitBTC
:
{
Adapter
:
StandardBridgeAdapter
,
l1Bridge
:
'
0xaBA2c5F108F7E820C049D5Af70B16ac266c8f128
'
,
l2Bridge
:
'
0x158F513096923fF2d3aab2BcF4478536de6725e2
'
,
},
DAI
:
{
Adapter
:
DAIBridgeAdapter
,
l1Bridge
:
'
0x10E6593CDda8c58a1d0f14C5164B376352a55f2F
'
,
l2Bridge
:
'
0x467194771dAe2967Aef3ECbEDD3Bf9a310C76C65
'
,
},
},
42
:
{
Standard
:
{
Adapter
:
StandardBridgeAdapter
,
l1Bridge
:
CONTRACT_ADDRESSES
[
42
].
l1
.
L1StandardBridge
,
l2Bridge
:
predeploys
.
L2StandardBridge
,
},
ETH
:
{
Adapter
:
ETHBridgeAdapter
,
l1Bridge
:
CONTRACT_ADDRESSES
[
42
].
l1
.
L1StandardBridge
,
l2Bridge
:
predeploys
.
L2StandardBridge
,
},
BitBTC
:
{
Adapter
:
StandardBridgeAdapter
,
l1Bridge
:
'
0x0b651A42F32069d62d5ECf4f2a7e5Bd3E9438746
'
,
l2Bridge
:
'
0x0CFb46528a7002a7D8877a5F7a69b9AaF1A9058e
'
,
},
USX
:
{
Adapter
:
StandardBridgeAdapter
,
l1Bridge
:
'
0x40E862341b2416345F02c41Ac70df08525150dC7
'
,
l2Bridge
:
'
0xB4d37826b14Cd3CB7257A2A5094507d701fe715f
'
,
},
DAI
:
{
Adapter
:
DAIBridgeAdapter
,
l1Bridge
:
'
0xb415e822C4983ecD6B1c1596e8a5f976cf6CD9e3
'
,
l2Bridge
:
'
0x467194771dAe2967Aef3ECbEDD3Bf9a310C76C65
'
,
},
},
5
:
{
Standard
:
{
Adapter
:
StandardBridgeAdapter
,
l1Bridge
:
CONTRACT_ADDRESSES
[
5
].
l1
.
L1StandardBridge
,
l2Bridge
:
predeploys
.
L2StandardBridge
,
},
ETH
:
{
Adapter
:
ETHBridgeAdapter
,
l1Bridge
:
CONTRACT_ADDRESSES
[
5
].
l1
.
L1StandardBridge
,
l2Bridge
:
predeploys
.
L2StandardBridge
,
},
},
}
// TODO: PR is big enough as-is, will add support for SNX in another PR
// MAINNET
// l1: {
// SNX: '0xCd9D4988C0AE61887B075bA77f08cbFAd2b65068',
// },
// l2: {
// SNX: '0x3f87Ff1de58128eF8FCb4c807eFD776E1aC72E51',
// },
// KOVAN
// l1: {
// SNX: '0xD134Db47DDF5A6feB245452af17cCAf92ee53D3c',
// },
// l2: {
// SNX: '0x5C3f51CEd0C2F6157e2be67c029264D6C44bfe42',
// },
/**
* Returns an ethers.Contract object for the given name, connected to the appropriate address for
* the given L1 chain ID. Users can also provide a custom address to connect the contract to
...
...
@@ -231,57 +288,32 @@ export const getAllOEContracts = (
}
/**
* Gets a series of
custom bridge
s for the given L1 chain ID.
* Gets a series of
bridge adapter
s for the given L1 chain ID.
*
* @param l1ChainId L1 chain ID for the L1 network where the custom bridges are deployed.
* @param provider Cross chain provider to connect to the bridge adapters
* @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.
* @param opts.overrides Custom bridge adapters.
* @returns An object containing all bridge adapters
*/
export
const
get
CustomBridge
s
=
(
export
const
get
BridgeAdapter
s
=
(
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
provider
:
ICrossChainProvider
,
opts
?:
{
overrides
?:
BridgeAdapterData
}
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
)
):
BridgeAdapters
=>
{
const
adapters
:
BridgeAdapters
=
{}
for
(
const
[
bridgeName
,
bridgeData
]
of
Object
.
entries
({
...(
BRIDGE_ADAPTER_DATA
[
l1ChainId
]
||
{}),
...(
opts
?.
overrides
||
{}),
}))
{
adapters
[
bridgeName
]
=
new
bridgeData
.
Adapter
({
provider
,
l1Bridge
:
bridgeData
.
l1Bridge
,
l2Bridge
:
bridgeData
.
l2Bridge
,
})
}
return
bridge
s
return
adapter
s
}
packages/sdk/test/cross-chain-erc20-pair.spec.ts
deleted
100644 → 0
View file @
137f7765
import
'
./setup
'
describe
(
'
CrossChainERC20Pair
'
,
()
=>
{
describe
(
'
construction
'
,
()
=>
{
it
(
'
should have a messenger
'
)
describe
(
'
when the token is a standard bridge token
'
,
()
=>
{
it
(
'
should resolve the correct bridge
'
)
})
describe
(
'
when the token is SNX
'
,
()
=>
{
it
(
'
should resolve the correct bridge
'
)
})
describe
(
'
when the token is DAI
'
,
()
=>
{
it
(
'
should resolve the correct bridge
'
)
})
describe
(
'
when a custom adapter is provided
'
,
()
=>
{
it
(
'
should use the custom adapter
'
)
})
})
describe
(
'
deposit
'
,
()
=>
{
describe
(
'
when the user has enough balance and allowance
'
,
()
=>
{
describe
(
'
when the token is a standard bridge token
'
,
()
=>
{
it
(
'
should trigger a token deposit
'
)
})
describe
(
'
when the token is ETH
'
,
()
=>
{
it
(
'
should trigger a token deposit
'
)
})
describe
(
'
when the token is SNX
'
,
()
=>
{
it
(
'
should trigger a token deposit
'
)
})
describe
(
'
when the token is DAI
'
,
()
=>
{
it
(
'
should trigger a token deposit
'
)
})
})
describe
(
'
when the user does not have enough balance
'
,
()
=>
{
it
(
'
should throw an error
'
)
})
describe
(
'
when the user has not given enough allowance to the bridge
'
,
()
=>
{
it
(
'
should throw an error
'
)
})
})
describe
(
'
withdraw
'
,
()
=>
{
describe
(
'
when the user has enough balance
'
,
()
=>
{
describe
(
'
when the token is a standard bridge token
'
,
()
=>
{
it
(
'
should trigger a token withdrawal
'
)
})
describe
(
'
when the token is ETH
'
,
()
=>
{
it
(
'
should trigger a token withdrawal
'
)
})
describe
(
'
when the token is SNX
'
,
()
=>
{
it
(
'
should trigger a token withdrawal
'
)
})
describe
(
'
when the token is DAI
'
,
()
=>
{
it
(
'
should trigger a token withdrawal
'
)
})
})
describe
(
'
when the user does not have enough balance
'
,
()
=>
{
it
(
'
should throw an error
'
)
})
})
describe
(
'
populateTransaction
'
,
()
=>
{
describe
(
'
deposit
'
,
()
=>
{
it
(
'
should populate the transaction with the correct values
'
)
})
describe
(
'
withdraw
'
,
()
=>
{
it
(
'
should populate the transaction with the correct values
'
)
})
})
describe
(
'
estimateGas
'
,
()
=>
{
describe
(
'
deposit
'
,
()
=>
{
it
(
'
should estimate gas required for the transaction
'
)
})
describe
(
'
withdraw
'
,
()
=>
{
it
(
'
should estimate gas required for the transaction
'
)
})
})
})
packages/sdk/test/cross-chain-messenger.spec.ts
View file @
40ffd24d
...
...
@@ -7,6 +7,7 @@ import {
CrossChainProvider
,
CrossChainMessenger
,
MessageDirection
,
ETHBridgeAdapter
,
}
from
'
../src
'
describe
(
'
CrossChainMessenger
'
,
()
=>
{
...
...
@@ -215,7 +216,9 @@ describe('CrossChainMessenger', () => {
describe
(
'
depositETH
'
,
()
=>
{
let
l1Messenger
:
Contract
let
l2Messenger
:
Contract
let
l1Bridge
:
Contract
let
l2Bridge
:
Contract
let
provider
:
CrossChainProvider
let
messenger
:
CrossChainMessenger
beforeEach
(
async
()
=>
{
...
...
@@ -225,6 +228,12 @@ describe('CrossChainMessenger', () => {
l1Bridge
=
(
await
(
await
ethers
.
getContractFactory
(
'
MockBridge
'
)
).
deploy
(
l1Messenger
.
address
))
as
any
l2Messenger
=
(
await
(
await
ethers
.
getContractFactory
(
'
MockMessenger
'
)
).
deploy
())
as
any
l2Bridge
=
(
await
(
await
ethers
.
getContractFactory
(
'
MockBridge
'
)
).
deploy
(
l2Messenger
.
address
))
as
any
provider
=
new
CrossChainProvider
({
l1Provider
:
ethers
.
provider
,
...
...
@@ -235,6 +244,17 @@ describe('CrossChainMessenger', () => {
L1CrossDomainMessenger
:
l1Messenger
.
address
,
L1StandardBridge
:
l1Bridge
.
address
,
},
l2
:
{
L2CrossDomainMessenger
:
l2Messenger
.
address
,
L2StandardBridge
:
l2Bridge
.
address
,
},
},
bridges
:
{
ETH
:
{
Adapter
:
ETHBridgeAdapter
,
l1Bridge
:
l1Bridge
.
address
,
l2Bridge
:
l2Bridge
.
address
,
},
},
})
...
...
@@ -258,11 +278,19 @@ describe('CrossChainMessenger', () => {
})
describe
(
'
withdrawETH
'
,
()
=>
{
let
l1Messenger
:
Contract
let
l2Messenger
:
Contract
let
l1Bridge
:
Contract
let
l2Bridge
:
Contract
let
provider
:
CrossChainProvider
let
messenger
:
CrossChainMessenger
beforeEach
(
async
()
=>
{
l1Messenger
=
(
await
(
await
ethers
.
getContractFactory
(
'
MockMessenger
'
)
).
deploy
())
as
any
l1Bridge
=
(
await
(
await
ethers
.
getContractFactory
(
'
MockBridge
'
)
).
deploy
(
l1Messenger
.
address
))
as
any
l2Messenger
=
(
await
(
await
ethers
.
getContractFactory
(
'
MockMessenger
'
)
).
deploy
())
as
any
...
...
@@ -275,11 +303,22 @@ describe('CrossChainMessenger', () => {
l2Provider
:
ethers
.
provider
,
l1ChainId
:
31337
,
contracts
:
{
l1
:
{
L1CrossDomainMessenger
:
l1Messenger
.
address
,
L1StandardBridge
:
l1Bridge
.
address
,
},
l2
:
{
L2CrossDomainMessenger
:
l2Messenger
.
address
,
L2StandardBridge
:
l2Bridge
.
address
,
},
},
bridges
:
{
ETH
:
{
Adapter
:
ETHBridgeAdapter
,
l1Bridge
:
l1Bridge
.
address
,
l2Bridge
:
l2Bridge
.
address
,
},
},
})
messenger
=
new
CrossChainMessenger
({
...
...
packages/sdk/test/cross-chain-provider.spec.ts
View file @
40ffd24d
...
...
@@ -12,6 +12,7 @@ import {
omit
,
MessageStatus
,
CrossChainMessage
,
StandardBridgeAdapter
,
}
from
'
../src
'
import
{
DUMMY_MESSAGE
}
from
'
./helpers
'
...
...
@@ -434,6 +435,13 @@ describe('CrossChainProvider', () => {
L2StandardBridge
:
l2Bridge
.
address
,
},
},
bridges
:
{
Standard
:
{
Adapter
:
StandardBridgeAdapter
,
l1Bridge
:
l1Bridge
.
address
,
l2Bridge
:
l2Bridge
.
address
,
},
},
})
})
...
...
@@ -566,71 +574,6 @@ describe('CrossChainProvider', () => {
expect
(
found
[
1
].
to
).
to
.
deep
.
equal
(
withdrawal
.
to
)
})
})
describe
(
'
when a block range is specified
'
,
()
=>
{
describe
(
'
when a direction is specified
'
,
()
=>
{
it
(
'
should find all deposits or withdrawals only in the given direction and within the block range
'
,
async
()
=>
{
const
from
=
'
0x
'
+
'
99
'
.
repeat
(
20
)
const
deposit1
=
{
l1Token
:
'
0x
'
+
'
11
'
.
repeat
(
20
),
l2Token
:
'
0x
'
+
'
22
'
.
repeat
(
20
),
from
,
to
:
'
0x
'
+
'
44
'
.
repeat
(
20
),
amount
:
ethers
.
BigNumber
.
from
(
1234
),
data
:
'
0x1234
'
,
}
const
deposit2
=
{
l1Token
:
'
0x
'
+
'
33
'
.
repeat
(
20
),
l2Token
:
'
0x
'
+
'
44
'
.
repeat
(
20
),
from
,
to
:
'
0x
'
+
'
55
'
.
repeat
(
20
),
amount
:
ethers
.
BigNumber
.
from
(
1234
),
data
:
'
0x1234
'
,
}
const
withdrawal
=
{
l1Token
:
'
0x
'
+
'
12
'
.
repeat
(
20
),
l2Token
:
'
0x
'
+
'
23
'
.
repeat
(
20
),
from
,
to
:
'
0x
'
+
'
45
'
.
repeat
(
20
),
amount
:
ethers
.
BigNumber
.
from
(
5678
),
data
:
'
0x5678
'
,
}
await
l1Bridge
.
emitERC20DepositInitiated
(
deposit1
)
const
tx
=
await
l1Bridge
.
emitERC20DepositInitiated
(
deposit2
)
await
l2Bridge
.
emitWithdrawalInitiated
(
withdrawal
)
const
found
=
await
provider
.
getTokenBridgeMessagesByAddress
(
from
,
{
direction
:
MessageDirection
.
L1_TO_L2
,
fromBlock
:
tx
.
blockNumber
,
})
expect
(
found
.
length
).
to
.
equal
(
1
)
expect
(
found
[
0
].
amount
).
to
.
deep
.
equal
(
deposit2
.
amount
)
expect
(
found
[
0
].
data
).
to
.
deep
.
equal
(
deposit2
.
data
)
expect
(
found
[
0
].
direction
).
to
.
equal
(
MessageDirection
.
L1_TO_L2
)
expect
(
found
[
0
].
l1Token
).
to
.
deep
.
equal
(
deposit2
.
l1Token
)
expect
(
found
[
0
].
l2Token
).
to
.
deep
.
equal
(
deposit2
.
l2Token
)
expect
(
found
[
0
].
from
).
to
.
deep
.
equal
(
deposit2
.
from
)
expect
(
found
[
0
].
to
).
to
.
deep
.
equal
(
deposit2
.
to
)
})
})
describe
(
'
when a direction is not specified
'
,
()
=>
{
it
(
'
should throw an error
'
,
async
()
=>
{
const
from
=
'
0x
'
+
'
99
'
.
repeat
(
20
)
await
expect
(
provider
.
getTokenBridgeMessagesByAddress
(
from
,
{
fromBlock
:
0
,
toBlock
:
100
,
})
).
to
.
be
.
rejectedWith
(
'
direction must be specified
'
)
})
})
})
})
describe
(
'
when the address has not made any deposits or withdrawals
'
,
()
=>
{
...
...
@@ -676,6 +619,13 @@ describe('CrossChainProvider', () => {
L2StandardBridge
:
l2Bridge
.
address
,
},
},
bridges
:
{
Standard
:
{
Adapter
:
StandardBridgeAdapter
,
l1Bridge
:
l1Bridge
.
address
,
l2Bridge
:
l2Bridge
.
address
,
},
},
})
})
...
...
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