-
Notifications
You must be signed in to change notification settings - Fork 6
feat: deploy Rollup contracts #76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
c0eeace
e843372
b33bd2a
75db01b
444ac7d
aced846
f5e85bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
// SPDX-License-Identifier: MIT OR Apache-2.0 | ||
pragma solidity 0.8.26; | ||
|
||
// deps | ||
import {SafeL2} from "safe-smart-account/contracts/SafeL2.sol"; | ||
import {SafeProxyFactory} from "safe-smart-account/contracts/proxies/SafeProxyFactory.sol"; | ||
import {CompatibilityFallbackHandler} from "safe-smart-account/contracts/handler/CompatibilityFallbackHandler.sol"; | ||
// script deps | ||
import {Script, console2} from "forge-std/Script.sol"; | ||
|
||
struct SafeSetup { | ||
address[] owners; | ||
uint256 threshold; | ||
address to; | ||
bytes data; | ||
address fallbackHandler; | ||
address paymentToken; | ||
uint256 payment; | ||
address payable paymentReceiver; | ||
uint256 saltNonce; | ||
} | ||
|
||
address constant OWNER_ONE = 0x1111111111111111111111111111111111111111; | ||
address constant OWNER_TWO = 0x2222222222222222222222222222222222222222; | ||
bytes32 constant SENTINEL_VALUE = 0x0000000000000000000000000000000000000000000000000000000000000001; | ||
|
||
contract GnosisScript is Script { | ||
// deploy: | ||
// forge script GnosisScript --sig "deployGnosis()" --rpc-url $RPC_URL --broadcast [signing args] | ||
function deployGnosis() | ||
public | ||
returns (address gnosisFactory, address gnosisSingleton, address gnosisFallbackHandler, address usdcAdmin) | ||
{ | ||
vm.startBroadcast(); | ||
|
||
// deploy gnosis safe singleton & proxy factory | ||
(gnosisFactory, gnosisSingleton, gnosisFallbackHandler) = deployGnosisCore(); | ||
|
||
// deploy a gnosis safe proxy as the USDC admin | ||
usdcAdmin = deploySafeInstance(gnosisFactory, gnosisSingleton, getUsdcAdminSetup(gnosisFallbackHandler)); | ||
} | ||
|
||
function deployGnosisCore() public returns (address factory, address singleton, address fallbackHandler) { | ||
factory = address(new SafeProxyFactory{salt: "zenith.gnosisFactory"}()); | ||
singleton = address(new SafeL2{salt: "zenith.gnosisSingleton"}()); | ||
fallbackHandler = address(new CompatibilityFallbackHandler{salt: "zenith.gnosisFallbackHandlder"}()); | ||
} | ||
|
||
function deploySafeInstance(address factory, address singleton, SafeSetup memory setup) | ||
public | ||
returns (address safe) | ||
{ | ||
bytes memory init = abi.encodeWithSignature( | ||
"setup(address[],uint256,address,bytes,address,address,uint256,address)", | ||
setup.owners, | ||
setup.threshold, | ||
setup.to, | ||
setup.data, | ||
setup.fallbackHandler, | ||
setup.paymentToken, | ||
setup.payment, | ||
setup.paymentReceiver | ||
); | ||
safe = address(SafeProxyFactory(factory).createProxyWithNonce(singleton, init, setup.saltNonce)); | ||
} | ||
|
||
// setup the gnosis safe with 2 owners, threshold of 1. | ||
// make the owners recognizable addrs to aid in inspecting storage layout | ||
function getUsdcAdminSetup(address fallbackHandler) public pure returns (SafeSetup memory usdcAdminSetup) { | ||
address[] memory owners = new address[](2); | ||
owners[0] = OWNER_ONE; | ||
owners[1] = OWNER_TWO; | ||
usdcAdminSetup = SafeSetup({ | ||
owners: owners, | ||
threshold: 1, | ||
to: address(0), | ||
data: "", | ||
fallbackHandler: fallbackHandler, | ||
paymentToken: address(0), | ||
payment: 0, | ||
paymentReceiver: payable(address(0)), | ||
saltNonce: 17001 | ||
}); | ||
} | ||
|
||
// example run: | ||
// forge script GnosisScript --sig "printOwnerSlots" "[0x1111111111111111111111111111111111111111, 0x2222222222222222222222222222222222222222]" | ||
function printOwnerSlots(address[] memory owners) public pure { | ||
for (uint256 i = 0; i <= owners.length; i++) { | ||
bytes32 value = (i == 0) ? SENTINEL_VALUE : addressToBytes32(owners[i - 1]); | ||
bytes32 slot = keccak256(abi.encodePacked(value, uint256(2))); | ||
console2.logBytes32(slot); | ||
} | ||
} | ||
|
||
function addressToBytes32(address addr) internal pure returns (bytes32) { | ||
return bytes32(uint256(uint160(addr))); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// SPDX-License-Identifier: MIT OR Apache-2.0 | ||
pragma solidity 0.8.26; | ||
|
||
// system contracts | ||
import {RollupOrders} from "../src/orders/RollupOrders.sol"; | ||
import {RollupPassage} from "../src/passage/RollupPassage.sol"; | ||
// simple erc20 | ||
import {SimpleERC20} from "simple-erc20/SimpleERC20.sol"; | ||
// utils | ||
import {Script, console2} from "forge-std/Script.sol"; | ||
|
||
// create2 address for Permit2 | ||
address constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3; | ||
// address of Minter with permission to mint ERC20 tokens | ||
address constant MINTER = 0x9999999999999999999999999999999999999999; | ||
|
||
contract L2Script is Script { | ||
// deploy: | ||
// forge script L2Script --sig "deploySystem()" --rpc-url $RPC_URL --broadcast [signing args] | ||
function deploySystem() public returns (address rollupPassage, address rollupOrders, address wbtc, address usdt) { | ||
vm.startBroadcast(); | ||
|
||
// deploy system contracts | ||
rollupPassage = address(new RollupPassage{salt: "zenith.rollupPassage"}(PERMIT2)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we want to do salt-mining to find low addresses? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm. well, we don't technically have to. we could handpick better addrs than we could reasonably mine by simply directly editing the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, we'd also have to do some find-and-replace shenangians to update immutables and storage vars. which would be annoying. but it would be awful nice to have them have short addresses 🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. depends on how dearly we wish for the script to produce the exact final allocs if we want efficient addrs for all contracts (&& fully deterministic script -> allocs) we might want to consider forking the USDC deploy script and making them CREATE2 deploys, because rn they're just CREATE and can't be easily mined There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
well, actually, out of the following contracts
we could handpick the addrs without find-and-replacing anything in code/storage:
BUT, for all three of these cases, the addrs that are stored in storage/immutables are just params configured in the deploys of the subsequent contracts. we could just handpick the addrs and reconfigure the deploys to point to those addrs and the immutables/storage slots would be produced correctly by the deploy scripts. The only ones that would be more complex to fiddle with (and therefore something I'm reticent to do) is:
which has a lot of internal dependencies on itself that is stored in immutables/storage, so we'd have to get into the guts to mess with the addresses |
||
rollupOrders = address(new RollupOrders{salt: "zenith.rollupOrders"}(PERMIT2)); | ||
|
||
// deploy simple erc20 tokens | ||
wbtc = address(new SimpleERC20{salt: "zenith.wbtc"}(MINTER, "Wrapped BTC", "WBTC")); | ||
usdt = address(new SimpleERC20{salt: "zenith.usdt"}(MINTER, "Tether USD", "USDT")); | ||
} | ||
} | ||
|
||
// NOTE: must deploy Permit2 via https://github.com/Uniswap/permit2/blob/main/script/DeployPermit2.s.sol | ||
// in order to properly setup _CACHED_CHAIN_ID and _CACHED_DOMAIN_SEPARATOR | ||
|
||
// NOTE: must deploy USDC via https://github.com/circlefin/stablecoin-evm/blob/master/scripts/deploy/deploy-fiat-token.s.sol |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you document this further? not 100% sure what's going on. is this a canary that is used later to do a find/replace on the minter address?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
correct. the intention was actually to replace the real minter address here once it's chosen (rather than find/replace in the alloc json)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
similar for
OWNER_ONE
andOWNER_TWO
, which really should be updated in code because the storage slots that Gnosis stores owners at depend on the list of owners themselvesThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
kind-of a nit, but this canary value is ambiguous if preceded by a 9 nibble. can we use something like
0x9988...
(only ambiguous if preceded by0x9988
)Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how bout this (improved docs too) f5e85bd