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
0bc53656
Unverified
Commit
0bc53656
authored
Feb 01, 2022
by
Matthew Slipper
Committed by
GitHub
Feb 01, 2022
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into feat/bring-back-unsupported-rpcs
parents
81d90563
40ffd24d
Changes
41
Hide whitespace changes
Inline
Side-by-side
Showing
41 changed files
with
1556 additions
and
473 deletions
+1556
-473
afraid-otters-repeat.md
.changeset/afraid-otters-repeat.md
+5
-0
cool-starfishes-peel.md
.changeset/cool-starfishes-peel.md
+5
-0
famous-wombats-exercise.md
.changeset/famous-wombats-exercise.md
+5
-0
forty-dancers-try.md
.changeset/forty-dancers-try.md
+5
-0
green-donuts-bathe.md
.changeset/green-donuts-bathe.md
+5
-0
quick-drinks-tease.md
.changeset/quick-drinks-tease.md
+5
-0
smooth-bags-applaud.md
.changeset/smooth-bags-applaud.md
+5
-0
.dockerignore
.dockerignore
+2
-0
FakeL2StandardERC20.sol
integration-tests/contracts/FakeL2StandardERC20.sol
+14
-0
bridged-tokens.spec.ts
integration-tests/test/bridged-tokens.spec.ts
+54
-0
env.ts
integration-tests/test/shared/env.ts
+4
-0
utils.ts
integration-tests/test/shared/utils.ts
+11
-1
verifier.spec.ts
integration-tests/test/verifier.spec.ts
+103
-0
worker.go
l2geth/miner/worker.go
+1
-12
sync_service.go
l2geth/rollup/sync_service.go
+3
-2
docker-compose.yml
ops/docker-compose.yml
+3
-1
Dockerfile.batch-submitter-service
ops/docker/Dockerfile.batch-submitter-service
+2
-2
dtl.env
ops/envs/dtl.env
+1
-0
transport-db.ts
packages/data-transport-layer/src/db/transport-db.ts
+43
-41
sequencer-batch-appended.ts
...ervices/l1-ingestion/handlers/sequencer-batch-appended.ts
+1
-1
service.ts
...data-transport-layer/src/services/l1-ingestion/service.ts
+3
-1
service.ts
...data-transport-layer/src/services/l2-ingestion/service.ts
+3
-1
service.ts
packages/data-transport-layer/src/services/main/service.ts
+1
-0
run.ts
packages/data-transport-layer/src/services/run.ts
+1
-0
service.ts
packages/data-transport-layer/src/services/server/service.ts
+4
-1
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.
.changeset/afraid-otters-repeat.md
0 → 100644
View file @
0bc53656
---
'
@eth-optimism/integration-tests'
:
patch
---
Increase withdrawal test timeout
.changeset/cool-starfishes-peel.md
0 → 100644
View file @
0bc53656
---
'
@eth-optimism/data-transport-layer'
:
patch
---
Updates DTL to correctly parse L1 to L2 tx timestamps after the first bss hardfork
.changeset/famous-wombats-exercise.md
0 → 100644
View file @
0bc53656
---
'
@eth-optimism/integration-tests'
:
patch
---
Add an integration test showing the infeasability of withdrawing a fake token in exchange for a legitimate token.
.changeset/forty-dancers-try.md
0 → 100644
View file @
0bc53656
---
'
@eth-optimism/integration-tests'
:
patch
---
Updates integration tests to include a test for syncing a Verifier from L1
.changeset/green-donuts-bathe.md
0 → 100644
View file @
0bc53656
---
'
@eth-optimism/l2geth'
:
patch
---
Fixes incorrect timestamp handling for L1 syncing verifiers
.changeset/quick-drinks-tease.md
0 → 100644
View file @
0bc53656
---
'
@eth-optimism/batch-submitter'
:
patch
---
Updates batch submitter to also include separate timestamps for deposit transactions"
.changeset/smooth-bags-applaud.md
0 → 100644
View file @
0bc53656
---
'
@eth-optimism/integration-tests'
:
patch
---
Add verifier integration tests
.dockerignore
View file @
0bc53656
.github
node_modules
.env
**/.env
test
**/*_test.go
...
...
integration-tests/contracts/FakeL2StandardERC20.sol
0 → 100644
View file @
0bc53656
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
contract FakeL2StandardERC20 {
address public immutable l1Token;
constructor(address _l1Token) {
l1Token = _l1Token;
}
// Burn will be called by the L2 Bridge to burn the tokens we are bridging to L1
function burn(address, uint256) external {}
}
integration-tests/test/bridged-tokens.spec.ts
View file @
0bc53656
...
...
@@ -127,4 +127,58 @@ describe('Bridged tokens', () => {
)
}
)
// This test demonstrates that an apparent withdrawal bug is in fact non-existent.
// Specifically, the L2 bridge does not check that the L2 token being burned corresponds
// with the L1 token which is specified for the withdrawal.
withdrawalTest
(
'
should not allow an arbitrary L2 token to be withdrawn in exchange for a legitimate L1 token
'
,
async
()
=>
{
before
(
async
()
=>
{
// First deposit some of the L1 token to L2, so that there is something which could be stolen.
const
depositTx
=
await
env
.
l1Bridge
.
connect
(
env
.
l1Wallet
)
.
depositERC20
(
L1__ERC20
.
address
,
L2__ERC20
.
address
,
1000
,
2000000
,
'
0x
'
)
await
env
.
waitForXDomainTransaction
(
depositTx
,
Direction
.
L1ToL2
)
expect
(
await
L2__ERC20
.
balanceOf
(
env
.
l2Wallet
.
address
)).
to
.
deep
.
equal
(
BigNumber
.
from
(
1000
)
)
})
// Deploy a Fake L2 token, which:
// - returns the address of a legitimate L1 token from its l1Token() getter.
// - allows the L2 bridge to call its burn() function.
const
fakeToken
=
await
(
await
ethers
.
getContractFactory
(
'
FakeL2StandardERC20
'
,
env
.
l2Wallet
)
).
deploy
(
L1__ERC20
.
address
)
await
fakeToken
.
deployed
()
const
balBefore
=
await
L1__ERC20
.
balanceOf
(
otherWalletL1
.
address
)
// Withdraw some of the Fake L2 token, hoping to receive the same amount of the legitimate
// token on L1.
const
withdrawalTx
=
await
env
.
l2Bridge
.
connect
(
otherWalletL2
)
.
withdrawTo
(
fakeToken
.
address
,
otherWalletL1
.
address
,
500
,
1
_000_000
,
'
0x
'
)
await
env
.
relayXDomainMessages
(
withdrawalTx
)
await
env
.
waitForXDomainTransaction
(
withdrawalTx
,
Direction
.
L2ToL1
)
// Ensure that the L1 recipient address has not received any additional L1 token balance.
expect
(
await
L1__ERC20
.
balanceOf
(
otherWalletL1
.
address
)).
to
.
deep
.
equal
(
balBefore
)
}
)
})
integration-tests/test/shared/env.ts
View file @
0bc53656
...
...
@@ -11,6 +11,7 @@ import {
l1Provider
,
l2Provider
,
replicaProvider
,
verifierProvider
,
l1Wallet
,
l2Wallet
,
gasPriceOracleWallet
,
...
...
@@ -57,6 +58,7 @@ export class OptimismEnv {
l1Provider
:
providers
.
JsonRpcProvider
l2Provider
:
providers
.
JsonRpcProvider
replicaProvider
:
providers
.
JsonRpcProvider
verifierProvider
:
providers
.
JsonRpcProvider
constructor
(
args
:
any
)
{
this
.
addressManager
=
args
.
addressManager
...
...
@@ -74,6 +76,7 @@ export class OptimismEnv {
this
.
l1Provider
=
args
.
l1Provider
this
.
l2Provider
=
args
.
l2Provider
this
.
replicaProvider
=
args
.
replicaProvider
this
.
verifierProvider
=
args
.
verifierProvider
this
.
ctc
=
args
.
ctc
this
.
scc
=
args
.
scc
}
...
...
@@ -140,6 +143,7 @@ export class OptimismEnv {
l2Wallet
,
l1Provider
,
l2Provider
,
verifierProvider
,
replicaProvider
,
})
}
...
...
integration-tests/test/shared/utils.ts
View file @
0bc53656
...
...
@@ -62,6 +62,8 @@ const procEnv = cleanEnv(process.env, {
REPLICA_URL
:
str
({
default
:
'
http://localhost:8549
'
}),
REPLICA_POLLING_INTERVAL
:
num
({
default
:
10
}),
VERIFIER_URL
:
str
({
default
:
'
http://localhost:8547
'
}),
PRIVATE_KEY
:
str
({
default
:
'
0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
'
,
...
...
@@ -96,6 +98,9 @@ const procEnv = cleanEnv(process.env, {
RUN_NIGHTLY_TESTS
:
bool
({
default
:
false
,
}),
RUN_VERIFIER_TESTS
:
bool
({
default
:
true
,
}),
MOCHA_TIMEOUT
:
num
({
default
:
120
_000
,
...
...
@@ -121,6 +126,11 @@ export const replicaProvider = injectL2Context(
)
replicaProvider
.
pollingInterval
=
procEnv
.
REPLICA_POLLING_INTERVAL
export
const
verifierProvider
=
injectL2Context
(
new
providers
.
JsonRpcProvider
(
procEnv
.
VERIFIER_URL
)
)
verifierProvider
.
pollingInterval
=
procEnv
.
L2_POLLING_INTERVAL
// The sequencer private key which is funded on L1
export
const
l1Wallet
=
new
Wallet
(
procEnv
.
PRIVATE_KEY
,
l1Provider
)
...
...
@@ -210,7 +220,7 @@ export const conditionalTest = (
}
await
fn
()
}).
timeout
(
timeout
||
envConfig
.
MOCHA_TIMEOUT
)
}).
timeout
(
timeout
||
envConfig
.
MOCHA_TIMEOUT
*
2
)
}
export
const
withdrawalTest
=
(
name
,
fn
,
timeout
?:
number
)
=>
...
...
integration-tests/test/verifier.spec.ts
0 → 100644
View file @
0bc53656
import
{
TransactionReceipt
}
from
'
@ethersproject/abstract-provider
'
import
{
expect
}
from
'
./shared/setup
'
import
{
OptimismEnv
}
from
'
./shared/env
'
import
{
defaultTransactionFactory
,
gasPriceForL2
,
sleep
,
envConfig
,
}
from
'
./shared/utils
'
describe
(
'
Verifier Tests
'
,
()
=>
{
let
env
:
OptimismEnv
before
(
async
function
()
{
if
(
!
envConfig
.
RUN_VERIFIER_TESTS
)
{
this
.
skip
()
return
}
env
=
await
OptimismEnv
.
new
()
})
describe
(
'
Matching blocks
'
,
()
=>
{
it
(
'
should sync a transaction
'
,
async
()
=>
{
const
tx
=
defaultTransactionFactory
()
tx
.
gasPrice
=
await
gasPriceForL2
()
const
result
=
await
env
.
l2Wallet
.
sendTransaction
(
tx
)
let
receipt
:
TransactionReceipt
while
(
!
receipt
)
{
receipt
=
await
env
.
verifierProvider
.
getTransactionReceipt
(
result
.
hash
)
await
sleep
(
200
)
}
const
sequencerBlock
=
(
await
env
.
l2Provider
.
getBlock
(
result
.
blockNumber
))
as
any
const
verifierBlock
=
(
await
env
.
verifierProvider
.
getBlock
(
result
.
blockNumber
))
as
any
expect
(
sequencerBlock
.
stateRoot
).
to
.
deep
.
eq
(
verifierBlock
.
stateRoot
)
expect
(
sequencerBlock
.
hash
).
to
.
deep
.
eq
(
verifierBlock
.
hash
)
})
it
(
'
sync an unprotected tx (eip155)
'
,
async
()
=>
{
const
tx
=
{
...
defaultTransactionFactory
(),
nonce
:
await
env
.
l2Wallet
.
getTransactionCount
(),
gasPrice
:
await
gasPriceForL2
(),
chainId
:
null
,
// Disables EIP155 transaction signing.
}
const
signed
=
await
env
.
l2Wallet
.
signTransaction
(
tx
)
const
result
=
await
env
.
l2Provider
.
sendTransaction
(
signed
)
let
receipt
:
TransactionReceipt
while
(
!
receipt
)
{
receipt
=
await
env
.
verifierProvider
.
getTransactionReceipt
(
result
.
hash
)
await
sleep
(
200
)
}
const
sequencerBlock
=
(
await
env
.
l2Provider
.
getBlock
(
result
.
blockNumber
))
as
any
const
verifierBlock
=
(
await
env
.
verifierProvider
.
getBlock
(
result
.
blockNumber
))
as
any
expect
(
sequencerBlock
.
stateRoot
).
to
.
deep
.
eq
(
verifierBlock
.
stateRoot
)
expect
(
sequencerBlock
.
hash
).
to
.
deep
.
eq
(
verifierBlock
.
hash
)
})
it
(
'
should forward tx to sequencer
'
,
async
()
=>
{
const
tx
=
{
...
defaultTransactionFactory
(),
nonce
:
await
env
.
l2Wallet
.
getTransactionCount
(),
gasPrice
:
await
gasPriceForL2
(),
}
const
signed
=
await
env
.
l2Wallet
.
signTransaction
(
tx
)
const
result
=
await
env
.
verifierProvider
.
sendTransaction
(
signed
)
let
receipt
:
TransactionReceipt
while
(
!
receipt
)
{
receipt
=
await
env
.
verifierProvider
.
getTransactionReceipt
(
result
.
hash
)
await
sleep
(
200
)
}
const
sequencerBlock
=
(
await
env
.
l2Provider
.
getBlock
(
result
.
blockNumber
))
as
any
const
verifierBlock
=
(
await
env
.
verifierProvider
.
getBlock
(
result
.
blockNumber
))
as
any
expect
(
sequencerBlock
.
stateRoot
).
to
.
deep
.
eq
(
verifierBlock
.
stateRoot
)
expect
(
sequencerBlock
.
hash
).
to
.
deep
.
eq
(
verifierBlock
.
hash
)
})
})
})
l2geth/miner/worker.go
View file @
0bc53656
...
...
@@ -931,18 +931,7 @@ func (w *worker) commitNewTx(tx *types.Transaction) error {
// Preserve liveliness as best as possible. Must panic on L1 to L2
// transactions as the timestamp cannot be malleated
if
parent
.
Time
()
>
tx
.
L1Timestamp
()
{
log
.
Error
(
"Monotonicity violation"
,
"index"
,
num
)
if
tx
.
QueueOrigin
()
==
types
.
QueueOriginSequencer
{
tx
.
SetL1Timestamp
(
parent
.
Time
())
prev
:=
parent
.
Transactions
()
if
len
(
prev
)
==
1
{
tx
.
SetL1BlockNumber
(
prev
[
0
]
.
L1BlockNumber
()
.
Uint64
())
}
else
{
log
.
Error
(
"Cannot recover L1 Blocknumber"
)
}
}
else
{
log
.
Error
(
"Cannot recover from monotonicity violation"
)
}
log
.
Error
(
"Monotonicity violation"
,
"index"
,
num
,
"parent"
,
parent
.
Time
(),
"tx"
,
tx
.
L1Timestamp
())
}
// Fill in the index field in the tx meta if it is `nil`.
...
...
l2geth/rollup/sync_service.go
View file @
0bc53656
...
...
@@ -825,13 +825,14 @@ func (s *SyncService) applyTransactionToTip(tx *types.Transaction) error {
if
now
.
Sub
(
current
)
>
s
.
timestampRefreshThreshold
{
current
=
now
}
log
.
Info
(
"Updating latest timestamp"
,
"timestamp"
,
current
,
"unix"
,
current
.
Unix
())
tx
.
SetL1Timestamp
(
uint64
(
current
.
Unix
()))
}
else
if
tx
.
L1Timestamp
()
==
0
&&
s
.
verifier
{
// This should never happen
log
.
Error
(
"No tx timestamp found when running as verifier"
,
"hash"
,
tx
.
Hash
()
.
Hex
())
}
else
if
tx
.
L1Timestamp
()
<
s
.
GetLatestL1Timestamp
()
{
}
else
if
tx
.
L1Timestamp
()
<
ts
{
// This should never happen, but sometimes does
log
.
Error
(
"Timestamp monotonicity violation"
,
"hash"
,
tx
.
Hash
()
.
Hex
())
log
.
Error
(
"Timestamp monotonicity violation"
,
"hash"
,
tx
.
Hash
()
.
Hex
()
,
"latest"
,
ts
,
"tx"
,
tx
.
L1Timestamp
()
)
}
l1BlockNumber
:=
tx
.
L1BlockNumber
()
...
...
ops/docker-compose.yml
View file @
0bc53656
...
...
@@ -136,8 +136,9 @@ services:
-
l1_chain
-
deployer
-
dtl
-
l2geth
deploy
:
replicas
:
0
replicas
:
1
build
:
context
:
..
dockerfile
:
./ops/docker/Dockerfile.geth
...
...
@@ -146,6 +147,7 @@ services:
-
./envs/geth.env
environment
:
ETH1_HTTP
:
http://l1_chain:8545
SEQUENCER_CLIENT_HTTP
:
http://l2geth:8545
ROLLUP_STATE_DUMP_PATH
:
http://deployer:8081/state-dump.latest.json
ROLLUP_CLIENT_HTTP
:
http://dtl:7878
ROLLUP_BACKEND
:
'
l1'
...
...
ops/docker/Dockerfile.batch-submitter-service
View file @
0bc53656
FROM golang:1.17.
6-alpine3.15
as builder
FROM golang:1.17.
3-alpine3.13
as builder
RUN apk add --no-cache make gcc musl-dev linux-headers git jq bash
...
...
@@ -9,7 +9,7 @@ RUN go mod graph | grep -v l2geth | awk '{if ($1 !~ "@") print $2}' | xargs -n 1
COPY ./go/batch-submitter/ ./
RUN make
FROM alpine:3.1
5
FROM alpine:3.1
3
RUN apk add --no-cache ca-certificates jq curl
COPY --from=builder /go/batch-submitter/batch-submitter /usr/local/bin/
...
...
ops/envs/dtl.env
View file @
0bc53656
...
...
@@ -9,6 +9,7 @@ DATA_TRANSPORT_LAYER__LOGS_PER_POLLING_INTERVAL=2000
DATA_TRANSPORT_LAYER__DANGEROUSLY_CATCH_ALL_ERRORS=true
DATA_TRANSPORT_LAYER__SERVER_HOSTNAME=0.0.0.0
DATA_TRANSPORT_LAYER__L1_START_HEIGHT=1
DATA_TRANSPORT_LAYER__BSS_HARDFORK_1_INDEX=0
DATA_TRANSPORT_LAYER__ADDRESS_MANAGER=
DATA_TRANSPORT_LAYER__L1_RPC_ENDPOINT=
...
...
packages/data-transport-layer/src/db/transport-db.ts
View file @
0bc53656
...
...
@@ -31,11 +31,17 @@ interface Indexed {
index
:
number
}
interface
ExtraTransportDBOptions
{
bssHardfork1Index
?:
number
}
export
class
TransportDB
{
public
db
:
SimpleDB
public
opts
:
ExtraTransportDBOptions
constructor
(
leveldb
:
LevelUp
)
{
constructor
(
leveldb
:
LevelUp
,
opts
?:
ExtraTransportDBOptions
)
{
this
.
db
=
new
SimpleDB
(
leveldb
)
this
.
opts
=
opts
||
{}
}
public
async
putEnqueueEntries
(
entries
:
EnqueueEntry
[]):
Promise
<
void
>
{
...
...
@@ -254,26 +260,7 @@ export class TransportDB {
return
null
}
if
(
transaction
.
queueOrigin
===
'
l1
'
)
{
const
enqueue
=
await
this
.
getEnqueueByIndex
(
transaction
.
queueIndex
)
if
(
enqueue
===
null
)
{
return
null
}
return
{
...
transaction
,
...{
blockNumber
:
enqueue
.
blockNumber
,
timestamp
:
enqueue
.
timestamp
,
gasLimit
:
enqueue
.
gasLimit
,
target
:
enqueue
.
target
,
origin
:
enqueue
.
origin
,
data
:
enqueue
.
data
,
},
}
}
else
{
return
transaction
}
return
this
.
_makeFullTransaction
(
transaction
)
}
public
async
getLatestFullTransaction
():
Promise
<
TransactionEntry
>
{
...
...
@@ -293,31 +280,46 @@ export class TransportDB {
const
fullTransactions
=
[]
for
(
const
transaction
of
transactions
)
{
if
(
transaction
.
queueOrigin
===
'
l1
'
)
{
const
enqueue
=
await
this
.
getEnqueueByIndex
(
transaction
.
queueIndex
)
if
(
enqueue
===
null
)
{
return
null
}
fullTransactions
.
push
({
...
transaction
,
...{
blockNumber
:
enqueue
.
blockNumber
,
timestamp
:
enqueue
.
timestamp
,
gasLimit
:
enqueue
.
gasLimit
,
target
:
enqueue
.
target
,
origin
:
enqueue
.
origin
,
data
:
enqueue
.
data
,
},
})
}
else
{
fullTransactions
.
push
(
transaction
)
}
fullTransactions
.
push
(
await
this
.
_makeFullTransaction
(
transaction
))
}
return
fullTransactions
}
private
async
_makeFullTransaction
(
transaction
:
TransactionEntry
):
Promise
<
TransactionEntry
>
{
// We only need to do extra work for L1 to L2 transactions.
if
(
transaction
.
queueOrigin
!==
'
l1
'
)
{
return
transaction
}
const
enqueue
=
await
this
.
getEnqueueByIndex
(
transaction
.
queueIndex
)
if
(
enqueue
===
null
)
{
return
null
}
let
timestamp
=
enqueue
.
timestamp
if
(
typeof
this
.
opts
.
bssHardfork1Index
===
'
number
'
&&
transaction
.
index
>=
this
.
opts
.
bssHardfork1Index
)
{
timestamp
=
transaction
.
timestamp
}
return
{
...
transaction
,
...{
blockNumber
:
enqueue
.
blockNumber
,
timestamp
,
gasLimit
:
enqueue
.
gasLimit
,
target
:
enqueue
.
target
,
origin
:
enqueue
.
origin
,
data
:
enqueue
.
data
,
},
}
}
private
async
_getLatestEntryIndex
(
key
:
string
):
Promise
<
number
>
{
return
this
.
db
.
get
<
number
>
(
`
${
key
}
:latest`
,
0
)
||
0
}
...
...
packages/data-transport-layer/src/services/l1-ingestion/handlers/sequencer-batch-appended.ts
View file @
0bc53656
...
...
@@ -143,7 +143,7 @@ export const handleEventsSequencerBatchAppended: EventHandlerSet<
.
toNumber
(),
batchIndex
:
extraData
.
batchIndex
.
toNumber
(),
blockNumber
:
BigNumber
.
from
(
0
).
toNumber
(),
timestamp
:
BigNumber
.
from
(
0
).
toNumber
()
,
timestamp
:
context
.
timestamp
,
gasLimit
:
BigNumber
.
from
(
0
).
toString
(),
target
:
constants
.
AddressZero
,
origin
:
constants
.
AddressZero
,
...
...
packages/data-transport-layer/src/services/l1-ingestion/service.ts
View file @
0bc53656
...
...
@@ -104,7 +104,9 @@ export class L1IngestionService extends BaseService<L1IngestionServiceOptions> {
}
=
{}
as
any
protected
async
_init
():
Promise
<
void
>
{
this
.
state
.
db
=
new
TransportDB
(
this
.
options
.
db
)
this
.
state
.
db
=
new
TransportDB
(
this
.
options
.
db
,
{
bssHardfork1Index
:
this
.
options
.
bssHardfork1Index
,
})
this
.
l1IngestionMetrics
=
registerMetrics
(
this
.
metrics
)
...
...
packages/data-transport-layer/src/services/l2-ingestion/service.ts
View file @
0bc53656
...
...
@@ -84,7 +84,9 @@ export class L2IngestionService extends BaseService<L2IngestionServiceOptions> {
this
.
l2IngestionMetrics
=
registerMetrics
(
this
.
metrics
)
this
.
state
.
db
=
new
TransportDB
(
this
.
options
.
db
)
this
.
state
.
db
=
new
TransportDB
(
this
.
options
.
db
,
{
bssHardfork1Index
:
this
.
options
.
bssHardfork1Index
,
})
this
.
state
.
l2RpcProvider
=
typeof
this
.
options
.
l2RpcProvider
===
'
string
'
...
...
packages/data-transport-layer/src/services/main/service.ts
View file @
0bc53656
...
...
@@ -36,6 +36,7 @@ export interface L1DataTransportServiceOptions {
defaultBackend
:
string
l1GasPriceBackend
:
string
l1StartHeight
?:
number
bssHardfork1Index
?:
number
}
const
optionSettings
=
{
...
...
packages/data-transport-layer/src/services/run.ts
View file @
0bc53656
...
...
@@ -51,6 +51,7 @@ type ethNetwork = 'mainnet' | 'kovan' | 'goerli'
useSentry
:
config
.
bool
(
'
use-sentry
'
,
false
),
sentryDsn
:
config
.
str
(
'
sentry-dsn
'
),
sentryTraceRate
:
config
.
ufloat
(
'
sentry-trace-rate
'
,
0.05
),
bssHardfork1Index
:
config
.
uint
(
'
bss-hardfork-1-index
'
,
null
),
})
const
stop
=
async
(
signal
)
=>
{
...
...
packages/data-transport-layer/src/services/server/service.ts
View file @
0bc53656
...
...
@@ -87,7 +87,10 @@ export class L1TransportServer extends BaseService<L1TransportServerOptions> {
await
this
.
options
.
db
.
open
()
}
this
.
state
.
db
=
new
TransportDB
(
this
.
options
.
db
)
this
.
state
.
db
=
new
TransportDB
(
this
.
options
.
db
,
{
bssHardfork1Index
:
this
.
options
.
bssHardfork1Index
,
})
this
.
state
.
l1RpcProvider
=
typeof
this
.
options
.
l1RpcProvider
===
'
string
'
?
new
JsonRpcProvider
(
this
.
options
.
l1RpcProvider
)
...
...
packages/sdk/src/adapters/dai-bridge.ts
0 → 100644
View file @
0bc53656
/* 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 @
0bc53656
/* 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 @
0bc53656
export
*
from
'
./standard-bridge
'
export
*
from
'
./eth-bridge
'
export
*
from
'
./dai-bridge
'
packages/sdk/src/adapters/standard-bridge.ts
0 → 100644
View file @
0bc53656
/* 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 @
0bc53656
/* 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 @
0bc53656
...
...
@@ -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 @
0bc53656
...
...
@@ -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 @
0bc53656
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 @
0bc53656
...
...
@@ -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 @
0bc53656
...
...
@@ -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 @
0bc53656
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 @
0bc53656
...
...
@@ -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 @
0bc53656
...
...
@@ -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 @
81d90563
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 @
0bc53656
...
...
@@ -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 @
0bc53656
...
...
@@ -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