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
e4103622
Unverified
Commit
e4103622
authored
Jun 01, 2021
by
smartcontracts
Committed by
GitHub
Jun 01, 2021
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into regenesis/0.4.0
parents
c4c7beaa
5e3c5d1c
Changes
23
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
1085 additions
and
21 deletions
+1085
-21
cool-baboons-guess.md
.changeset/cool-baboons-guess.md
+5
-0
nasty-dots-grow.md
.changeset/nasty-dots-grow.md
+6
-0
sharp-files-knock.md
.changeset/sharp-files-knock.md
+5
-0
client.go
l2geth/rollup/client.go
+2
-2
transport-db.ts
packages/data-transport-layer/src/db/transport-db.ts
+26
-3
sequencer-batch-appended.ts
...ervices/l1-ingestion/handlers/sequencer-batch-appended.ts
+3
-3
transaction-enqueued.ts
...rc/services/l1-ingestion/handlers/transaction-enqueued.ts
+1
-1
transaction.ts
...t-layer/src/services/l2-ingestion/handlers/transaction.ts
+2
-2
database-types.ts
packages/data-transport-layer/src/types/database-types.ts
+2
-2
event-handler-types.ts
...ges/data-transport-layer/src/types/event-handler-types.ts
+1
-1
sequencer-batch-appended.spec.ts
...es/l1-ingestion/handlers/sequencer-batch-appended.spec.ts
+1
-1
transaction-enqueued.spec.ts
...rvices/l1-ingestion/handlers/transaction-enqueued.spec.ts
+2
-2
hardhat.config.ts
packages/message-relayer/hardhat.config.ts
+15
-0
package.json
packages/message-relayer/package.json
+15
-2
index.ts
packages/message-relayer/src/index.ts
+1
-0
relay-tx.ts
packages/message-relayer/src/relay-tx.ts
+418
-0
setup.ts
packages/message-relayer/test/setup.ts
+12
-0
MockL2CrossDomainMessenger.sol
...elayer/test/test-contracts/MockL2CrossDomainMessenger.sol
+44
-0
relay-tx.spec.ts
packages/message-relayer/test/unit-tests/relay-tx.spec.ts
+377
-0
smockit.ts
packages/smock/src/smockit/smockit.ts
+9
-2
TestHelpers_MockCaller.sol
packages/smock/test/contracts/TestHelpers_MockCaller.sol
+8
-0
call-assertions.spec.ts
packages/smock/test/smockit/call-assertions.spec.ts
+72
-0
yarn.lock
yarn.lock
+58
-0
No files found.
.changeset/cool-baboons-guess.md
0 → 100644
View file @
e4103622
---
'
@eth-optimism/smock'
:
patch
---
Fixes a bug that would break call assertions for overloaded smocked functions
.changeset/nasty-dots-grow.md
0 → 100644
View file @
e4103622
---
'
@eth-optimism/l2geth'
:
patch
'
@eth-optimism/data-transport-layer'
:
patch
---
Fix gasLimit overflow
.changeset/sharp-files-knock.md
0 → 100644
View file @
e4103622
---
'
@eth-optimism/message-relayer'
:
patch
---
Adds a new set of tools for generating messages to be relayed and their proofs
l2geth/rollup/client.go
View file @
e4103622
...
...
@@ -69,7 +69,7 @@ type transaction struct {
BlockNumber
uint64
`json:"blockNumber"`
Timestamp
uint64
`json:"timestamp"`
Value
hexutil
.
Uint64
`json:"value"`
GasLimit
uint64
`json:"gasLimit"`
GasLimit
uint64
`json:"gasLimit
,string
"`
Target
common
.
Address
`json:"target"`
Origin
*
common
.
Address
`json:"origin"`
Data
hexutil
.
Bytes
`json:"data"`
...
...
@@ -83,7 +83,7 @@ type Enqueue struct {
Index
*
uint64
`json:"ctcIndex"`
Target
*
common
.
Address
`json:"target"`
Data
*
hexutil
.
Bytes
`json:"data"`
GasLimit
*
uint64
`json:"gasLimit"`
GasLimit
*
uint64
`json:"gasLimit
,string
"`
Origin
*
common
.
Address
`json:"origin"`
BlockNumber
*
uint64
`json:"blockNumber"`
Timestamp
*
uint64
`json:"timestamp"`
...
...
packages/data-transport-layer/src/db/transport-db.ts
View file @
e4103622
...
...
@@ -379,8 +379,9 @@ export class TransportDB {
if
(
index
===
null
)
{
return
null
}
return
this
.
db
.
get
<
TEntry
>
(
`
${
key
}
:index`
,
index
)
let
entry
=
await
this
.
db
.
get
<
TEntry
>
(
`
${
key
}
:index`
,
index
)
entry
=
stringify
(
entry
)
return
entry
}
private
async
_getEntries
<
TEntry
extends
Indexed
>
(
...
...
@@ -388,6 +389,28 @@ export class TransportDB {
startIndex
:
number
,
endIndex
:
number
):
Promise
<
TEntry
[]
|
[]
>
{
return
this
.
db
.
range
<
TEntry
>
(
`
${
key
}
:index`
,
startIndex
,
endIndex
)
const
entries
=
await
this
.
db
.
range
<
TEntry
>
(
`
${
key
}
:index`
,
startIndex
,
endIndex
)
const
results
=
[]
for
(
const
entry
of
entries
)
{
results
.
push
(
stringify
(
entry
))
}
return
results
}
}
function
stringify
(
entry
)
{
if
(
entry
===
null
||
entry
===
undefined
)
{
return
entry
}
if
(
entry
.
gasLimit
)
{
entry
.
gasLimit
=
BigNumber
.
from
(
entry
.
gasLimit
).
toString
()
}
if
(
entry
.
decoded
)
{
entry
.
decoded
.
gasLimit
=
BigNumber
.
from
(
entry
.
decoded
.
gasLimit
).
toString
()
}
return
entry
}
packages/data-transport-layer/src/services/l1-ingestion/handlers/sequencer-batch-appended.ts
View file @
e4103622
...
...
@@ -69,7 +69,7 @@ export const handleEventsSequencerBatchAppended: EventHandlerSet<
submitter
:
l1Transaction
.
from
,
l1TransactionHash
:
l1Transaction
.
hash
,
l1TransactionData
:
l1Transaction
.
data
,
gasLimit
:
SEQUENCER_GAS_LIMIT
,
gasLimit
:
`
${
SEQUENCER_GAS_LIMIT
}
`
,
prevTotalElements
:
batchSubmissionEvent
.
args
.
_prevTotalElements
,
batchIndex
:
batchSubmissionEvent
.
args
.
_batchIndex
,
...
...
@@ -115,7 +115,7 @@ export const handleEventsSequencerBatchAppended: EventHandlerSet<
batchIndex
:
extraData
.
batchIndex
.
toNumber
(),
blockNumber
:
BigNumber
.
from
(
context
.
blockNumber
).
toNumber
(),
timestamp
:
BigNumber
.
from
(
context
.
timestamp
).
toNumber
(),
gasLimit
:
BigNumber
.
from
(
extraData
.
gasLimit
).
to
Number
(),
gasLimit
:
BigNumber
.
from
(
extraData
.
gasLimit
).
to
String
(),
target
:
SEQUENCER_ENTRYPOINT_ADDRESS
,
origin
:
null
,
data
:
toHexString
(
sequencerTransaction
),
...
...
@@ -147,7 +147,7 @@ export const handleEventsSequencerBatchAppended: EventHandlerSet<
batchIndex
:
extraData
.
batchIndex
.
toNumber
(),
blockNumber
:
BigNumber
.
from
(
0
).
toNumber
(),
timestamp
:
BigNumber
.
from
(
0
).
toNumber
(),
gasLimit
:
BigNumber
.
from
(
0
).
to
Number
(),
gasLimit
:
BigNumber
.
from
(
0
).
to
String
(),
target
:
constants
.
AddressZero
,
origin
:
constants
.
AddressZero
,
data
:
'
0x
'
,
...
...
packages/data-transport-layer/src/services/l1-ingestion/handlers/transaction-enqueued.ts
View file @
e4103622
...
...
@@ -17,7 +17,7 @@ export const handleEventsTransactionEnqueued: EventHandlerSet<
index
:
event
.
args
.
_queueIndex
.
toNumber
(),
target
:
event
.
args
.
_target
,
data
:
event
.
args
.
_data
,
gasLimit
:
event
.
args
.
_gasLimit
.
to
Number
(),
gasLimit
:
event
.
args
.
_gasLimit
.
to
String
(),
origin
:
event
.
args
.
_l1TxOrigin
,
blockNumber
:
BigNumber
.
from
(
event
.
blockNumber
).
toNumber
(),
timestamp
:
event
.
args
.
_timestamp
.
toNumber
(),
...
...
packages/data-transport-layer/src/services/l2-ingestion/handlers/transaction.ts
View file @
e4103622
...
...
@@ -57,7 +57,7 @@ export const handleSequencerBlock = {
transactionEntry
=
{
...
transactionEntry
,
gasLimit
:
SEQUENCER_GAS_LIMIT
,
// ?
gasLimit
:
`
${
SEQUENCER_GAS_LIMIT
}
`
,
// ?
target
:
SEQUENCER_ENTRYPOINT_ADDRESS
,
origin
:
null
,
data
:
serialize
(
...
...
@@ -82,7 +82,7 @@ export const handleSequencerBlock = {
}
else
{
transactionEntry
=
{
...
transactionEntry
,
gasLimit
:
BigNumber
.
from
(
transaction
.
gas
).
to
Number
(),
gasLimit
:
BigNumber
.
from
(
transaction
.
gas
).
to
String
(),
target
:
ethers
.
utils
.
getAddress
(
transaction
.
to
),
origin
:
ethers
.
utils
.
getAddress
(
transaction
.
l1TxOrigin
),
data
:
transaction
.
input
,
...
...
packages/data-transport-layer/src/types/database-types.ts
View file @
e4103622
...
...
@@ -16,7 +16,7 @@ export interface EnqueueEntry {
index
:
number
target
:
string
data
:
string
gasLimit
:
number
gasLimit
:
string
origin
:
string
blockNumber
:
number
timestamp
:
number
...
...
@@ -28,7 +28,7 @@ export interface TransactionEntry {
data
:
string
blockNumber
:
number
timestamp
:
number
gasLimit
:
number
gasLimit
:
string
target
:
string
origin
:
string
value
:
string
...
...
packages/data-transport-layer/src/types/event-handler-types.ts
View file @
e4103622
...
...
@@ -40,7 +40,7 @@ export interface SequencerBatchAppendedExtraData {
submitter
:
string
l1TransactionData
:
string
l1TransactionHash
:
string
gasLimit
:
number
gasLimit
:
string
// Stuff from TransactionBatchAppended.
prevTotalElements
:
BigNumber
...
...
packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/sequencer-batch-appended.spec.ts
View file @
e4103622
...
...
@@ -18,7 +18,7 @@ describe('Event Handlers: OVM_CanonicalTransactionChain.SequencerBatchAppended',
submitter
:
'
0xfd7d4de366850c08ee2cba32d851385a3071ec8d
'
,
l1TransactionHash
:
'
0x6effe006836b841205ace4d99d7ae1b74ee96aac499a3f358b97fccd32ee9af2
'
,
gasLimit
:
548976
,
gasLimit
:
'
548976
'
,
prevTotalElements
:
BigNumber
.
from
(
73677
),
batchIndex
:
BigNumber
.
from
(
743
),
batchSize
:
BigNumber
.
from
(
101
),
...
...
packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/transaction-enqueued.spec.ts
View file @
e4103622
...
...
@@ -93,7 +93,7 @@ describe('Event Handlers: OVM_CanonicalTransactionChain.TransactionEnqueued', ()
}
})
it
(
'
should have a gasLimit equal to the
integer
value of the _gasLimit argument
'
,
()
=>
{
it
(
'
should have a gasLimit equal to the
string
value of the _gasLimit argument
'
,
()
=>
{
for
(
let
i
=
0
;
i
<
Number
.
MAX_SAFE_INTEGER
;
...
...
@@ -113,7 +113,7 @@ describe('Event Handlers: OVM_CanonicalTransactionChain.TransactionEnqueued', ()
const
output1
=
handleEventsTransactionEnqueued
.
parseEvent
(...
input1
)
const
expected1
=
BigNumber
.
from
(
i
).
to
Number
()
const
expected1
=
BigNumber
.
from
(
i
).
to
String
()
expect
(
output1
).
to
.
have
.
property
(
'
gasLimit
'
,
expected1
)
}
...
...
packages/message-relayer/hardhat.config.ts
0 → 100644
View file @
e4103622
import
{
HardhatUserConfig
}
from
'
hardhat/config
'
import
'
@nomiclabs/hardhat-ethers
'
import
'
@nomiclabs/hardhat-waffle
'
const
config
:
HardhatUserConfig
=
{
paths
:
{
sources
:
'
./test/test-contracts
'
,
},
solidity
:
{
version
:
'
0.7.6
'
,
},
}
export
default
config
packages/message-relayer/package.json
View file @
e4103622
...
...
@@ -14,7 +14,8 @@
"clean"
:
"rimraf dist/ ./tsconfig.build.tsbuildinfo"
,
"lint"
:
"yarn lint:fix && yarn lint:check"
,
"lint:fix"
:
"prettier --config .prettierrc.json --write
\"
{src,exec,test}/**/*.ts
\"
"
,
"lint:check"
:
"tslint --format stylish --project ."
"lint:check"
:
"tslint --format stylish --project ."
,
"test"
:
"hardhat test --show-stack-traces"
},
"keywords"
:
[
"optimism"
,
...
...
@@ -30,9 +31,9 @@
},
"dependencies"
:
{
"@eth-optimism/common-ts"
:
"^0.1.2"
,
"bcfg"
:
"^0.1.6"
,
"@eth-optimism/contracts"
:
"^0.3.3"
,
"@eth-optimism/core-utils"
:
"^0.4.3"
,
"bcfg"
:
"^0.1.6"
,
"dotenv"
:
"^8.2.0"
,
"ethers"
:
"^5.1.0"
,
"google-spreadsheet"
:
"^3.1.15"
,
...
...
@@ -40,6 +41,18 @@
"rlp"
:
"^2.2.6"
},
"devDependencies"
:
{
"@eth-optimism/smock"
:
"^1.1.4"
,
"@nomiclabs/hardhat-ethers"
:
"^2.0.2"
,
"@nomiclabs/hardhat-waffle"
:
"^2.0.1"
,
"@types/chai"
:
"^4.2.18"
,
"@types/chai-as-promised"
:
"^7.1.4"
,
"@types/mocha"
:
"^8.2.2"
,
"chai"
:
"^4.3.4"
,
"chai-as-promised"
:
"^7.1.1"
,
"ethereum-waffle"
:
"^3.3.0"
,
"hardhat"
:
"^2.3.0"
,
"lodash"
:
"^4.17.21"
,
"mocha"
:
"^8.4.0"
,
"prettier"
:
"^2.2.1"
,
"tslint"
:
"^6.1.3"
,
"tslint-config-prettier"
:
"^1.18.0"
,
...
...
packages/message-relayer/src/index.ts
0 → 100644
View file @
e4103622
export
*
from
'
./relay-tx
'
packages/message-relayer/src/relay-tx.ts
0 → 100644
View file @
e4103622
/* Imports: External */
import
{
ethers
}
from
'
ethers
'
import
{
fromHexString
,
remove0x
,
toHexString
,
toRpcHexString
,
}
from
'
@eth-optimism/core-utils
'
import
{
getContractInterface
,
predeploys
}
from
'
@eth-optimism/contracts
'
import
*
as
rlp
from
'
rlp
'
import
{
MerkleTree
}
from
'
merkletreejs
'
// Number of blocks added to the L2 chain before the first L2 transaction. Genesis are added to the
// chain to initialize the system. However, they create a discrepancy between the L2 block number
// the index of the transaction that corresponds to that block number. For example, if there's 1
// genesis block, then the transaction with an index of 0 corresponds to the block with index 1.
const
NUM_L2_GENESIS_BLOCKS
=
1
interface
StateRootBatchHeader
{
batchIndex
:
ethers
.
BigNumber
batchRoot
:
string
batchSize
:
ethers
.
BigNumber
prevTotalElements
:
ethers
.
BigNumber
extraData
:
string
}
interface
StateRootBatch
{
header
:
StateRootBatchHeader
stateRoots
:
string
[]
}
interface
CrossDomainMessage
{
target
:
string
sender
:
string
message
:
string
messageNonce
:
number
}
interface
CrossDomainMessageProof
{
stateRoot
:
string
stateRootBatchHeader
:
StateRootBatchHeader
stateRootProof
:
{
index
:
number
siblings
:
string
[]
}
stateTrieWitness
:
string
storageTrieWitness
:
string
}
interface
CrossDomainMessagePair
{
message
:
CrossDomainMessage
proof
:
CrossDomainMessageProof
}
interface
StateTrieProof
{
accountProof
:
string
storageProof
:
string
}
/**
* Finds all L2 => L1 messages triggered by a given L2 transaction, if the message exists.
* @param l2RpcProvider L2 RPC provider.
* @param l2CrossDomainMessengerAddress Address of the L2CrossDomainMessenger.
* @param l2TransactionHash Hash of the L2 transaction to find a message for.
* @returns Messages associated with the transaction.
*/
export
const
getMessagesByTransactionHash
=
async
(
l2RpcProvider
:
ethers
.
providers
.
JsonRpcProvider
,
l2CrossDomainMessengerAddress
:
string
,
l2TransactionHash
:
string
):
Promise
<
CrossDomainMessage
[]
>
=>
{
// Complain if we can't find the given transaction.
const
transaction
=
await
l2RpcProvider
.
getTransaction
(
l2TransactionHash
)
if
(
transaction
===
null
)
{
throw
new
Error
(
`unable to find tx with hash:
${
l2TransactionHash
}
`
)
}
const
l2CrossDomainMessenger
=
new
ethers
.
Contract
(
l2CrossDomainMessengerAddress
,
getContractInterface
(
'
OVM_L2CrossDomainMessenger
'
),
l2RpcProvider
)
// Find all SentMessage events created in the same block as the given transaction. This is
// reliable because we should only have one transaction per block.
const
sentMessageEvents
=
await
l2CrossDomainMessenger
.
queryFilter
(
l2CrossDomainMessenger
.
filters
.
SentMessage
(),
transaction
.
blockNumber
,
transaction
.
blockNumber
)
// Decode the messages and turn them into a nicer struct.
const
sentMessages
=
sentMessageEvents
.
map
((
sentMessageEvent
)
=>
{
const
encodedMessage
=
sentMessageEvent
.
args
.
message
const
decodedMessage
=
l2CrossDomainMessenger
.
interface
.
decodeFunctionData
(
'
relayMessage
'
,
encodedMessage
)
return
{
target
:
decodedMessage
.
_target
,
sender
:
decodedMessage
.
_sender
,
message
:
decodedMessage
.
_message
,
messageNonce
:
decodedMessage
.
_messageNonce
.
toNumber
(),
}
})
return
sentMessages
}
/**
* Encodes a cross domain message.
* @param message Message to encode.
* @returns Encoded message.
*/
const
encodeCrossDomainMessage
=
(
message
:
CrossDomainMessage
):
string
=>
{
return
getContractInterface
(
'
OVM_L2CrossDomainMessenger
'
).
encodeFunctionData
(
'
relayMessage
'
,
[
message
.
target
,
message
.
sender
,
message
.
message
,
message
.
messageNonce
,
])
}
/**
* Finds the StateBatchAppended event associated with a given L2 transaction.
* @param l1RpcProvider L1 RPC provider.
* @param l1StateCommitmentChainAddress Address of the L1StateCommitmentChain.
* @param l2TransactionIndex Index of the L2 transaction to find a StateBatchAppended event for.
* @returns StateBatchAppended event for the given transaction or null if no such event exists.
*/
export
const
getStateBatchAppendedEventByTransactionIndex
=
async
(
l1RpcProvider
:
ethers
.
providers
.
JsonRpcProvider
,
l1StateCommitmentChainAddress
:
string
,
l2TransactionIndex
:
number
):
Promise
<
ethers
.
Event
|
null
>
=>
{
const
l1StateCommitmentChain
=
new
ethers
.
Contract
(
l1StateCommitmentChainAddress
,
getContractInterface
(
'
OVM_StateCommitmentChain
'
),
l1RpcProvider
)
const
getStateBatchAppendedEventByBatchIndex
=
async
(
index
:
number
):
Promise
<
ethers
.
Event
|
null
>
=>
{
const
eventQueryResult
=
await
l1StateCommitmentChain
.
queryFilter
(
l1StateCommitmentChain
.
filters
.
StateBatchAppended
(
index
)
)
if
(
eventQueryResult
.
length
===
0
)
{
return
null
}
else
{
return
eventQueryResult
[
0
]
}
}
const
isEventHi
=
(
event
:
ethers
.
Event
,
index
:
number
)
=>
{
const
prevTotalElements
=
event
.
args
.
_prevTotalElements
.
toNumber
()
return
index
<
prevTotalElements
}
const
isEventLo
=
(
event
:
ethers
.
Event
,
index
:
number
)
=>
{
const
prevTotalElements
=
event
.
args
.
_prevTotalElements
.
toNumber
()
const
batchSize
=
event
.
args
.
_batchSize
.
toNumber
()
return
index
>=
prevTotalElements
+
batchSize
}
const
totalBatches
:
ethers
.
BigNumber
=
await
l1StateCommitmentChain
.
getTotalBatches
()
if
(
totalBatches
.
eq
(
0
))
{
return
null
}
let
lowerBound
=
0
let
upperBound
=
totalBatches
.
toNumber
()
-
1
let
batchEvent
:
ethers
.
Event
|
null
=
await
getStateBatchAppendedEventByBatchIndex
(
upperBound
)
if
(
isEventLo
(
batchEvent
,
l2TransactionIndex
))
{
// Upper bound is too low, means this transaction doesn't have a corresponding state batch yet.
return
null
}
else
if
(
!
isEventHi
(
batchEvent
,
l2TransactionIndex
))
{
// Upper bound is not too low and also not too high. This means the upper bound event is the
// one we're looking for! Return it.
return
batchEvent
}
// Binary search to find the right event. The above checks will guarantee that the event does
// exist and that we'll find it during this search.
while
(
lowerBound
<
upperBound
)
{
const
middleOfBounds
=
Math
.
floor
((
lowerBound
+
upperBound
)
/
2
)
batchEvent
=
await
getStateBatchAppendedEventByBatchIndex
(
middleOfBounds
)
if
(
isEventHi
(
batchEvent
,
l2TransactionIndex
))
{
upperBound
=
middleOfBounds
}
else
if
(
isEventLo
(
batchEvent
,
l2TransactionIndex
))
{
lowerBound
=
middleOfBounds
}
else
{
break
}
}
return
batchEvent
}
/**
* Finds the full state root batch associated with a given transaction index.
* @param l1RpcProvider L1 RPC provider.
* @param l1StateCommitmentChainAddress Address of the L1StateCommitmentChain.
* @param l2TransactionIndex Index of the L2 transaction to find a state root batch for.
* @returns State root batch associated with the given transaction index or null if no state root
* batch exists.
*/
export
const
getStateRootBatchByTransactionIndex
=
async
(
l1RpcProvider
:
ethers
.
providers
.
JsonRpcProvider
,
l1StateCommitmentChainAddress
:
string
,
l2TransactionIndex
:
number
):
Promise
<
StateRootBatch
|
null
>
=>
{
const
l1StateCommitmentChain
=
new
ethers
.
Contract
(
l1StateCommitmentChainAddress
,
getContractInterface
(
'
OVM_StateCommitmentChain
'
),
l1RpcProvider
)
const
stateBatchAppendedEvent
=
await
getStateBatchAppendedEventByTransactionIndex
(
l1RpcProvider
,
l1StateCommitmentChainAddress
,
l2TransactionIndex
)
if
(
stateBatchAppendedEvent
===
null
)
{
return
null
}
const
stateBatchTransaction
=
await
stateBatchAppendedEvent
.
getTransaction
()
const
[
stateRoots
]
=
l1StateCommitmentChain
.
interface
.
decodeFunctionData
(
'
appendStateBatch
'
,
stateBatchTransaction
.
data
)
return
{
header
:
{
batchIndex
:
stateBatchAppendedEvent
.
args
.
_batchIndex
,
batchRoot
:
stateBatchAppendedEvent
.
args
.
_batchRoot
,
batchSize
:
stateBatchAppendedEvent
.
args
.
_batchSize
,
prevTotalElements
:
stateBatchAppendedEvent
.
args
.
_prevTotalElements
,
extraData
:
stateBatchAppendedEvent
.
args
.
_extraData
,
},
stateRoots
,
}
}
/**
* Generates a Merkle proof (using the particular scheme we use within Lib_MerkleTree).
* @param leaves Leaves of the merkle tree.
* @param index Index to generate a proof for.
* @returns Merkle proof sibling leaves, as hex strings.
*/
const
getMerkleTreeProof
=
(
leaves
:
string
[],
index
:
number
):
string
[]
=>
{
// Our specific Merkle tree implementation requires that the number of leaves is a power of 2.
// If the number of given leaves is less than a power of 2, we need to round up to the next
// available power of 2. We fill the remaining space with the hash of bytes32(0).
const
correctedTreeSize
=
Math
.
pow
(
2
,
Math
.
ceil
(
Math
.
log2
(
leaves
.
length
)))
const
parsedLeaves
=
[]
for
(
let
i
=
0
;
i
<
correctedTreeSize
;
i
++
)
{
if
(
i
<
leaves
.
length
)
{
parsedLeaves
.
push
(
leaves
[
i
])
}
else
{
parsedLeaves
.
push
(
ethers
.
utils
.
keccak256
(
'
0x
'
+
'
00
'
.
repeat
(
32
)))
}
}
// merkletreejs prefers things to be Buffers.
const
bufLeaves
=
parsedLeaves
.
map
(
fromHexString
)
const
tree
=
new
MerkleTree
(
bufLeaves
,
(
el
:
Buffer
|
string
):
Buffer
=>
{
return
fromHexString
(
ethers
.
utils
.
keccak256
(
el
))
}
)
const
proof
=
tree
.
getProof
(
bufLeaves
[
index
],
index
).
map
((
element
:
any
)
=>
{
return
toHexString
(
element
.
data
)
})
return
proof
}
/**
* Generates a Merkle-Patricia trie proof for a given account and storage slot.
* @param l2RpcProvider L2 RPC provider.
* @param blockNumber Block number to generate the proof at.
* @param address Address to generate the proof for.
* @param slot Storage slot to generate the proof for.
* @returns Account proof and storage proof.
*/
const
getStateTrieProof
=
async
(
l2RpcProvider
:
ethers
.
providers
.
JsonRpcProvider
,
blockNumber
:
number
,
address
:
string
,
slot
:
string
):
Promise
<
StateTrieProof
>
=>
{
const
proof
=
await
l2RpcProvider
.
send
(
'
eth_getProof
'
,
[
address
,
[
slot
],
toRpcHexString
(
blockNumber
),
])
return
{
accountProof
:
toHexString
(
rlp
.
encode
(
proof
.
accountProof
)),
storageProof
:
toHexString
(
rlp
.
encode
(
proof
.
storageProof
[
0
].
proof
)),
}
}
/**
* Finds all L2 => L1 messages sent in a given L2 transaction and generates proofs for each of
* those messages.
* @param l1RpcProvider L1 RPC provider.
* @param l2RpcProvider L2 RPC provider.
* @param l1StateCommitmentChainAddress Address of the StateCommitmentChain.
* @param l2CrossDomainMessengerAddress Address of the L2CrossDomainMessenger.
* @param l2TransactionHash L2 transaction hash to generate a relay transaction for.
* @returns An array of messages sent in the transaction and a proof of inclusion for each.
*/
export
const
getMessagesAndProofsForL2Transaction
=
async
(
l1RpcProvider
:
ethers
.
providers
.
JsonRpcProvider
,
l2RpcProvider
:
ethers
.
providers
.
JsonRpcProvider
,
l1StateCommitmentChainAddress
:
string
,
l2CrossDomainMessengerAddress
:
string
,
l2TransactionHash
:
string
):
Promise
<
CrossDomainMessagePair
[]
>
=>
{
const
l2Transaction
=
await
l2RpcProvider
.
getTransaction
(
l2TransactionHash
)
if
(
l2Transaction
===
null
)
{
throw
new
Error
(
`unable to find tx with hash:
${
l2TransactionHash
}
`
)
}
// Need to find the state batch for the given transaction. If no state batch has been published
// yet then we will not be able to generate a proof.
const
batch
=
await
getStateRootBatchByTransactionIndex
(
l1RpcProvider
,
l1StateCommitmentChainAddress
,
l2Transaction
.
blockNumber
-
NUM_L2_GENESIS_BLOCKS
)
if
(
batch
===
null
)
{
throw
new
Error
(
`unable to find state root batch for tx with hash:
${
l2TransactionHash
}
`
)
}
// Adjust the transaction index based on the number of L2 genesis block we have. "Index" here
// refers to the position of the transaction within the *Canonical Transaction Chain*.
const
l2TransactionIndex
=
l2Transaction
.
blockNumber
-
NUM_L2_GENESIS_BLOCKS
// Here the index refers to the position of the state root that corresponds to this transaction
// within the batch of state roots in which that state root was published.
const
txIndexInBatch
=
l2TransactionIndex
-
batch
.
header
.
prevTotalElements
.
toNumber
()
// Find every message that was sent during this transaction. We'll then attach a proof for each.
const
messages
=
await
getMessagesByTransactionHash
(
l2RpcProvider
,
l2CrossDomainMessengerAddress
,
l2TransactionHash
)
const
messagePairs
:
CrossDomainMessagePair
[]
=
[]
for
(
const
message
of
messages
)
{
// We need to calculate the specific storage slot that demonstrates that this message was
// actually included in the L2 chain. The following calculation is based on the fact that
// messages are stored in the following mapping on L2:
// https://github.com/ethereum-optimism/optimism/blob/c84d3450225306abbb39b4e7d6d82424341df2be/packages/contracts/contracts/optimistic-ethereum/OVM/predeploys/OVM_L2ToL1MessagePasser.sol#L23
// You can read more about how Solidity storage slots are computed for mappings here:
// https://docs.soliditylang.org/en/v0.8.4/internals/layout_in_storage.html#mappings-and-dynamic-arrays
const
messageSlot
=
ethers
.
utils
.
keccak256
(
ethers
.
utils
.
keccak256
(
encodeCrossDomainMessage
(
message
)
+
remove0x
(
l2CrossDomainMessengerAddress
)
)
+
'
00
'
.
repeat
(
32
)
)
// We need a Merkle trie proof for the given storage slot. This allows us to prove to L1 that
// the message was actually sent on L2.
const
stateTrieProof
=
await
getStateTrieProof
(
l2RpcProvider
,
l2Transaction
.
blockNumber
,
predeploys
.
OVM_L2ToL1MessagePasser
,
messageSlot
)
// State roots are published in batches to L1 and correspond 1:1 to transactions. We compute a
// Merkle root for these state roots so that we only need to store the minimum amount of
// information on-chain. So we need to create a Merkle proof for the specific state root that
// corresponds to this transaction.
const
stateRootMerkleProof
=
getMerkleTreeProof
(
batch
.
stateRoots
,
txIndexInBatch
)
// We now have enough information to create the message proof.
const
proof
:
CrossDomainMessageProof
=
{
stateRoot
:
batch
.
stateRoots
[
txIndexInBatch
],
stateRootBatchHeader
:
batch
.
header
,
stateRootProof
:
{
index
:
txIndexInBatch
,
siblings
:
stateRootMerkleProof
,
},
stateTrieWitness
:
stateTrieProof
.
accountProof
,
storageTrieWitness
:
stateTrieProof
.
storageProof
,
}
messagePairs
.
push
({
message
,
proof
,
})
}
return
messagePairs
}
packages/message-relayer/test/setup.ts
0 → 100644
View file @
e4103622
/* External Imports */
import
chai
=
require
(
'
chai
'
)
import
Mocha
from
'
mocha
'
import
{
solidity
}
from
'
ethereum-waffle
'
import
chaiAsPromised
from
'
chai-as-promised
'
chai
.
use
(
solidity
)
chai
.
use
(
chaiAsPromised
)
const
should
=
chai
.
should
()
const
expect
=
chai
.
expect
export
{
should
,
expect
,
Mocha
}
packages/message-relayer/test/test-contracts/MockL2CrossDomainMessenger.sol
0 → 100644
View file @
e4103622
// SPDX-License-Identifier: MIT
pragma solidity >0.7.0 <0.9.0;
pragma experimental ABIEncoderV2;
contract MockL2CrossDomainMessenger {
struct MessageData {
address target;
address sender;
bytes message;
uint256 messageNonce;
}
event SentMessage(bytes message);
function emitSentMessageEvent(
MessageData memory _message
)
public
{
emit SentMessage(
abi.encodeWithSignature(
"relayMessage(address,address,bytes,uint256)",
_message.target,
_message.sender,
_message.message,
_message.messageNonce
)
);
}
function emitMultipleSentMessageEvents(
MessageData[] memory _messages
)
public
{
for (uint256 i = 0; i < _messages.length; i++) {
emitSentMessageEvent(
_messages[i]
);
}
}
function doNothing() public {}
}
packages/message-relayer/test/unit-tests/relay-tx.spec.ts
0 → 100644
View file @
e4103622
import
{
expect
}
from
'
../setup
'
/* Imports: External */
import
hre
from
'
hardhat
'
import
{
Contract
,
Signer
}
from
'
ethers
'
import
{
getContractFactory
}
from
'
@eth-optimism/contracts
'
import
{
smockit
}
from
'
@eth-optimism/smock
'
import
{
toPlainObject
}
from
'
lodash
'
/* Imports: Internal */
import
{
getMessagesAndProofsForL2Transaction
,
getStateRootBatchByTransactionIndex
,
getStateBatchAppendedEventByTransactionIndex
,
getMessagesByTransactionHash
,
}
from
'
../../src/relay-tx
'
describe
(
'
relay transaction generation functions
'
,
()
=>
{
const
ethers
=
(
hre
as
any
).
ethers
const
l1RpcProvider
=
ethers
.
provider
const
l2RpcProvider
=
ethers
.
provider
let
signer1
:
Signer
before
(
async
()
=>
{
;[
signer1
]
=
await
ethers
.
getSigners
()
})
let
MockL2CrossDomainMessenger
:
Contract
beforeEach
(
async
()
=>
{
const
factory
=
await
ethers
.
getContractFactory
(
'
MockL2CrossDomainMessenger
'
)
MockL2CrossDomainMessenger
=
await
factory
.
deploy
()
})
let
StateCommitmentChain
:
Contract
beforeEach
(
async
()
=>
{
const
factory1
=
getContractFactory
(
'
Lib_AddressManager
'
)
const
factory2
=
getContractFactory
(
'
OVM_ChainStorageContainer
'
)
const
factory3
=
getContractFactory
(
'
OVM_StateCommitmentChain
'
)
const
mockBondManager
=
await
smockit
(
getContractFactory
(
'
OVM_BondManager
'
))
const
mockCanonicalTransactionChain
=
await
smockit
(
getContractFactory
(
'
OVM_CanonicalTransactionChain
'
)
)
mockBondManager
.
smocked
.
isCollateralized
.
will
.
return
.
with
(
true
)
mockCanonicalTransactionChain
.
smocked
.
getTotalElements
.
will
.
return
.
with
(
999999
)
const
AddressManager
=
await
factory1
.
connect
(
signer1
).
deploy
()
const
ChainStorageContainer
=
await
factory2
.
connect
(
signer1
)
.
deploy
(
AddressManager
.
address
,
'
OVM_StateCommitmentChain
'
)
StateCommitmentChain
=
await
factory3
.
connect
(
signer1
)
.
deploy
(
AddressManager
.
address
,
0
,
0
)
await
AddressManager
.
setAddress
(
'
OVM_ChainStorageContainer:SCC:batches
'
,
ChainStorageContainer
.
address
)
await
AddressManager
.
setAddress
(
'
OVM_StateCommitmentChain
'
,
StateCommitmentChain
.
address
)
await
AddressManager
.
setAddress
(
'
OVM_BondManager
'
,
mockBondManager
.
address
)
await
AddressManager
.
setAddress
(
'
OVM_CanonicalTransactionChain
'
,
mockCanonicalTransactionChain
.
address
)
})
describe
(
'
getMessageByTransactionHash
'
,
()
=>
{
it
(
'
should throw an error if a transaction with the given hash does not exist
'
,
async
()
=>
{
await
expect
(
getMessagesByTransactionHash
(
l2RpcProvider
,
MockL2CrossDomainMessenger
.
address
,
ethers
.
constants
.
HashZero
)
).
to
.
be
.
rejected
})
it
(
'
should return null if the transaction did not emit a SentMessage event
'
,
async
()
=>
{
const
tx
=
await
MockL2CrossDomainMessenger
.
doNothing
()
expect
(
await
getMessagesByTransactionHash
(
l2RpcProvider
,
MockL2CrossDomainMessenger
.
address
,
tx
.
hash
)
).
to
.
deep
.
equal
([])
})
it
(
'
should return the parsed event if the transaction emitted exactly one SentMessage event
'
,
async
()
=>
{
const
message
=
{
target
:
ethers
.
constants
.
AddressZero
,
sender
:
ethers
.
constants
.
AddressZero
,
message
:
'
0x
'
,
messageNonce
:
0
,
}
const
tx
=
await
MockL2CrossDomainMessenger
.
emitSentMessageEvent
(
message
)
expect
(
await
getMessagesByTransactionHash
(
l2RpcProvider
,
MockL2CrossDomainMessenger
.
address
,
tx
.
hash
)
).
to
.
deep
.
equal
([
message
])
})
it
(
'
should return the parsed events if the transaction emitted more than one SentMessage event
'
,
async
()
=>
{
const
messages
=
[
{
target
:
ethers
.
constants
.
AddressZero
,
sender
:
ethers
.
constants
.
AddressZero
,
message
:
'
0x
'
,
messageNonce
:
0
,
},
{
target
:
ethers
.
constants
.
AddressZero
,
sender
:
ethers
.
constants
.
AddressZero
,
message
:
'
0x
'
,
messageNonce
:
1
,
},
]
const
tx
=
await
MockL2CrossDomainMessenger
.
emitMultipleSentMessageEvents
(
messages
)
expect
(
await
getMessagesByTransactionHash
(
l2RpcProvider
,
MockL2CrossDomainMessenger
.
address
,
tx
.
hash
)
).
to
.
deep
.
equal
(
messages
)
})
})
describe
(
'
getStateBatchAppendedEventByTransactionIndex
'
,
()
=>
{
it
(
'
should return null when there are no batches yet
'
,
async
()
=>
{
expect
(
await
getStateBatchAppendedEventByTransactionIndex
(
l1RpcProvider
,
StateCommitmentChain
.
address
,
0
)
).
to
.
equal
(
null
)
})
it
(
'
should return null if a batch for the index does not exist
'
,
async
()
=>
{
// Should have a total of 1 element now.
await
StateCommitmentChain
.
appendStateBatch
(
[
ethers
.
constants
.
HashZero
],
0
)
expect
(
await
getStateBatchAppendedEventByTransactionIndex
(
l1RpcProvider
,
StateCommitmentChain
.
address
,
1
// Index 0 is ok but 1 should return null
)
).
to
.
equal
(
null
)
})
it
(
'
should return the batch if the index is part of the first batch
'
,
async
()
=>
{
// 5 elements
await
StateCommitmentChain
.
appendStateBatch
(
[
ethers
.
constants
.
HashZero
,
ethers
.
constants
.
HashZero
,
ethers
.
constants
.
HashZero
,
ethers
.
constants
.
HashZero
,
ethers
.
constants
.
HashZero
,
],
0
)
// Add another 5 so we have two batches and can isolate tests against the first.
await
StateCommitmentChain
.
appendStateBatch
(
[
ethers
.
constants
.
HashZero
,
ethers
.
constants
.
HashZero
,
ethers
.
constants
.
HashZero
,
ethers
.
constants
.
HashZero
,
ethers
.
constants
.
HashZero
,
],
5
)
const
event
=
await
getStateBatchAppendedEventByTransactionIndex
(
l1RpcProvider
,
StateCommitmentChain
.
address
,
1
)
expect
(
toPlainObject
(
event
.
args
)).
to
.
deep
.
include
({
_batchIndex
:
ethers
.
BigNumber
.
from
(
0
),
_batchSize
:
ethers
.
BigNumber
.
from
(
5
),
_prevTotalElements
:
ethers
.
BigNumber
.
from
(
0
),
})
})
it
(
'
should return the batch if the index is part of the last batch
'
,
async
()
=>
{
// 5 elements
await
StateCommitmentChain
.
appendStateBatch
(
[
ethers
.
constants
.
HashZero
,
ethers
.
constants
.
HashZero
,
ethers
.
constants
.
HashZero
,
ethers
.
constants
.
HashZero
,
ethers
.
constants
.
HashZero
,
],
0
)
// Add another 5 so we have two batches and can isolate tests against the second.
await
StateCommitmentChain
.
appendStateBatch
(
[
ethers
.
constants
.
HashZero
,
ethers
.
constants
.
HashZero
,
ethers
.
constants
.
HashZero
,
ethers
.
constants
.
HashZero
,
ethers
.
constants
.
HashZero
,
],
5
)
const
event
=
await
getStateBatchAppendedEventByTransactionIndex
(
l1RpcProvider
,
StateCommitmentChain
.
address
,
7
)
expect
(
toPlainObject
(
event
.
args
)).
to
.
deep
.
include
({
_batchIndex
:
ethers
.
BigNumber
.
from
(
1
),
_batchSize
:
ethers
.
BigNumber
.
from
(
5
),
_prevTotalElements
:
ethers
.
BigNumber
.
from
(
5
),
})
})
for
(
const
numBatches
of
[
1
,
2
,
8
])
{
const
elementsPerBatch
=
8
describe
(
`when there are
${
numBatches
}
batch(es) of
${
elementsPerBatch
}
elements each`
,
()
=>
{
const
totalElements
=
numBatches
*
elementsPerBatch
beforeEach
(
async
()
=>
{
for
(
let
i
=
0
;
i
<
numBatches
;
i
++
)
{
await
StateCommitmentChain
.
appendStateBatch
(
new
Array
(
elementsPerBatch
).
fill
(
ethers
.
constants
.
HashZero
),
i
*
elementsPerBatch
)
}
})
for
(
let
i
=
0
;
i
<
totalElements
;
i
+=
elementsPerBatch
)
{
it
(
`should be able to get the correct event for the
${
i
}
th/st/rd/whatever element`
,
async
()
=>
{
const
event
=
await
getStateBatchAppendedEventByTransactionIndex
(
l1RpcProvider
,
StateCommitmentChain
.
address
,
i
)
expect
(
toPlainObject
(
event
.
args
)).
to
.
deep
.
include
({
_batchIndex
:
ethers
.
BigNumber
.
from
(
i
/
elementsPerBatch
),
_batchSize
:
ethers
.
BigNumber
.
from
(
elementsPerBatch
),
_prevTotalElements
:
ethers
.
BigNumber
.
from
(
i
),
})
})
}
})
}
})
describe
(
'
getStateRootBatchByTransactionIndex
'
,
()
=>
{
it
(
'
should return null if a batch for the index does not exist
'
,
async
()
=>
{
// Should have a total of 1 element now.
await
StateCommitmentChain
.
appendStateBatch
(
[
ethers
.
constants
.
HashZero
],
0
)
expect
(
await
getStateRootBatchByTransactionIndex
(
l1RpcProvider
,
StateCommitmentChain
.
address
,
1
// Index 0 is ok but 1 should return null
)
).
to
.
equal
(
null
)
})
it
(
'
should return the full batch for a given index when it exists
'
,
async
()
=>
{
// Should have a total of 1 element now.
await
StateCommitmentChain
.
appendStateBatch
(
[
ethers
.
constants
.
HashZero
],
0
)
const
batch
=
await
getStateRootBatchByTransactionIndex
(
l1RpcProvider
,
StateCommitmentChain
.
address
,
0
// Index 0 is ok but 1 should return null
)
expect
(
batch
.
header
).
to
.
deep
.
include
({
batchIndex
:
ethers
.
BigNumber
.
from
(
0
),
batchSize
:
ethers
.
BigNumber
.
from
(
1
),
prevTotalElements
:
ethers
.
BigNumber
.
from
(
0
),
})
expect
(
batch
.
stateRoots
).
to
.
deep
.
equal
([
ethers
.
constants
.
HashZero
])
})
})
describe
(
'
makeRelayTransactionData
'
,
()
=>
{
it
(
'
should throw an error if the transaction does not exist
'
,
async
()
=>
{
await
expect
(
getMessagesAndProofsForL2Transaction
(
l1RpcProvider
,
l2RpcProvider
,
StateCommitmentChain
.
address
,
MockL2CrossDomainMessenger
.
address
,
ethers
.
constants
.
HashZero
)
).
to
.
be
.
rejected
})
it
(
'
should throw an error if the transaction did not send a message
'
,
async
()
=>
{
const
tx
=
await
MockL2CrossDomainMessenger
.
doNothing
()
await
expect
(
getMessagesAndProofsForL2Transaction
(
l1RpcProvider
,
l2RpcProvider
,
StateCommitmentChain
.
address
,
MockL2CrossDomainMessenger
.
address
,
tx
.
hash
)
).
to
.
be
.
rejected
})
it
(
'
should throw an error if the corresponding state batch has not been submitted
'
,
async
()
=>
{
const
tx
=
await
MockL2CrossDomainMessenger
.
emitSentMessageEvent
({
target
:
ethers
.
constants
.
AddressZero
,
sender
:
ethers
.
constants
.
AddressZero
,
message
:
'
0x
'
,
messageNonce
:
0
,
})
await
expect
(
getMessagesAndProofsForL2Transaction
(
l1RpcProvider
,
l2RpcProvider
,
StateCommitmentChain
.
address
,
MockL2CrossDomainMessenger
.
address
,
tx
.
hash
)
).
to
.
be
.
rejected
})
// Unfortunately this is hard to test here because hardhat doesn't support eth_getProof.
// Because this function is embedded into the message relayer, we should be able to use
// integration tests to sufficiently test this.
it
.
skip
(
'
should otherwise return the encoded transaction data
'
,
()
=>
{
// TODO?
})
})
})
packages/smock/src/smockit/smockit.ts
View file @
e4103622
...
...
@@ -79,18 +79,25 @@ const smockifyFunction = (
let
data
:
any
=
toHexString
(
calldataBuf
)
try
{
data
=
contract
.
interface
.
decodeFunctionData
(
fragment
.
name
,
data
)
data
=
contract
.
interface
.
decodeFunctionData
(
fragment
.
format
(),
data
)
}
catch
(
e
)
{
console
.
error
(
e
)
}
return
{
functionName
:
fragment
.
name
,
functionSignature
:
fragment
.
format
(),
data
,
}
})
.
filter
((
functionResult
:
any
)
=>
{
return
functionResult
.
functionName
===
functionName
return
(
functionResult
.
functionName
===
functionName
||
functionResult
.
functionSignature
===
functionName
)
})
.
map
((
functionResult
:
any
)
=>
{
return
functionResult
.
data
...
...
packages/smock/test/contracts/TestHelpers_MockCaller.sol
0 → 100644
View file @
e4103622
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
contract TestHelpers_MockCaller {
function callMock(address _target, bytes memory _data) public {
_target.call(_data);
}
}
packages/smock/test/smockit/call-assertions.spec.ts
0 → 100644
View file @
e4103622
/* Imports: External */
import
hre
from
'
hardhat
'
import
{
expect
}
from
'
chai
'
import
{
Contract
}
from
'
ethers
'
/* Imports: Internal */
import
{
MockContract
,
smockit
}
from
'
../../src
'
describe
(
'
[smock]: call assertion tests
'
,
()
=>
{
const
ethers
=
(
hre
as
any
).
ethers
let
mock
:
MockContract
beforeEach
(
async
()
=>
{
mock
=
await
smockit
(
'
TestHelpers_BasicReturnContract
'
)
})
let
mockCaller
:
Contract
before
(
async
()
=>
{
const
mockCallerFactory
=
await
ethers
.
getContractFactory
(
'
TestHelpers_MockCaller
'
)
mockCaller
=
await
mockCallerFactory
.
deploy
()
})
describe
(
'
call assertions for functions
'
,
()
=>
{
it
(
'
should be able to make assertions about a non-overloaded function
'
,
async
()
=>
{
mock
.
smocked
.
getInputtedUint256
.
will
.
return
.
with
(
0
)
const
expected1
=
ethers
.
BigNumber
.
from
(
1234
)
await
mockCaller
.
callMock
(
mock
.
address
,
mock
.
interface
.
encodeFunctionData
(
'
getInputtedUint256(uint256)
'
,
[
expected1
,
])
)
expect
(
mock
.
smocked
.
getInputtedUint256
.
calls
[
0
]).
to
.
deep
.
equal
([
expected1
,
])
})
it
(
'
should be able to make assertions about both versions of an overloaded function
'
,
async
()
=>
{
mock
.
smocked
[
'
overloadedFunction(uint256)
'
].
will
.
return
.
with
(
0
)
mock
.
smocked
[
'
overloadedFunction(uint256,uint256)
'
].
will
.
return
.
with
(
0
)
const
expected1
=
ethers
.
BigNumber
.
from
(
1234
)
await
mockCaller
.
callMock
(
mock
.
address
,
mock
.
interface
.
encodeFunctionData
(
'
overloadedFunction(uint256)
'
,
[
expected1
,
])
)
expect
(
mock
.
smocked
[
'
overloadedFunction(uint256)
'
].
calls
[
0
]
).
to
.
deep
.
equal
([
expected1
])
const
expected2
=
ethers
.
BigNumber
.
from
(
5678
)
await
mockCaller
.
callMock
(
mock
.
address
,
mock
.
interface
.
encodeFunctionData
(
'
overloadedFunction(uint256,uint256)
'
,
[
expected2
,
expected2
]
)
)
expect
(
mock
.
smocked
[
'
overloadedFunction(uint256,uint256)
'
].
calls
[
0
]
).
to
.
deep
.
equal
([
expected2
,
expected2
])
})
})
})
yarn.lock
View file @
e4103622
...
...
@@ -2248,6 +2248,13 @@
dependencies:
"@types/chai" "*"
"@types/chai-as-promised@^7.1.4":
version "7.1.4"
resolved "https://registry.yarnpkg.com/@types/chai-as-promised/-/chai-as-promised-7.1.4.tgz#caf64e76fb056b8c8ced4b761ed499272b737601"
integrity sha512-1y3L1cHePcIm5vXkh1DSGf/zQq5n5xDKG1fpCvf18+uOkpce0Z1ozNFPkyWsVswK7ntN1sZBw3oU6gmN+pDUcA==
dependencies:
"@types/chai" "*"
"@types/chai@*", "@types/chai@^4.1.7":
version "4.2.16"
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.16.tgz#f09cc36e18d28274f942e7201147cce34d97e8c8"
...
...
@@ -7178,6 +7185,57 @@ hardhat@^2.2.1:
uuid "^3.3.2"
ws "^7.2.1"
hardhat@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.3.0.tgz#5c29f8b4d08155c3dc8c908af9713fd5079522d5"
integrity sha512-nc4ro2bM4wPaA6/0Y22o5F5QrifQk2KCyPUUKLPUeFFZoGNGYB8vmeW/k9gV9DdMukdWTzfYlKc2Jn4bfb6tDQ==
dependencies:
"@ethereumjs/block" "^3.2.1"
"@ethereumjs/blockchain" "^5.2.1"
"@ethereumjs/common" "^2.2.0"
"@ethereumjs/tx" "^3.1.3"
"@ethereumjs/vm" "^5.3.2"
"@sentry/node" "^5.18.1"
"@solidity-parser/parser" "^0.11.0"
"@types/bn.js" "^5.1.0"
"@types/lru-cache" "^5.1.0"
abort-controller "^3.0.0"
adm-zip "^0.4.16"
ansi-escapes "^4.3.0"
chalk "^2.4.2"
chokidar "^3.4.0"
ci-info "^2.0.0"
debug "^4.1.1"
enquirer "^2.3.0"
env-paths "^2.2.0"
eth-sig-util "^2.5.2"
ethereum-cryptography "^0.1.2"
ethereumjs-abi "^0.6.8"
ethereumjs-util "^7.0.10"
find-up "^2.1.0"
fp-ts "1.19.3"
fs-extra "^7.0.1"
glob "^7.1.3"
immutable "^4.0.0-rc.12"
io-ts "1.10.4"
lodash "^4.17.11"
merkle-patricia-tree "^4.1.0"
mnemonist "^0.38.0"
mocha "^7.1.2"
node-fetch "^2.6.0"
qs "^6.7.0"
raw-body "^2.4.1"
resolve "1.17.0"
semver "^6.3.0"
slash "^3.0.0"
solc "0.7.3"
source-map-support "^0.5.13"
stacktrace-parser "^0.1.10"
"true-case-path" "^2.2.1"
tsort "0.0.1"
uuid "^3.3.2"
ws "^7.2.1"
has-ansi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
...
...
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