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
c9123af4
Unverified
Commit
c9123af4
authored
Jan 07, 2022
by
smartcontracts
Committed by
GitHub
Jan 07, 2022
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1979 from ethereum-optimism/sc/sdk-wait-for-receipt
feat(sdk): implement waitForMessageReceipt
parents
9195dcf2
0b6eca57
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
172 additions
and
67 deletions
+172
-67
cross-chain-provider.ts
packages/sdk/src/cross-chain-provider.ts
+17
-4
cross-chain-provider.ts
packages/sdk/src/interfaces/cross-chain-provider.ts
+1
-1
cross-chain-erc20-pair.spec.ts
packages/sdk/test/cross-chain-erc20-pair.spec.ts
+20
-21
cross-chain-messenger.spec.ts
packages/sdk/test/cross-chain-messenger.spec.ts
+8
-9
cross-chain-provider.spec.ts
packages/sdk/test/cross-chain-provider.spec.ts
+126
-32
No files found.
packages/sdk/src/cross-chain-provider.ts
View file @
c9123af4
...
...
@@ -5,6 +5,7 @@ import {
TransactionReceipt
,
}
from
'
@ethersproject/abstract-provider
'
import
{
ethers
,
BigNumber
,
Event
}
from
'
ethers
'
import
{
sleep
}
from
'
@eth-optimism/core-utils
'
import
{
ICrossChainProvider
,
OEContracts
,
...
...
@@ -395,15 +396,27 @@ export class CrossChainProvider implements ICrossChainProvider {
return
null
}
public
async
waitForMessageRec
ie
pt
(
public
async
waitForMessageRec
ei
pt
(
message
:
MessageLike
,
opts
?
:
{
opts
:
{
confirmations
?:
number
pollIntervalMs
?:
number
timeoutMs
?:
number
}
}
=
{}
):
Promise
<
MessageReceipt
>
{
throw
new
Error
(
'
Not implemented
'
)
let
totalTimeMs
=
0
while
(
totalTimeMs
<
(
opts
.
timeoutMs
||
Infinity
))
{
const
tick
=
Date
.
now
()
const
receipt
=
await
this
.
getMessageReceipt
(
message
)
if
(
receipt
!==
null
)
{
return
receipt
}
else
{
await
sleep
(
opts
.
pollIntervalMs
||
4000
)
totalTimeMs
+=
Date
.
now
()
-
tick
}
}
throw
new
Error
(
`timed out waiting for message receipt`
)
}
public
async
estimateL2MessageGasLimit
(
...
...
packages/sdk/src/interfaces/cross-chain-provider.ts
View file @
c9123af4
...
...
@@ -186,7 +186,7 @@ export interface ICrossChainProvider {
* @returns CrossChainMessage receipt including receipt of the transaction that relayed the
* given message.
*/
waitForMessageRec
ie
pt
(
waitForMessageRec
ei
pt
(
message
:
MessageLike
,
opts
?:
{
confirmations
?:
number
...
...
packages/sdk/test/cross-chain-erc20-pair.spec.ts
View file @
c9123af4
/* eslint-disable @typescript-eslint/no-empty-function */
import
'
./setup
'
describe
(
'
CrossChainERC20Pair
'
,
()
=>
{
describe
(
'
construction
'
,
()
=>
{
it
(
'
should have a messenger
'
,
()
=>
{}
)
it
(
'
should have a messenger
'
)
describe
(
'
when the token is a standard bridge token
'
,
()
=>
{
it
(
'
should resolve the correct bridge
'
,
()
=>
{}
)
it
(
'
should resolve the correct bridge
'
)
})
describe
(
'
when the token is SNX
'
,
()
=>
{
it
(
'
should resolve the correct bridge
'
,
()
=>
{}
)
it
(
'
should resolve the correct bridge
'
)
})
describe
(
'
when the token is DAI
'
,
()
=>
{
it
(
'
should resolve the correct bridge
'
,
()
=>
{}
)
it
(
'
should resolve the correct bridge
'
)
})
describe
(
'
when a custom adapter is provided
'
,
()
=>
{
it
(
'
should use the custom adapter
'
,
()
=>
{}
)
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
'
,
()
=>
{}
)
it
(
'
should trigger a token deposit
'
)
})
describe
(
'
when the token is ETH
'
,
()
=>
{
it
(
'
should trigger a token deposit
'
,
()
=>
{}
)
it
(
'
should trigger a token deposit
'
)
})
describe
(
'
when the token is SNX
'
,
()
=>
{
it
(
'
should trigger a token deposit
'
,
()
=>
{}
)
it
(
'
should trigger a token deposit
'
)
})
describe
(
'
when the token is DAI
'
,
()
=>
{
it
(
'
should trigger a token deposit
'
,
()
=>
{}
)
it
(
'
should trigger a token deposit
'
)
})
})
describe
(
'
when the user does not have enough balance
'
,
()
=>
{
it
(
'
should throw an error
'
,
()
=>
{}
)
it
(
'
should throw an error
'
)
})
describe
(
'
when the user has not given enough allowance to the bridge
'
,
()
=>
{
it
(
'
should throw an error
'
,
()
=>
{}
)
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
'
,
()
=>
{}
)
it
(
'
should trigger a token withdrawal
'
)
})
describe
(
'
when the token is ETH
'
,
()
=>
{
it
(
'
should trigger a token withdrawal
'
,
()
=>
{}
)
it
(
'
should trigger a token withdrawal
'
)
})
describe
(
'
when the token is SNX
'
,
()
=>
{
it
(
'
should trigger a token withdrawal
'
,
()
=>
{}
)
it
(
'
should trigger a token withdrawal
'
)
})
describe
(
'
when the token is DAI
'
,
()
=>
{
it
(
'
should trigger a token withdrawal
'
,
()
=>
{}
)
it
(
'
should trigger a token withdrawal
'
)
})
})
describe
(
'
when the user does not have enough balance
'
,
()
=>
{
it
(
'
should throw an error
'
,
()
=>
{}
)
it
(
'
should throw an error
'
)
})
})
describe
(
'
populateTransaction
'
,
()
=>
{
describe
(
'
deposit
'
,
()
=>
{
it
(
'
should populate the transaction with the correct values
'
,
()
=>
{}
)
it
(
'
should populate the transaction with the correct values
'
)
})
describe
(
'
withdraw
'
,
()
=>
{
it
(
'
should populate the transaction with the correct values
'
,
()
=>
{}
)
it
(
'
should populate the transaction with the correct values
'
)
})
})
describe
(
'
estimateGas
'
,
()
=>
{
describe
(
'
deposit
'
,
()
=>
{
it
(
'
should estimate gas required for the transaction
'
,
()
=>
{}
)
it
(
'
should estimate gas required for the transaction
'
)
})
describe
(
'
withdraw
'
,
()
=>
{
it
(
'
should estimate gas required for the transaction
'
,
()
=>
{}
)
it
(
'
should estimate gas required for the transaction
'
)
})
})
})
packages/sdk/test/cross-chain-messenger.spec.ts
View file @
c9123af4
/* eslint-disable @typescript-eslint/no-empty-function */
import
'
./setup
'
describe
(
'
CrossChainMessenger
'
,
()
=>
{
describe
(
'
sendMessage
'
,
()
=>
{
describe
(
'
when no l2GasLimit is provided
'
,
()
=>
{
it
(
'
should send a message with an estimated l2GasLimit
'
,
()
=>
{}
)
it
(
'
should send a message with an estimated l2GasLimit
'
)
})
describe
(
'
when an l2GasLimit is provided
'
,
()
=>
{
it
(
'
should send a message with the provided l2GasLimit
'
,
()
=>
{}
)
it
(
'
should send a message with the provided l2GasLimit
'
)
})
})
describe
(
'
resendMessage
'
,
()
=>
{
describe
(
'
when the message being resent exists
'
,
()
=>
{
it
(
'
should resend the message with the new gas limit
'
,
()
=>
{}
)
it
(
'
should resend the message with the new gas limit
'
)
})
describe
(
'
when the message being resent does not exist
'
,
()
=>
{
it
(
'
should throw an error
'
,
()
=>
{}
)
it
(
'
should throw an error
'
)
})
})
describe
(
'
finalizeMessage
'
,
()
=>
{
describe
(
'
when the message being finalized exists
'
,
()
=>
{
describe
(
'
when the message is ready to be finalized
'
,
()
=>
{
it
(
'
should finalize the message
'
,
()
=>
{}
)
it
(
'
should finalize the message
'
)
})
describe
(
'
when the message is not ready to be finalized
'
,
()
=>
{
it
(
'
should throw an error
'
,
()
=>
{}
)
it
(
'
should throw an error
'
)
})
describe
(
'
when the message has already been finalized
'
,
()
=>
{
it
(
'
should throw an error
'
,
()
=>
{}
)
it
(
'
should throw an error
'
)
})
})
describe
(
'
when the message being finalized does not exist
'
,
()
=>
{
it
(
'
should throw an error
'
,
()
=>
{}
)
it
(
'
should throw an error
'
)
})
})
})
packages/sdk/test/cross-chain-provider.spec.ts
View file @
c9123af4
/* eslint-disable @typescript-eslint/no-empty-function */
import
{
expect
}
from
'
./setup
'
import
{
Provider
}
from
'
@ethersproject/abstract-provider
'
import
{
Contract
}
from
'
ethers
'
...
...
@@ -383,24 +382,26 @@ describe('CrossChainProvider', () => {
describe
(
'
getMessagesByAddress
'
,
()
=>
{
describe
(
'
when the address has sent messages
'
,
()
=>
{
describe
(
'
when no direction is specified
'
,
()
=>
{
it
(
'
should find all messages sent by the address
'
,
()
=>
{}
)
it
(
'
should find all messages sent by the address
'
)
})
describe
(
'
when a direction is specified
'
,
()
=>
{
it
(
'
should find all messages only in the given direction
'
,
()
=>
{}
)
it
(
'
should find all messages only in the given direction
'
)
})
describe
(
'
when a block range is specified
'
,
()
=>
{
it
(
'
should find all messages within the block range
'
,
()
=>
{}
)
it
(
'
should find all messages within the block range
'
)
})
describe
(
'
when both a direction and a block range are specified
'
,
()
=>
{
it
(
'
should find all messages only in the given direction and within the block range
'
,
()
=>
{})
it
(
'
should find all messages only in the given direction and within the block range
'
)
})
})
describe
(
'
when the address has not sent messages
'
,
()
=>
{
it
(
'
should find nothing
'
,
()
=>
{}
)
it
(
'
should find nothing
'
)
})
})
...
...
@@ -784,44 +785,44 @@ describe('CrossChainProvider', () => {
describe
(
'
getMessageStatus
'
,
()
=>
{
describe
(
'
when the message is an L1 => L2 message
'
,
()
=>
{
describe
(
'
when the message has not been executed on L2 yet
'
,
()
=>
{
it
(
'
should return a status of UNCONFIRMED_L1_TO_L2_MESSAGE
'
,
()
=>
{}
)
it
(
'
should return a status of UNCONFIRMED_L1_TO_L2_MESSAGE
'
)
})
describe
(
'
when the message has been executed on L2
'
,
()
=>
{
it
(
'
should return a status of RELAYED
'
,
()
=>
{}
)
it
(
'
should return a status of RELAYED
'
)
})
describe
(
'
when the message has been executed but failed
'
,
()
=>
{
it
(
'
should return a status of FAILED_L1_TO_L2_MESSAGE
'
,
()
=>
{}
)
it
(
'
should return a status of FAILED_L1_TO_L2_MESSAGE
'
)
})
})
describe
(
'
when the message is an L2 => L1 message
'
,
()
=>
{
describe
(
'
when the message state root has not been published
'
,
()
=>
{
it
(
'
should return a status of STATE_ROOT_NOT_PUBLISHED
'
,
()
=>
{}
)
it
(
'
should return a status of STATE_ROOT_NOT_PUBLISHED
'
)
})
describe
(
'
when the message state root is still in the challenge period
'
,
()
=>
{
it
(
'
should return a status of IN_CHALLENGE_PERIOD
'
,
()
=>
{}
)
it
(
'
should return a status of IN_CHALLENGE_PERIOD
'
)
})
describe
(
'
when the message is no longer in the challenge period
'
,
()
=>
{
describe
(
'
when the message has been relayed successfully
'
,
()
=>
{
it
(
'
should return a status of RELAYED
'
,
()
=>
{}
)
it
(
'
should return a status of RELAYED
'
)
})
describe
(
'
when the message has been relayed but the relay failed
'
,
()
=>
{
it
(
'
should return a status of READY_FOR_RELAY
'
,
()
=>
{}
)
it
(
'
should return a status of READY_FOR_RELAY
'
)
})
describe
(
'
when the message has not been relayed
'
,
()
=>
{
it
(
'
should return a status of READY_FOR_RELAY
'
,
()
=>
{}
)
it
(
'
should return a status of READY_FOR_RELAY
'
)
})
})
})
describe
(
'
when the message does not exist
'
,
()
=>
{
it
(
'
should throw an error
'
,
()
=>
{}
)
it
(
'
should throw an error
'
)
})
})
...
...
@@ -982,64 +983,157 @@ describe('CrossChainProvider', () => {
// track of
})
describe
(
'
waitForMessageReciept
'
,
()
=>
{
describe
(
'
waitForMessageReceipt
'
,
()
=>
{
let
l2Messenger
:
Contract
let
provider
:
CrossChainProvider
beforeEach
(
async
()
=>
{
l2Messenger
=
(
await
(
await
ethers
.
getContractFactory
(
'
MockMessenger
'
)
).
deploy
())
as
any
provider
=
new
CrossChainProvider
({
l1Provider
:
ethers
.
provider
,
l2Provider
:
ethers
.
provider
,
l1ChainId
:
31337
,
contracts
:
{
l2
:
{
L2CrossDomainMessenger
:
l2Messenger
.
address
,
},
},
})
})
describe
(
'
when the message receipt already exists
'
,
()
=>
{
it
(
'
should immediately return the receipt
'
,
()
=>
{})
it
(
'
should immediately return the receipt
'
,
async
()
=>
{
const
message
=
{
direction
:
MessageDirection
.
L1_TO_L2
,
target
:
'
0x
'
+
'
11
'
.
repeat
(
20
),
sender
:
'
0x
'
+
'
22
'
.
repeat
(
20
),
message
:
'
0x
'
+
'
33
'
.
repeat
(
64
),
messageNonce
:
1234
,
logIndex
:
0
,
blockNumber
:
1234
,
transactionHash
:
'
0x
'
+
'
44
'
.
repeat
(
32
),
}
const
tx
=
await
l2Messenger
.
triggerRelayedMessageEvents
([
hashCrossChainMessage
(
message
),
])
const
messageReceipt
=
await
provider
.
waitForMessageReceipt
(
message
)
expect
(
messageReceipt
.
receiptStatus
).
to
.
equal
(
1
)
expect
(
omit
(
messageReceipt
.
transactionReceipt
,
'
confirmations
'
)
).
to
.
deep
.
equal
(
omit
(
await
ethers
.
provider
.
getTransactionReceipt
(
tx
.
hash
),
'
confirmations
'
)
)
})
})
describe
(
'
when the message receipt does not exist already
'
,
()
=>
{
describe
(
'
when no extra options are provided
'
,
()
=>
{
it
(
'
should wait for the receipt to be published
'
,
()
=>
{})
it
(
'
should wait forever for the receipt if the receipt is never published
'
,
()
=>
{})
it
(
'
should wait for the receipt to be published
'
,
async
()
=>
{
const
message
=
{
direction
:
MessageDirection
.
L1_TO_L2
,
target
:
'
0x
'
+
'
11
'
.
repeat
(
20
),
sender
:
'
0x
'
+
'
22
'
.
repeat
(
20
),
message
:
'
0x
'
+
'
33
'
.
repeat
(
64
),
messageNonce
:
1234
,
logIndex
:
0
,
blockNumber
:
1234
,
transactionHash
:
'
0x
'
+
'
44
'
.
repeat
(
32
),
}
setTimeout
(
async
()
=>
{
await
l2Messenger
.
triggerRelayedMessageEvents
([
hashCrossChainMessage
(
message
),
])
},
5000
)
const
tick
=
Date
.
now
()
const
messageReceipt
=
await
provider
.
waitForMessageReceipt
(
message
)
const
tock
=
Date
.
now
()
expect
(
messageReceipt
.
receiptStatus
).
to
.
equal
(
1
)
expect
(
tock
-
tick
).
to
.
be
.
greaterThan
(
5000
)
})
it
(
'
should wait forever for the receipt if the receipt is never published
'
,
()
=>
{
// Not sure how to easily test this without introducing some sort of cancellation token
// I don't want the promise to loop forever and make the tests never finish.
})
})
describe
(
'
when a timeout is provided
'
,
()
=>
{
it
(
'
should throw an error if the timeout is reached
'
,
()
=>
{})
})
})
it
(
'
should throw an error if the timeout is reached
'
,
async
()
=>
{
const
message
=
{
direction
:
MessageDirection
.
L1_TO_L2
,
target
:
'
0x
'
+
'
11
'
.
repeat
(
20
),
sender
:
'
0x
'
+
'
22
'
.
repeat
(
20
),
message
:
'
0x
'
+
'
33
'
.
repeat
(
64
),
messageNonce
:
1234
,
logIndex
:
0
,
blockNumber
:
1234
,
transactionHash
:
'
0x
'
+
'
44
'
.
repeat
(
32
),
}
describe
(
'
when the message does not exist
'
,
()
=>
{
it
(
'
should throw an error
'
,
()
=>
{})
await
expect
(
provider
.
waitForMessageReceipt
(
message
,
{
timeoutMs
:
10000
,
})
).
to
.
be
.
rejectedWith
(
'
timed out waiting for message receipt
'
)
})
})
})
})
describe
(
'
estimateL2MessageGasLimit
'
,
()
=>
{
it
(
'
should perform a gas estimation of the L2 action
'
,
()
=>
{}
)
it
(
'
should perform a gas estimation of the L2 action
'
)
})
describe
(
'
estimateMessageWaitTimeBlocks
'
,
()
=>
{
describe
(
'
when the message exists
'
,
()
=>
{
describe
(
'
when the message is an L1 => L2 message
'
,
()
=>
{
describe
(
'
when the message has not been executed on L2 yet
'
,
()
=>
{
it
(
'
should return the estimated blocks until the message will be confirmed on L2
'
,
()
=>
{})
it
(
'
should return the estimated blocks until the message will be confirmed on L2
'
)
})
describe
(
'
when the message has been executed on L2
'
,
()
=>
{
it
(
'
should return 0
'
,
()
=>
{}
)
it
(
'
should return 0
'
)
})
})
describe
(
'
when the message is an L2 => L1 message
'
,
()
=>
{
describe
(
'
when the state root has not been published
'
,
()
=>
{
it
(
'
should return the estimated blocks until the state root will be published and pass the challenge period
'
,
()
=>
{})
it
(
'
should return the estimated blocks until the state root will be published and pass the challenge period
'
)
})
describe
(
'
when the state root is within the challenge period
'
,
()
=>
{
it
(
'
should return the estimated blocks until the state root passes the challenge period
'
,
()
=>
{})
it
(
'
should return the estimated blocks until the state root passes the challenge period
'
)
})
describe
(
'
when the state root passes the challenge period
'
,
()
=>
{
it
(
'
should return 0
'
,
()
=>
{}
)
it
(
'
should return 0
'
)
})
})
})
describe
(
'
when the message does not exist
'
,
()
=>
{
it
(
'
should throw an error
'
,
()
=>
{}
)
it
(
'
should throw an error
'
)
})
})
describe
(
'
estimateMessageWaitTimeSeconds
'
,
()
=>
{
it
(
'
should be the result of estimateMessageWaitTimeBlocks multiplied by the L1 block time
'
,
()
=>
{})
it
(
'
should be the result of estimateMessageWaitTimeBlocks multiplied by the L1 block time
'
)
})
})
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