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
59f3574c
Unverified
Commit
59f3574c
authored
Mar 13, 2023
by
mergify[bot]
Committed by
GitHub
Mar 13, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into opstack-docs
parents
b0fd19f2
b5e4ab7e
Changes
12
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
387 additions
and
213 deletions
+387
-213
wicked-ads-pump.md
.changeset/wicked-ads-pump.md
+5
-0
cli.go
op-chain-ops/ether/cli.go
+0
-10
migrate.go
op-chain-ops/ether/migrate.go
+109
-30
migrate_test.go
op-chain-ops/ether/migrate_test.go
+224
-0
precheck.go
op-chain-ops/ether/precheck.go
+0
-155
check.go
op-chain-ops/genesis/check.go
+1
-6
db_migration.go
op-chain-ops/genesis/db_migration.go
+7
-12
.env.example
packages/contracts-bedrock/.env.example
+4
-0
getting-started.json
...ages/contracts-bedrock/deploy-config/getting-started.json
+1
-0
020-SystemDictatorSteps-1.ts
...ges/contracts-bedrock/deploy/020-SystemDictatorSteps-1.ts
+4
-0
021-SystemDictatorSteps-2.ts
...ges/contracts-bedrock/deploy/021-SystemDictatorSteps-2.ts
+4
-0
deploy-utils.ts
packages/contracts-bedrock/src/deploy-utils.ts
+28
-0
No files found.
.changeset/wicked-ads-pump.md
0 → 100644
View file @
59f3574c
---
'
@eth-optimism/contracts-bedrock'
:
patch
---
Print tenderly simulation links during deployment
op-chain-ops/ether/cli.go
View file @
59f3574c
...
@@ -20,13 +20,3 @@ func getOVMETHTotalSupplySlot() common.Hash {
...
@@ -20,13 +20,3 @@ func getOVMETHTotalSupplySlot() common.Hash {
key
:=
common
.
BytesToHash
(
common
.
LeftPadBytes
(
position
.
Bytes
(),
32
))
key
:=
common
.
BytesToHash
(
common
.
LeftPadBytes
(
position
.
Bytes
(),
32
))
return
key
return
key
}
}
func
GetOVMETHTotalSupplySlot
()
common
.
Hash
{
return
getOVMETHTotalSupplySlot
()
}
// getOVMETHBalance gets a user's OVM ETH balance from state by querying the
// appropriate storage slot directly.
func
getOVMETHBalance
(
db
*
state
.
StateDB
,
addr
common
.
Address
)
*
big
.
Int
{
return
db
.
GetState
(
OVMETHAddress
,
CalcOVMETHStorageKey
(
addr
))
.
Big
()
}
op-chain-ops/ether/migrate.go
View file @
59f3574c
...
@@ -17,7 +17,7 @@ var (
...
@@ -17,7 +17,7 @@ var (
// OVMETHAddress is the address of the OVM ETH predeploy.
// OVMETHAddress is the address of the OVM ETH predeploy.
OVMETHAddress
=
common
.
HexToAddress
(
"0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000"
)
OVMETHAddress
=
common
.
HexToAddress
(
"0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000"
)
i
gnoredSlots
=
map
[
common
.
Hash
]
bool
{
OVMETHI
gnoredSlots
=
map
[
common
.
Hash
]
bool
{
// Total Supply
// Total Supply
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000002"
)
:
true
,
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000002"
)
:
true
,
// Name
// Name
...
@@ -29,7 +29,14 @@ var (
...
@@ -29,7 +29,14 @@ var (
}
}
)
)
func
MigrateLegacyETH
(
db
*
state
.
StateDB
,
addresses
[]
common
.
Address
,
chainID
int
,
noCheck
bool
)
error
{
// MigrateLegacyETH checks that the given list of addresses and allowances represents all storage
// slots in the LegacyERC20ETH contract. We don't have to filter out extra addresses like we do for
// withdrawals because we'll simply carry the balance of a given address to the new system, if the
// account is extra then it won't have any balance and nothing will happen. For each valid balance,
// this method will migrate into state. This method does the checking as part of the migration loop
// in order to avoid having to iterate over state twice. This saves approximately 40 minutes during
// the mainnet migration.
func
MigrateLegacyETH
(
db
*
state
.
StateDB
,
addresses
[]
common
.
Address
,
allowances
[]
*
crossdomain
.
Allowance
,
chainID
int
,
noCheck
bool
,
dryRun
bool
)
error
{
// Chain params to use for integrity checking.
// Chain params to use for integrity checking.
params
:=
crossdomain
.
ParamsByChainID
[
chainID
]
params
:=
crossdomain
.
ParamsByChainID
[
chainID
]
if
params
==
nil
{
if
params
==
nil
{
...
@@ -39,38 +46,99 @@ func MigrateLegacyETH(db *state.StateDB, addresses []common.Address, chainID int
...
@@ -39,38 +46,99 @@ func MigrateLegacyETH(db *state.StateDB, addresses []common.Address, chainID int
// Log the chain params for debugging purposes.
// Log the chain params for debugging purposes.
log
.
Info
(
"Chain params"
,
"chain-id"
,
chainID
,
"supply-delta"
,
params
.
ExpectedSupplyDelta
)
log
.
Info
(
"Chain params"
,
"chain-id"
,
chainID
,
"supply-delta"
,
params
.
ExpectedSupplyDelta
)
// Deduplicate the list of addresses by converting to a map.
return
doMigration
(
db
,
addresses
,
allowances
,
params
.
ExpectedSupplyDelta
,
noCheck
,
dryRun
)
deduped
:=
make
(
map
[
common
.
Address
]
bool
)
}
func
doMigration
(
db
*
state
.
StateDB
,
addresses
[]
common
.
Address
,
allowances
[]
*
crossdomain
.
Allowance
,
expSupplyDiff
*
big
.
Int
,
noCheck
bool
,
dryRun
bool
)
error
{
// We'll need to maintain a list of all addresses that we've seen along with all of the storage
// slots based on the witness data.
slotsAddrs
:=
make
(
map
[
common
.
Hash
]
common
.
Address
)
slotTypes
:=
make
(
map
[
common
.
Hash
]
int
)
// For each known address, compute its balance key and add it to the list of addresses.
// Mint events are instrumented as regular ETH events in the witness data, so we no longer
// need to iterate over mint events during the migration.
for
_
,
addr
:=
range
addresses
{
for
_
,
addr
:=
range
addresses
{
deduped
[
addr
]
=
true
sk
:=
CalcOVMETHStorageKey
(
addr
)
slotTypes
[
sk
]
=
1
slotsAddrs
[
sk
]
=
addr
}
}
// Migrate the legacy ETH to ETH.
// For each known allowance, compute its storage key and add it to the list of addresses.
for
_
,
allowance
:=
range
allowances
{
slotTypes
[
CalcAllowanceStorageKey
(
allowance
.
From
,
allowance
.
To
)]
=
2
}
// Add the old SequencerEntrypoint because someone sent it ETH a long time ago and it has a
// balance but none of our instrumentation could easily find it. Special case.
sequencerEntrypointAddr
:=
common
.
HexToAddress
(
"0x4200000000000000000000000000000000000005"
)
slotTypes
[
CalcOVMETHStorageKey
(
sequencerEntrypointAddr
)]
=
1
// Migrate the OVM_ETH to ETH.
log
.
Info
(
"Migrating legacy ETH to ETH"
,
"num-accounts"
,
len
(
addresses
))
log
.
Info
(
"Migrating legacy ETH to ETH"
,
"num-accounts"
,
len
(
addresses
))
totalMigrated
:=
new
(
big
.
Int
)
totalMigrated
:=
new
(
big
.
Int
)
logAccountProgress
:=
util
.
ProgressLogger
(
1000
,
"imported accounts"
)
logAccountProgress
:=
util
.
ProgressLogger
(
1000
,
"imported OVM_ETH storage slot"
)
for
addr
:=
range
deduped
{
var
innerErr
error
// No accounts should have a balance in state. If they do, bail.
err
:=
db
.
ForEachStorage
(
predeploys
.
LegacyERC20ETHAddr
,
func
(
key
,
value
common
.
Hash
)
bool
{
defer
logAccountProgress
()
// We can safely ignore specific slots (totalSupply, name, symbol).
if
OVMETHIgnoredSlots
[
key
]
{
return
true
}
// Look up the slot type.
slotType
,
ok
:=
slotTypes
[
key
]
if
!
ok
{
log
.
Error
(
"unknown storage slot in state"
,
"slot"
,
key
.
String
())
if
!
noCheck
{
innerErr
=
fmt
.
Errorf
(
"unknown storage slot in state: %s"
,
key
.
String
())
return
false
}
}
switch
slotType
{
case
1
:
// Balance slot.
bal
:=
value
.
Big
()
totalMigrated
.
Add
(
totalMigrated
,
bal
)
addr
:=
slotsAddrs
[
key
]
// There should never be any balances in state, so verify that here.
if
db
.
GetBalance
(
addr
)
.
Sign
()
>
0
{
if
db
.
GetBalance
(
addr
)
.
Sign
()
>
0
{
if
noCheck
{
log
.
Error
(
"account has non-zero balance in state - should never happen"
,
"addr"
,
addr
)
log
.
Error
(
"account has non-zero balance in state - should never happen"
,
"addr"
,
addr
)
}
else
{
if
!
noCheck
{
log
.
Crit
(
"account has non-zero balance in state - should never happen"
,
"addr"
,
addr
)
innerErr
=
fmt
.
Errorf
(
"account has non-zero balance in state - should never happen: %s"
,
addr
)
return
false
}
}
}
}
// Pull out the OVM ETH balance.
if
dryRun
{
ovmBalance
:=
getOVMETHBalance
(
db
,
addr
)
return
true
}
// Actually perform the migration by setting the appropriate values in state.
db
.
SetBalance
(
addr
,
ovmBalance
)
db
.
SetState
(
predeploys
.
LegacyERC20ETHAddr
,
CalcOVMETHStorageKey
(
addr
),
common
.
Hash
{})
// Bump the total OVM balance.
// Set the balance, and delete the legacy slot.
totalMigrated
=
totalMigrated
.
Add
(
totalMigrated
,
ovmBalance
)
db
.
SetBalance
(
addr
,
bal
)
db
.
SetState
(
predeploys
.
LegacyERC20ETHAddr
,
key
,
common
.
Hash
{})
case
2
:
// Allowance slot. Nothing to do here.
return
true
default
:
// Should never happen.
log
.
Error
(
"unknown slot type"
,
"slot"
,
key
.
String
(),
"type"
,
slotType
)
if
!
noCheck
{
innerErr
=
fmt
.
Errorf
(
"unknown slot type: %d"
,
slotType
)
return
false
}
}
// Log progress.
return
true
logAccountProgress
()
})
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to iterate over OVM_ETH storage: %w"
,
err
)
}
if
innerErr
!=
nil
{
return
fmt
.
Errorf
(
"error in migration: %w"
,
innerErr
)
}
}
// Make sure that the total supply delta matches the expected delta. This is equivalent to
// Make sure that the total supply delta matches the expected delta. This is equivalent to
...
@@ -78,33 +146,44 @@ func MigrateLegacyETH(db *state.StateDB, addresses []common.Address, chainID int
...
@@ -78,33 +146,44 @@ func MigrateLegacyETH(db *state.StateDB, addresses []common.Address, chainID int
// same check against the total found (a = b, b = c => a = c).
// same check against the total found (a = b, b = c => a = c).
totalSupply
:=
getOVMETHTotalSupply
(
db
)
totalSupply
:=
getOVMETHTotalSupply
(
db
)
delta
:=
new
(
big
.
Int
)
.
Sub
(
totalSupply
,
totalMigrated
)
delta
:=
new
(
big
.
Int
)
.
Sub
(
totalSupply
,
totalMigrated
)
if
delta
.
Cmp
(
params
.
ExpectedSupplyDelta
)
!=
0
{
if
delta
.
Cmp
(
expSupplyDiff
)
!=
0
{
if
noCheck
{
if
noCheck
{
log
.
Error
(
log
.
Error
(
"supply mismatch"
,
"supply mismatch"
,
"migrated"
,
totalMigrated
.
String
(),
"migrated"
,
totalMigrated
.
String
(),
"supply"
,
totalSupply
.
String
(),
"supply"
,
totalSupply
.
String
(),
"delta"
,
delta
.
String
(),
"delta"
,
delta
.
String
(),
"exp_delta"
,
params
.
ExpectedSupplyDelta
.
String
(),
"exp_delta"
,
expSupplyDiff
.
String
(),
)
)
}
else
{
}
else
{
log
.
Crit
(
log
.
Error
(
"supply mismatch"
,
"supply mismatch"
,
"migrated"
,
totalMigrated
.
String
(),
"migrated"
,
totalMigrated
.
String
(),
"supply"
,
totalSupply
.
String
(),
"supply"
,
totalSupply
.
String
(),
"delta"
,
delta
.
String
(),
"delta"
,
delta
.
String
(),
"exp_delta"
,
params
.
ExpectedSupplyDelta
.
String
(),
"exp_delta"
,
expSupplyDiff
.
String
(),
)
)
return
fmt
.
Errorf
(
"supply mismatch: exp delta %s != %s"
,
expSupplyDiff
.
String
(),
delta
.
String
())
}
}
}
}
// Supply is verified.
log
.
Info
(
"supply verified OK"
,
"migrated"
,
totalMigrated
.
String
(),
"supply"
,
totalSupply
.
String
(),
"delta"
,
delta
.
String
(),
"exp_delta"
,
expSupplyDiff
.
String
(),
)
// Set the total supply to 0. We do this because the total supply is necessarily going to be
// Set the total supply to 0. We do this because the total supply is necessarily going to be
// different than the sum of all balances since we no longer track balances inside the contract
// different than the sum of all balances since we no longer track balances inside the contract
// itself. The total supply is going to be weird no matter what, might as well set it to zero
// itself. The total supply is going to be weird no matter what, might as well set it to zero
// so it's explicitly weird instead of implicitly weird.
// so it's explicitly weird instead of implicitly weird.
if
!
dryRun
{
db
.
SetState
(
predeploys
.
LegacyERC20ETHAddr
,
getOVMETHTotalSupplySlot
(),
common
.
Hash
{})
db
.
SetState
(
predeploys
.
LegacyERC20ETHAddr
,
getOVMETHTotalSupplySlot
(),
common
.
Hash
{})
log
.
Info
(
"Set the totalSupply to 0"
)
log
.
Info
(
"Set the totalSupply to 0"
)
}
// Fin.
return
nil
return
nil
}
}
op-chain-ops/ether/migrate_test.go
0 → 100644
View file @
59f3574c
package
ether
import
(
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/trie"
"github.com/stretchr/testify/require"
)
func
TestMigrateLegacyETH
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
totalSupply
*
big
.
Int
expDiff
*
big
.
Int
stateBalances
map
[
common
.
Address
]
*
big
.
Int
stateAllowances
map
[
common
.
Address
]
common
.
Address
inputAddresses
[]
common
.
Address
inputAllowances
[]
*
crossdomain
.
Allowance
check
func
(
t
*
testing
.
T
,
db
*
state
.
StateDB
,
err
error
)
}{
{
name
:
"everything matches"
,
totalSupply
:
big
.
NewInt
(
3
),
expDiff
:
big
.
NewInt
(
0
),
stateBalances
:
map
[
common
.
Address
]
*
big
.
Int
{
common
.
HexToAddress
(
"0x123"
)
:
big
.
NewInt
(
1
),
common
.
HexToAddress
(
"0x456"
)
:
big
.
NewInt
(
2
),
},
stateAllowances
:
map
[
common
.
Address
]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
)
:
common
.
HexToAddress
(
"0x456"
),
},
inputAddresses
:
[]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
),
common
.
HexToAddress
(
"0x456"
),
},
inputAllowances
:
[]
*
crossdomain
.
Allowance
{
{
From
:
common
.
HexToAddress
(
"0x123"
),
To
:
common
.
HexToAddress
(
"0x456"
),
},
},
check
:
func
(
t
*
testing
.
T
,
db
*
state
.
StateDB
,
err
error
)
{
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
db
.
GetBalance
(
common
.
HexToAddress
(
"0x123"
)),
big
.
NewInt
(
1
))
require
.
Equal
(
t
,
db
.
GetBalance
(
common
.
HexToAddress
(
"0x456"
)),
big
.
NewInt
(
2
))
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
CalcOVMETHStorageKey
(
common
.
HexToAddress
(
"0x123"
))),
common
.
Hash
{})
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
CalcOVMETHStorageKey
(
common
.
HexToAddress
(
"0x456"
))),
common
.
Hash
{})
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
getOVMETHTotalSupplySlot
()),
common
.
Hash
{})
},
},
{
name
:
"extra input addresses"
,
totalSupply
:
big
.
NewInt
(
1
),
expDiff
:
big
.
NewInt
(
0
),
stateBalances
:
map
[
common
.
Address
]
*
big
.
Int
{
common
.
HexToAddress
(
"0x123"
)
:
big
.
NewInt
(
1
),
},
inputAddresses
:
[]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
),
common
.
HexToAddress
(
"0x456"
),
},
check
:
func
(
t
*
testing
.
T
,
db
*
state
.
StateDB
,
err
error
)
{
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
db
.
GetBalance
(
common
.
HexToAddress
(
"0x123"
)),
big
.
NewInt
(
1
))
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
CalcOVMETHStorageKey
(
common
.
HexToAddress
(
"0x123"
))),
common
.
Hash
{})
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
getOVMETHTotalSupplySlot
()),
common
.
Hash
{})
},
},
{
name
:
"extra input allowances"
,
totalSupply
:
big
.
NewInt
(
1
),
expDiff
:
big
.
NewInt
(
0
),
stateBalances
:
map
[
common
.
Address
]
*
big
.
Int
{
common
.
HexToAddress
(
"0x123"
)
:
big
.
NewInt
(
1
),
},
stateAllowances
:
map
[
common
.
Address
]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
)
:
common
.
HexToAddress
(
"0x456"
),
},
inputAddresses
:
[]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
),
common
.
HexToAddress
(
"0x456"
),
},
inputAllowances
:
[]
*
crossdomain
.
Allowance
{
{
From
:
common
.
HexToAddress
(
"0x123"
),
To
:
common
.
HexToAddress
(
"0x456"
),
},
{
From
:
common
.
HexToAddress
(
"0x123"
),
To
:
common
.
HexToAddress
(
"0x789"
),
},
},
check
:
func
(
t
*
testing
.
T
,
db
*
state
.
StateDB
,
err
error
)
{
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
db
.
GetBalance
(
common
.
HexToAddress
(
"0x123"
)),
big
.
NewInt
(
1
))
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
CalcOVMETHStorageKey
(
common
.
HexToAddress
(
"0x123"
))),
common
.
Hash
{})
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
getOVMETHTotalSupplySlot
()),
common
.
Hash
{})
},
},
{
name
:
"missing input addresses"
,
totalSupply
:
big
.
NewInt
(
2
),
expDiff
:
big
.
NewInt
(
0
),
stateBalances
:
map
[
common
.
Address
]
*
big
.
Int
{
common
.
HexToAddress
(
"0x123"
)
:
big
.
NewInt
(
1
),
common
.
HexToAddress
(
"0x456"
)
:
big
.
NewInt
(
1
),
},
inputAddresses
:
[]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
),
},
check
:
func
(
t
*
testing
.
T
,
db
*
state
.
StateDB
,
err
error
)
{
require
.
Error
(
t
,
err
)
require
.
ErrorContains
(
t
,
err
,
"unknown storage slot"
)
},
},
{
name
:
"missing input allowances"
,
totalSupply
:
big
.
NewInt
(
2
),
expDiff
:
big
.
NewInt
(
0
),
stateBalances
:
map
[
common
.
Address
]
*
big
.
Int
{
common
.
HexToAddress
(
"0x123"
)
:
big
.
NewInt
(
1
),
},
stateAllowances
:
map
[
common
.
Address
]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
)
:
common
.
HexToAddress
(
"0x456"
),
common
.
HexToAddress
(
"0x123"
)
:
common
.
HexToAddress
(
"0x789"
),
},
inputAddresses
:
[]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
),
},
inputAllowances
:
[]
*
crossdomain
.
Allowance
{
{
From
:
common
.
HexToAddress
(
"0x123"
),
To
:
common
.
HexToAddress
(
"0x456"
),
},
},
check
:
func
(
t
*
testing
.
T
,
db
*
state
.
StateDB
,
err
error
)
{
require
.
Error
(
t
,
err
)
require
.
ErrorContains
(
t
,
err
,
"unknown storage slot"
)
},
},
{
name
:
"bad supply diff"
,
totalSupply
:
big
.
NewInt
(
4
),
expDiff
:
big
.
NewInt
(
0
),
stateBalances
:
map
[
common
.
Address
]
*
big
.
Int
{
common
.
HexToAddress
(
"0x123"
)
:
big
.
NewInt
(
1
),
common
.
HexToAddress
(
"0x456"
)
:
big
.
NewInt
(
2
),
},
inputAddresses
:
[]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
),
common
.
HexToAddress
(
"0x456"
),
},
check
:
func
(
t
*
testing
.
T
,
db
*
state
.
StateDB
,
err
error
)
{
require
.
Error
(
t
,
err
)
require
.
ErrorContains
(
t
,
err
,
"supply mismatch"
)
},
},
{
name
:
"good supply diff"
,
totalSupply
:
big
.
NewInt
(
4
),
expDiff
:
big
.
NewInt
(
1
),
stateBalances
:
map
[
common
.
Address
]
*
big
.
Int
{
common
.
HexToAddress
(
"0x123"
)
:
big
.
NewInt
(
1
),
common
.
HexToAddress
(
"0x456"
)
:
big
.
NewInt
(
2
),
},
inputAddresses
:
[]
common
.
Address
{
common
.
HexToAddress
(
"0x123"
),
common
.
HexToAddress
(
"0x456"
),
},
check
:
func
(
t
*
testing
.
T
,
db
*
state
.
StateDB
,
err
error
)
{
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
db
.
GetBalance
(
common
.
HexToAddress
(
"0x123"
)),
big
.
NewInt
(
1
))
require
.
Equal
(
t
,
db
.
GetBalance
(
common
.
HexToAddress
(
"0x456"
)),
big
.
NewInt
(
2
))
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
CalcOVMETHStorageKey
(
common
.
HexToAddress
(
"0x123"
))),
common
.
Hash
{})
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
CalcOVMETHStorageKey
(
common
.
HexToAddress
(
"0x456"
))),
common
.
Hash
{})
require
.
Equal
(
t
,
db
.
GetState
(
OVMETHAddress
,
getOVMETHTotalSupplySlot
()),
common
.
Hash
{})
},
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
db
:=
makeLegacyETH
(
t
,
tt
.
totalSupply
,
tt
.
stateBalances
,
tt
.
stateAllowances
)
err
:=
doMigration
(
db
,
tt
.
inputAddresses
,
tt
.
inputAllowances
,
tt
.
expDiff
,
false
,
false
)
tt
.
check
(
t
,
db
,
err
)
})
}
}
func
makeLegacyETH
(
t
*
testing
.
T
,
totalSupply
*
big
.
Int
,
balances
map
[
common
.
Address
]
*
big
.
Int
,
allowances
map
[
common
.
Address
]
common
.
Address
)
*
state
.
StateDB
{
db
,
err
:=
state
.
New
(
common
.
Hash
{},
state
.
NewDatabaseWithConfig
(
rawdb
.
NewMemoryDatabase
(),
&
trie
.
Config
{
Preimages
:
true
,
Cache
:
1024
,
}),
nil
)
require
.
NoError
(
t
,
err
)
db
.
CreateAccount
(
OVMETHAddress
)
db
.
SetState
(
OVMETHAddress
,
getOVMETHTotalSupplySlot
(),
common
.
BigToHash
(
totalSupply
))
for
slot
:=
range
OVMETHIgnoredSlots
{
if
slot
==
getOVMETHTotalSupplySlot
()
{
continue
}
db
.
SetState
(
OVMETHAddress
,
slot
,
common
.
Hash
{
31
:
0xff
})
}
for
addr
,
balance
:=
range
balances
{
db
.
SetState
(
OVMETHAddress
,
CalcOVMETHStorageKey
(
addr
),
common
.
BigToHash
(
balance
))
}
for
from
,
to
:=
range
allowances
{
db
.
SetState
(
OVMETHAddress
,
CalcAllowanceStorageKey
(
from
,
to
),
common
.
BigToHash
(
big
.
NewInt
(
1
)))
}
root
,
err
:=
db
.
Commit
(
false
)
require
.
NoError
(
t
,
err
)
err
=
db
.
Database
()
.
TrieDB
()
.
Commit
(
root
,
true
)
require
.
NoError
(
t
,
err
)
return
db
}
op-chain-ops/ether/precheck.go
deleted
100644 → 0
View file @
b0fd19f2
package
ether
import
(
"errors"
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
"github.com/ethereum-optimism/optimism/op-chain-ops/util"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
)
// PreCheckBalances checks that the given list of addresses and allowances represents all storage
// slots in the LegacyERC20ETH contract. We don't have to filter out extra addresses like we do for
// withdrawals because we'll simply carry the balance of a given address to the new system, if the
// account is extra then it won't have any balance and nothing will happen.
func
PreCheckBalances
(
ldb
ethdb
.
Database
,
db
*
state
.
StateDB
,
addresses
[]
common
.
Address
,
allowances
[]
*
crossdomain
.
Allowance
,
chainID
int
,
noCheck
bool
)
([]
common
.
Address
,
error
)
{
// Chain params to use for integrity checking.
params
:=
crossdomain
.
ParamsByChainID
[
chainID
]
if
params
==
nil
{
return
nil
,
fmt
.
Errorf
(
"no chain params for %d"
,
chainID
)
}
// We'll need to maintain a list of all addresses that we've seen along with all of the storage
// slots based on the witness data.
addrs
:=
make
([]
common
.
Address
,
0
)
slotsInp
:=
make
(
map
[
common
.
Hash
]
int
)
// For each known address, compute its balance key and add it to the list of addresses.
// Mint events are instrumented as regular ETH events in the witness data, so we no longer
// need to iterate over mint events during the migration.
for
_
,
addr
:=
range
addresses
{
addrs
=
append
(
addrs
,
addr
)
slotsInp
[
CalcOVMETHStorageKey
(
addr
)]
=
1
}
// For each known allowance, compute its storage key and add it to the list of addresses.
for
_
,
allowance
:=
range
allowances
{
addrs
=
append
(
addrs
,
allowance
.
From
)
slotsInp
[
CalcAllowanceStorageKey
(
allowance
.
From
,
allowance
.
To
)]
=
2
}
// Add the old SequencerEntrypoint because someone sent it ETH a long time ago and it has a
// balance but none of our instrumentation could easily find it. Special case.
sequencerEntrypointAddr
:=
common
.
HexToAddress
(
"0x4200000000000000000000000000000000000005"
)
addrs
=
append
(
addrs
,
sequencerEntrypointAddr
)
slotsInp
[
CalcOVMETHStorageKey
(
sequencerEntrypointAddr
)]
=
1
// Build a mapping of every storage slot in the LegacyERC20ETH contract, except the list of
// slots that we know we can ignore (totalSupply, name, symbol).
var
count
int
slotsAct
:=
make
(
map
[
common
.
Hash
]
common
.
Hash
)
progress
:=
util
.
ProgressLogger
(
1000
,
"Read OVM_ETH storage slot"
)
err
:=
db
.
ForEachStorage
(
predeploys
.
LegacyERC20ETHAddr
,
func
(
key
,
value
common
.
Hash
)
bool
{
progress
()
// We can safely ignore specific slots (totalSupply, name, symbol).
if
ignoredSlots
[
key
]
{
return
true
}
// Slot exists, so add it to the map.
slotsAct
[
key
]
=
value
count
++
return
true
})
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"cannot iterate over LegacyERC20ETHAddr: %w"
,
err
)
}
// Log how many slots were iterated over.
log
.
Info
(
"Iterated legacy balances"
,
"count"
,
count
)
// Iterate over the list of known slots and check that we have a slot for each one. We'll also
// keep track of the total balance to be migrated and throw if the total supply exceeds the
// expected supply delta.
totalFound
:=
new
(
big
.
Int
)
var
unknown
bool
for
slot
:=
range
slotsAct
{
slotType
,
ok
:=
slotsInp
[
slot
]
if
!
ok
{
if
noCheck
{
log
.
Error
(
"ignoring unknown storage slot in state"
,
"slot"
,
slot
.
String
())
}
else
{
unknown
=
true
log
.
Error
(
"unknown storage slot in state"
,
"slot"
,
slot
.
String
())
continue
}
}
// Add balances to the total found.
switch
slotType
{
case
1
:
// Balance slot.
totalFound
.
Add
(
totalFound
,
slotsAct
[
slot
]
.
Big
())
case
2
:
// Allowance slot.
continue
default
:
// Should never happen.
if
noCheck
{
log
.
Error
(
"unknown slot type"
,
"slot"
,
slot
,
"type"
,
slotType
)
}
else
{
log
.
Crit
(
"unknown slot type: %d"
,
slotType
)
}
}
}
if
unknown
{
return
nil
,
errors
.
New
(
"unknown storage slots in state (see logs for details)"
)
}
// Verify the supply delta. Recorded total supply in the LegacyERC20ETH contract may be higher
// than the actual migrated amount because self-destructs will remove ETH supply in a way that
// cannot be reflected in the contract. This is fine because self-destructs just mean the L2 is
// actually *overcollateralized* by some tiny amount.
totalSupply
:=
getOVMETHTotalSupply
(
db
)
delta
:=
new
(
big
.
Int
)
.
Sub
(
totalSupply
,
totalFound
)
if
delta
.
Cmp
(
params
.
ExpectedSupplyDelta
)
!=
0
{
if
noCheck
{
log
.
Error
(
"supply mismatch"
,
"migrated"
,
totalFound
.
String
(),
"supply"
,
totalSupply
.
String
(),
"delta"
,
delta
.
String
(),
"exp_delta"
,
params
.
ExpectedSupplyDelta
.
String
(),
)
}
else
{
log
.
Crit
(
"supply mismatch"
,
"migrated"
,
totalFound
.
String
(),
"supply"
,
totalSupply
.
String
(),
"delta"
,
delta
.
String
(),
"exp_delta"
,
params
.
ExpectedSupplyDelta
.
String
(),
)
}
}
// Supply is verified.
log
.
Info
(
"supply verified OK"
,
"migrated"
,
totalFound
.
String
(),
"supply"
,
totalSupply
.
String
(),
"delta"
,
delta
.
String
(),
"exp_delta"
,
params
.
ExpectedSupplyDelta
.
String
(),
)
// We know we have at least a superset of all addresses here since we know that we have every
// storage slot. It's fine to have extras because they won't have any balance.
return
addrs
,
nil
}
op-chain-ops/genesis/check.go
View file @
59f3574c
...
@@ -31,7 +31,6 @@ const MaxSlotChecks = 1000
...
@@ -31,7 +31,6 @@ const MaxSlotChecks = 1000
type
StorageCheckMap
=
map
[
common
.
Hash
]
common
.
Hash
type
StorageCheckMap
=
map
[
common
.
Hash
]
common
.
Hash
var
(
var
(
L2XDMOwnerSlot
=
common
.
Hash
{
31
:
0x33
}
ProxyAdminOwnerSlot
=
common
.
Hash
{}
ProxyAdminOwnerSlot
=
common
.
Hash
{}
LegacyETHCheckSlots
=
map
[
common
.
Hash
]
common
.
Hash
{
LegacyETHCheckSlots
=
map
[
common
.
Hash
]
common
.
Hash
{
...
@@ -53,10 +52,6 @@ var (
...
@@ -53,10 +52,6 @@ var (
predeploys
.
L2CrossDomainMessengerAddr
:
{
predeploys
.
L2CrossDomainMessengerAddr
:
{
// Slot 0x00 (0) is a combination of spacer_0_0_20, _initialized, and _initializing
// Slot 0x00 (0) is a combination of spacer_0_0_20, _initialized, and _initializing
common
.
Hash
{}
:
common
.
HexToHash
(
"0x0000000000000000000000010000000000000000000000000000000000000000"
),
common
.
Hash
{}
:
common
.
HexToHash
(
"0x0000000000000000000000010000000000000000000000000000000000000000"
),
// Slot 0x33 (51) is _owner. Requires custom check, so set to a garbage value
L2XDMOwnerSlot
:
common
.
HexToHash
(
"0xbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad0"
),
// Slot 0x97 (151) is _status
common
.
Hash
{
31
:
0x97
}
:
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000001"
),
// Slot 0xcc (204) is xDomainMsgSender
// Slot 0xcc (204) is xDomainMsgSender
common
.
Hash
{
31
:
0xcc
}
:
common
.
HexToHash
(
"0x000000000000000000000000000000000000000000000000000000000000dead"
),
common
.
Hash
{
31
:
0xcc
}
:
common
.
HexToHash
(
"0x000000000000000000000000000000000000000000000000000000000000dead"
),
// EIP-1967 storage slots
// EIP-1967 storage slots
...
@@ -346,7 +341,7 @@ func PostCheckPredeployStorage(db vm.StateDB, finalSystemOwner common.Address, p
...
@@ -346,7 +341,7 @@ func PostCheckPredeployStorage(db vm.StateDB, finalSystemOwner common.Address, p
for
key
,
value
:=
range
expSlots
{
for
key
,
value
:=
range
expSlots
{
// The owner slots for the L2XDM and ProxyAdmin are special cases.
// The owner slots for the L2XDM and ProxyAdmin are special cases.
// They are set to the final system owner in the config.
// They are set to the final system owner in the config.
if
(
*
addr
==
predeploys
.
L2CrossDomainMessengerAddr
&&
key
==
L2XDMOwnerSlot
)
||
(
*
addr
==
predeploys
.
ProxyAdminAddr
&&
key
==
ProxyAdminOwnerSlot
)
{
if
*
addr
==
predeploys
.
ProxyAdminAddr
&&
key
==
ProxyAdminOwnerSlot
{
actualOwner
:=
common
.
BytesToAddress
(
slots
[
key
]
.
Bytes
())
actualOwner
:=
common
.
BytesToAddress
(
slots
[
key
]
.
Bytes
())
if
actualOwner
!=
proxyAdminOwner
{
if
actualOwner
!=
proxyAdminOwner
{
return
fmt
.
Errorf
(
"expected owner for %s to be %s but got %s"
,
name
,
proxyAdminOwner
,
actualOwner
)
return
fmt
.
Errorf
(
"expected owner for %s to be %s but got %s"
,
name
,
proxyAdminOwner
,
actualOwner
)
...
...
op-chain-ops/genesis/db_migration.go
View file @
59f3574c
...
@@ -134,16 +134,6 @@ func MigrateDB(ldb ethdb.Database, config *DeployConfig, l1Block *types.Block, m
...
@@ -134,16 +134,6 @@ func MigrateDB(ldb ethdb.Database, config *DeployConfig, l1Block *types.Block, m
filteredWithdrawals
=
crossdomain
.
SafeFilteredWithdrawals
(
unfilteredWithdrawals
)
filteredWithdrawals
=
crossdomain
.
SafeFilteredWithdrawals
(
unfilteredWithdrawals
)
}
}
// We also need to verify that we have all of the storage slots for the LegacyERC20ETH contract
// that we expect to have. An error will be thrown if there are any missing storage slots.
// Unlike with withdrawals, we do not need to filter out extra addresses because their balances
// would necessarily be zero and therefore not affect the migration.
log
.
Info
(
"Checking addresses..."
,
"no-check"
,
noCheck
)
addrs
,
err
:=
ether
.
PreCheckBalances
(
ldb
,
db
,
migrationData
.
Addresses
(),
migrationData
.
OvmAllowances
,
int
(
config
.
L1ChainID
),
noCheck
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"addresses mismatch: %w"
,
err
)
}
// At this point we've fully verified the witness data for the migration, so we can begin the
// At this point we've fully verified the witness data for the migration, so we can begin the
// actual migration process. This involves modifying parts of the legacy database and inserting
// actual migration process. This involves modifying parts of the legacy database and inserting
// a transition block.
// a transition block.
...
@@ -192,10 +182,15 @@ func MigrateDB(ldb ethdb.Database, config *DeployConfig, l1Block *types.Block, m
...
@@ -192,10 +182,15 @@ func MigrateDB(ldb ethdb.Database, config *DeployConfig, l1Block *types.Block, m
return
nil
,
fmt
.
Errorf
(
"cannot migrate withdrawals: %w"
,
err
)
return
nil
,
fmt
.
Errorf
(
"cannot migrate withdrawals: %w"
,
err
)
}
}
// Finally we migrate the balances held inside the LegacyERC20ETH contract into the state trie.
// We also need to verify that we have all of the storage slots for the LegacyERC20ETH contract
// that we expect to have. An error will be thrown if there are any missing storage slots.
// Unlike with withdrawals, we do not need to filter out extra addresses because their balances
// would necessarily be zero and therefore not affect the migration.
//
// Once verified, we migrate the balances held inside the LegacyERC20ETH contract into the state trie.
// We also delete the balances from the LegacyERC20ETH contract.
// We also delete the balances from the LegacyERC20ETH contract.
log
.
Info
(
"Starting to migrate ERC20 ETH"
)
log
.
Info
(
"Starting to migrate ERC20 ETH"
)
err
=
ether
.
MigrateLegacyETH
(
db
,
addrs
,
int
(
config
.
L1ChainID
),
noCheck
)
err
=
ether
.
MigrateLegacyETH
(
db
,
migrationData
.
Addresses
(),
migrationData
.
OvmAllowances
,
int
(
config
.
L1ChainID
),
noCheck
,
commit
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"cannot migrate legacy eth: %w"
,
err
)
return
nil
,
fmt
.
Errorf
(
"cannot migrate legacy eth: %w"
,
err
)
}
}
...
...
packages/contracts-bedrock/.env.example
View file @
59f3574c
...
@@ -3,3 +3,7 @@ L1_RPC=
...
@@ -3,3 +3,7 @@ L1_RPC=
# Private key for the deployer account
# Private key for the deployer account
PRIVATE_KEY_DEPLOYER=
PRIVATE_KEY_DEPLOYER=
# Optional Tenderly details for a simulation link during deployment
TENDERLY_PROJECT=
TENDERLY_USERNAME=
packages/contracts-bedrock/deploy-config/getting-started.json
View file @
59f3574c
...
@@ -41,6 +41,7 @@
...
@@ -41,6 +41,7 @@
"l2GenesisBlockGasLimit"
:
"0x17D7840"
,
"l2GenesisBlockGasLimit"
:
"0x17D7840"
,
"l2GenesisBlockBaseFeePerGas"
:
"0x3b9aca00"
,
"l2GenesisBlockBaseFeePerGas"
:
"0x3b9aca00"
,
"l2GenesisRegolithTimeOffset"
:
"0x0"
,
"eip1559Denominator"
:
50
,
"eip1559Denominator"
:
50
,
"eip1559Elasticity"
:
10
"eip1559Elasticity"
:
10
...
...
packages/contracts-bedrock/deploy/020-SystemDictatorSteps-1.ts
View file @
59f3574c
...
@@ -13,6 +13,7 @@ import {
...
@@ -13,6 +13,7 @@ import {
getDeploymentAddress
,
getDeploymentAddress
,
doStep
,
doStep
,
jsonifyTransaction
,
jsonifyTransaction
,
getTenderlySimulationLink
,
}
from
'
../src/deploy-utils
'
}
from
'
../src/deploy-utils
'
const
deployFn
:
DeployFunction
=
async
(
hre
)
=>
{
const
deployFn
:
DeployFunction
=
async
(
hre
)
=>
{
...
@@ -97,6 +98,7 @@ const deployFn: DeployFunction = async (hre) => {
...
@@ -97,6 +98,7 @@ const deployFn: DeployFunction = async (hre) => {
console
.
log
(
`MSD address:
${
SystemDictator
.
address
}
`
)
console
.
log
(
`MSD address:
${
SystemDictator
.
address
}
`
)
console
.
log
(
`JSON:`
)
console
.
log
(
`JSON:`
)
console
.
log
(
jsonifyTransaction
(
tx
))
console
.
log
(
jsonifyTransaction
(
tx
))
console
.
log
(
await
getTenderlySimulationLink
(
SystemDictator
.
provider
,
tx
))
}
}
// Wait for the ownership transfer to complete.
// Wait for the ownership transfer to complete.
...
@@ -133,6 +135,7 @@ const deployFn: DeployFunction = async (hre) => {
...
@@ -133,6 +135,7 @@ const deployFn: DeployFunction = async (hre) => {
console
.
log
(
`MSD address:
${
SystemDictator
.
address
}
`
)
console
.
log
(
`MSD address:
${
SystemDictator
.
address
}
`
)
console
.
log
(
`JSON:`
)
console
.
log
(
`JSON:`
)
console
.
log
(
jsonifyTransaction
(
tx
))
console
.
log
(
jsonifyTransaction
(
tx
))
console
.
log
(
await
getTenderlySimulationLink
(
SystemDictator
.
provider
,
tx
))
}
}
// Wait for the ownership transfer to complete.
// Wait for the ownership transfer to complete.
...
@@ -169,6 +172,7 @@ const deployFn: DeployFunction = async (hre) => {
...
@@ -169,6 +172,7 @@ const deployFn: DeployFunction = async (hre) => {
console
.
log
(
`MSD address:
${
SystemDictator
.
address
}
`
)
console
.
log
(
`MSD address:
${
SystemDictator
.
address
}
`
)
console
.
log
(
`JSON:`
)
console
.
log
(
`JSON:`
)
console
.
log
(
jsonifyTransaction
(
tx
))
console
.
log
(
jsonifyTransaction
(
tx
))
console
.
log
(
await
getTenderlySimulationLink
(
SystemDictator
.
provider
,
tx
))
}
}
// Wait for the ownership transfer to complete.
// Wait for the ownership transfer to complete.
...
...
packages/contracts-bedrock/deploy/021-SystemDictatorSteps-2.ts
View file @
59f3574c
...
@@ -13,6 +13,7 @@ import {
...
@@ -13,6 +13,7 @@ import {
jsonifyTransaction
,
jsonifyTransaction
,
isStep
,
isStep
,
doStep
,
doStep
,
getTenderlySimulationLink
,
}
from
'
../src/deploy-utils
'
}
from
'
../src/deploy-utils
'
const
deployFn
:
DeployFunction
=
async
(
hre
)
=>
{
const
deployFn
:
DeployFunction
=
async
(
hre
)
=>
{
...
@@ -193,6 +194,7 @@ const deployFn: DeployFunction = async (hre) => {
...
@@ -193,6 +194,7 @@ const deployFn: DeployFunction = async (hre) => {
console
.
log
(
`MSD address:
${
SystemDictator
.
address
}
`
)
console
.
log
(
`MSD address:
${
SystemDictator
.
address
}
`
)
console
.
log
(
`JSON:`
)
console
.
log
(
`JSON:`
)
console
.
log
(
jsonifyTransaction
(
tx
))
console
.
log
(
jsonifyTransaction
(
tx
))
console
.
log
(
await
getTenderlySimulationLink
(
SystemDictator
.
provider
,
tx
))
}
}
await
awaitCondition
(
await
awaitCondition
(
...
@@ -303,6 +305,7 @@ const deployFn: DeployFunction = async (hre) => {
...
@@ -303,6 +305,7 @@ const deployFn: DeployFunction = async (hre) => {
console
.
log
(
`OptimismPortal address:
${
OptimismPortal
.
address
}
`
)
console
.
log
(
`OptimismPortal address:
${
OptimismPortal
.
address
}
`
)
console
.
log
(
`JSON:`
)
console
.
log
(
`JSON:`
)
console
.
log
(
jsonifyTransaction
(
tx
))
console
.
log
(
jsonifyTransaction
(
tx
))
console
.
log
(
await
getTenderlySimulationLink
(
SystemDictator
.
provider
,
tx
))
}
}
await
awaitCondition
(
await
awaitCondition
(
...
@@ -331,6 +334,7 @@ const deployFn: DeployFunction = async (hre) => {
...
@@ -331,6 +334,7 @@ const deployFn: DeployFunction = async (hre) => {
console
.
log
(
`MSD address:
${
SystemDictator
.
address
}
`
)
console
.
log
(
`MSD address:
${
SystemDictator
.
address
}
`
)
console
.
log
(
`JSON:`
)
console
.
log
(
`JSON:`
)
console
.
log
(
jsonifyTransaction
(
tx
))
console
.
log
(
jsonifyTransaction
(
tx
))
console
.
log
(
await
getTenderlySimulationLink
(
SystemDictator
.
provider
,
tx
))
}
}
await
awaitCondition
(
await
awaitCondition
(
...
...
packages/contracts-bedrock/src/deploy-utils.ts
View file @
59f3574c
import
assert
from
'
assert
'
import
assert
from
'
assert
'
import
{
URLSearchParams
}
from
'
url
'
import
{
ethers
,
Contract
}
from
'
ethers
'
import
{
ethers
,
Contract
}
from
'
ethers
'
import
{
Provider
}
from
'
@ethersproject/abstract-provider
'
import
{
Provider
}
from
'
@ethersproject/abstract-provider
'
...
@@ -293,6 +294,7 @@ export const getDeploymentAddress = async (
...
@@ -293,6 +294,7 @@ export const getDeploymentAddress = async (
export
const
jsonifyTransaction
=
(
tx
:
ethers
.
PopulatedTransaction
):
string
=>
{
export
const
jsonifyTransaction
=
(
tx
:
ethers
.
PopulatedTransaction
):
string
=>
{
return
JSON
.
stringify
(
return
JSON
.
stringify
(
{
{
from
:
tx
.
from
,
to
:
tx
.
to
,
to
:
tx
.
to
,
data
:
tx
.
data
,
data
:
tx
.
data
,
value
:
tx
.
value
,
value
:
tx
.
value
,
...
@@ -354,6 +356,9 @@ export const doStep = async (opts: {
...
@@ -354,6 +356,9 @@ export const doStep = async (opts: {
console
.
log
(
`MSD address:
${
opts
.
SystemDictator
.
address
}
`
)
console
.
log
(
`MSD address:
${
opts
.
SystemDictator
.
address
}
`
)
console
.
log
(
`JSON:`
)
console
.
log
(
`JSON:`
)
console
.
log
(
jsonifyTransaction
(
tx
))
console
.
log
(
jsonifyTransaction
(
tx
))
console
.
log
(
await
getTenderlySimulationLink
(
opts
.
SystemDictator
.
provider
,
tx
)
)
}
}
// Wait for the step to complete.
// Wait for the step to complete.
...
@@ -368,3 +373,26 @@ export const doStep = async (opts: {
...
@@ -368,3 +373,26 @@ export const doStep = async (opts: {
// Perform post-step checks.
// Perform post-step checks.
await
opts
.
checks
()
await
opts
.
checks
()
}
}
/**
* Returns a direct link to a Tenderly simulation.
*
* @param provider Ethers Provider.
* @param tx Ethers transaction object.
* @returns the url of the tenderly simulation.
*/
export
const
getTenderlySimulationLink
=
async
(
provider
:
ethers
.
providers
.
Provider
,
tx
:
ethers
.
PopulatedTransaction
):
Promise
<
string
>
=>
{
if
(
process
.
env
.
TENDERLY_PROJECT
&&
process
.
env
.
TENDERLY_USERNAME
)
{
return
`https://dashboard.tenderly.co/
${
process
.
env
.
TENDERLY_PROJECT
}
/
${
process
.
env
.
TENDERLY_USERNAME
}
/simulator/new?
${
new
URLSearchParams
({
network
:
(
await
provider
.
getNetwork
()).
chainId
.
toString
(),
contractAddress
:
tx
.
to
,
rawFunctionInput
:
tx
.
data
,
from
:
tx
.
from
,
}).
toString
()}
`
}
}
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