Skip to content

Commit

Permalink
feat: QoL changes Create3 + foundry.toml (ExocoreNetwork#138)
Browse files Browse the repository at this point in the history
* feat: update lib/forge-std

The `StdStorage` contract was changed as part of this upgrade and hence
the slot calculation in the test needs to be upgraded

* chore: forge fmt

* change init data for mark bootstrap

* fix: typo in env var name

* config: create test profile + rpc endpoints

- Whenever running `forge script` and other such commands, having an
  assigned chain_id in the default profile causes problems. Meanwhile,
  our tests require the chain_id to be set to be that of a known (live)
  network. To that end, create a dedicated profile to handle this split.
- Add RPC endpoints for chains and their explorers (based on env vars)
  to allow passing `--rpc-url <name>` and `--etherscan-api-key <name>`
  instead of the URL endpoints. This does not, however, impact any of
  the scripts that use environment variables to decide the chain.

* add `make test` command + remove debug logs

* chore: forge fmt

* update CREATE3 factory with comments

* remove superfluous file

* reorder config

* comment out the complaining section in CI

* fix test summary step

* fix(script): validate deployments once done

+ add comment but EIP155

* fix(ci): pass `env` via step definition

* fix: respond to AI comments

* fix(ci): correct forge-ci step

* fix(beacon-proxy): add source for bytecode
  • Loading branch information
MaxMustermann2 authored Jan 9, 2025
1 parent 47a9a15 commit 7c4c128
Show file tree
Hide file tree
Showing 22 changed files with 174 additions and 40 deletions.
9 changes: 5 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# anvil --port 8646 or via docker compose up in eth-pos-devnet
CLIENT_CHAIN_RPC=http://localhost:8646
EXOCORE_TESETNET_RPC=http://localhost:8545
EXOCORE_TESTNET_RPC=http://localhost:8545
EXOCORE_LOCAL_RPC=http://localhost:8545
# The following are default Anvil keys - not real keys!
TEST_ACCOUNT_ONE_PRIVATE_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
Expand All @@ -10,13 +10,14 @@ TEST_ACCOUNT_FOUR_PRIVATE_KEY=0x7c852118294e51e653712a81e05800f419141751be58f605
# Use 'exocored keys unsafe-export-eth-key "dev0" --home ~/.tmp-exocored' to get the privatekey
EXOCORE_GENESIS_PRIVATE_KEY=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a

USE_ENDPOINT_MOCK=true
USE_EXOCORE_PRECOMPILE_MOCK=true
USE_ENDPOINT_MOCK=false
USE_EXOCORE_PRECOMPILE_MOCK=false

# For contract verification
ETHERSCAN_API_KEY=
EXOCORE_TESTNET_EXPLORER=

# These are used for integration testing the Bootstrap contract, in addition to
# These are used for integration testing the Bootstrap contract, in addition to
# CLIENT_CHAIN_RPC and BEACON_CHAIN_ENDPOINT above.
INTEGRATION_VALIDATOR_KEYS=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80,0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d,0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a
INTEGRATION_STAKERS=0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6,0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a,0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba,0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e,0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356,0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97,0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6
Expand Down
13 changes: 10 additions & 3 deletions .github/workflows/forge-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,17 @@ jobs:
./cache
./broadcast
key: build-${{ github.event.pull_request.head.sha || github.event.after || github.sha }}
- name: Test
run: forge test -vvv
- name: Clear out the `etherscan` section in `foundry.toml` for missing env vars
run: sed -i '/\[etherscan\]/,/^\[/ s/^/#/' foundry.toml
- name: Run tests
env:
FOUNDRY_PROFILE: test
run: forge test
- name: Set test snapshot as summary
run: NO_COLOR=1 forge snapshot >> $GITHUB_STEP_SUMMARY
env:
FOUNDRY_PROFILE: test
NO_COLOR: 1
run: forge snapshot >> "$GITHUB_STEP_SUMMARY"

format:
# Takes less than 30s
Expand Down
16 changes: 16 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Makefile for Foundry commands

# Variables
FOUNDRY_PROFILE=test

# Targets
.PHONY: test build fmt

test:
FOUNDRY_PROFILE=$(FOUNDRY_PROFILE) forge test -vvv

build:
forge build

fmt:
forge fmt
33 changes: 28 additions & 5 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,40 @@ ignored_warnings_from = ["script", "test"]
# fail compilation if the warnings are not fixed.
# this is super useful for the code size warning.
deny_warnings = true
# for tests, use the mainnet chain_id for NetworkConstants to work.
chain_id = 1

[rpc_endpoints]
ethereum_local_rpc = "${ETHEREUM_LOCAL_RPC}"
exocore_local_rpc = "${EXOCORE_LOCAL_RPC}"
[profile.test]
# for tests, use the Eth mainnet chain_id for NetworkConstants to work.
# do not specify this in the default profile to pacify forge script
# running on non-Eth mainnet chains.
chain_id = 1

[fmt]
number_underscore = "thousands"
sort_imports = true
wrap_comments = true
single_line_statement_blocks = "multi"
contract_new_lines = true

[rpc_endpoints]
# We do not distinguish between Holesky / Sepolia / mainnet here, since that is
# handled within the scripts as an environment variable. This is more of a convenience
# mechanism to specify the chain name via command line; for example:
# cast balance $ADDRESS --rpc-url client
client = "${CLIENT_CHAIN_RPC}"
exocore_local = "${EXOCORE_LOCAL_RPC}"
exocore_testnet = "${EXOCORE_TESTNET_RPC}"

[etherscan]
# Similar shortcut as `rpc_endpoints` to verify contracts by name and not URL.
# Example:
# forge verify-contract --etherscan-api-key exocore_testnet <...usual args...>
# However, defining this section with these env vars makes `forge test` complain
# if they are missing. It is because it tries to fetch debugging context from
# the block explorer. To avoid this, either ensure these vars are set, or comment
# out this section.
mainnet = { key = "${ETHERSCAN_API_KEY}" }
sepolia = { key = "${ETHERSCAN_API_KEY}" }
holesky = { key = "${ETHERSCAN_API_KEY}" }
exocore_testnet = { key = "${ETHERSCAN_API_KEY}", chain = 233, url = "${EXOCORE_TESTNET_EXPLORER_API}" }

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
3 changes: 1 addition & 2 deletions script/12_RedeployClientChainGateway.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,8 @@ contract RedeployClientChainGateway is BaseScript {
new ClientChainGateway(address(clientChainLzEndpoint), config, address(rewardVaultBeacon));

// then the client chain initialization
address[] memory emptyList;
bytes memory initialization =
abi.encodeWithSelector(clientGatewayLogic.initialize.selector, exocoreValidatorSet.addr, emptyList);
abi.encodeWithSelector(clientGatewayLogic.initialize.selector, exocoreValidatorSet.addr);
bootstrap.setClientChainGatewayLogic(address(clientGatewayLogic), initialization);
vm.stopBroadcast();

Expand Down
81 changes: 81 additions & 0 deletions script/20_DeployCreate3.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "forge-std/Script.sol";

/// @title DeployCreate3
/// @author ExocoreNetwork
/// @notice This script is used to deploy the deterministic Create2 and Create3 factories to any network.
/// The Create2 factory is deployed using a raw transaction and the Create3 factory is deployed using a Create2 call.
/// @dev The advantage of using the Create3 factory over the Create2 factory for further deployments is that the
/// contract address for Create3-deployments is only dependent on the salt and the sender and not the code. This allows
/// for omni-chain deployments at the same address regardless of the contract code version that is deployed.
contract DeployCreate3 is Script {

// Unfortunately this is a pre-EIP155 transaction, so it will not work on most chains (which have blocked them).
// In that case, such a chain should add the contract as a predeploy.
bytes public constant CREATE2_RAW_TRANSACTION =
// solhint-disable-next-line
hex"f8a58085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf31ba02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222";
address public constant CREATE2_DEPLOYER = address(0x3fAB184622Dc19b6109349B94811493BF2a45362);
// this param is contained in the signed transaction already
uint256 public constant CREATE2_BALANCE = 100 gwei * 100_000;
address public constant CREATE2_DESTINATION = address(0x4e59b44847b379578588920cA78FbF26c0B4956C);
bytes public constant CREATE2_RUNTIME_CODE =
// solhint-disable-next-line
hex"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3";
// https://github.com/ZeframLou/create3-factory
bytes public constant CREATE3_INIT_CODE =
// solhint-disable-next-line
hex"608060405234801561001057600080fd5b5061063b806100206000396000f3fe6080604052600436106100295760003560e01c806350f1c4641461002e578063cdcb760a14610077575b600080fd5b34801561003a57600080fd5b5061004e610049366004610489565b61008a565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b61004e6100853660046104fd565b6100ee565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084901b166020820152603481018290526000906054016040516020818303038152906040528051906020012091506100e78261014c565b9392505050565b6040517fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003360601b166020820152603481018390526000906054016040516020818303038152906040528051906020012092506100e78383346102b2565b604080518082018252601081527f67363d3d37363d34f03d5260086018f30000000000000000000000000000000060209182015290517fff00000000000000000000000000000000000000000000000000000000000000918101919091527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b166021820152603581018290527f21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f60558201526000908190610228906075015b6040516020818303038152906040528051906020012090565b6040517fd69400000000000000000000000000000000000000000000000000000000000060208201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606083901b1660228201527f010000000000000000000000000000000000000000000000000000000000000060368201529091506100e79060370161020f565b6000806040518060400160405280601081526020017f67363d3d37363d34f03d5260086018f30000000000000000000000000000000081525090506000858251602084016000f5905073ffffffffffffffffffffffffffffffffffffffff811661037d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f4445504c4f594d454e545f4641494c454400000000000000000000000000000060448201526064015b60405180910390fd5b6103868661014c565b925060008173ffffffffffffffffffffffffffffffffffffffff1685876040516103b091906105d6565b60006040518083038185875af1925050503d80600081146103ed576040519150601f19603f3d011682016040523d82523d6000602084013e6103f2565b606091505b50509050808015610419575073ffffffffffffffffffffffffffffffffffffffff84163b15155b61047f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f494e495449414c495a4154494f4e5f4641494c454400000000000000000000006044820152606401610374565b5050509392505050565b6000806040838503121561049c57600080fd5b823573ffffffffffffffffffffffffffffffffffffffff811681146104c057600080fd5b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806040838503121561051057600080fd5b82359150602083013567ffffffffffffffff8082111561052f57600080fd5b818501915085601f83011261054357600080fd5b813581811115610555576105556104ce565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561059b5761059b6104ce565b816040528281528860208487010111156105b457600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b6000825160005b818110156105f757602081860181015185830152016105dd565b50600092019182525091905056fea2646970667358221220fd377c185926b3110b7e8a544f897646caf36a0e82b2629de851045e2a5f937764736f6c63430008100033";
bytes32 public constant CREATE3_SALT = bytes32(0);
address public constant CREATE3_DESTINATION = address(0x6aA3D87e99286946161dCA02B97C5806fC5eD46F);
bytes public constant CREATE3_RUNTIME_CODE =
// solhint-disable-next-line
hex"6080604052600436106100295760003560e01c806350f1c4641461002e578063cdcb760a14610077575b600080fd5b34801561003a57600080fd5b5061004e610049366004610489565b61008a565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b61004e6100853660046104fd565b6100ee565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084901b166020820152603481018290526000906054016040516020818303038152906040528051906020012091506100e78261014c565b9392505050565b6040517fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003360601b166020820152603481018390526000906054016040516020818303038152906040528051906020012092506100e78383346102b2565b604080518082018252601081527f67363d3d37363d34f03d5260086018f30000000000000000000000000000000060209182015290517fff00000000000000000000000000000000000000000000000000000000000000918101919091527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b166021820152603581018290527f21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f60558201526000908190610228906075015b6040516020818303038152906040528051906020012090565b6040517fd69400000000000000000000000000000000000000000000000000000000000060208201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606083901b1660228201527f010000000000000000000000000000000000000000000000000000000000000060368201529091506100e79060370161020f565b6000806040518060400160405280601081526020017f67363d3d37363d34f03d5260086018f30000000000000000000000000000000081525090506000858251602084016000f5905073ffffffffffffffffffffffffffffffffffffffff811661037d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f4445504c4f594d454e545f4641494c454400000000000000000000000000000060448201526064015b60405180910390fd5b6103868661014c565b925060008173ffffffffffffffffffffffffffffffffffffffff1685876040516103b091906105d6565b60006040518083038185875af1925050503d80600081146103ed576040519150601f19603f3d011682016040523d82523d6000602084013e6103f2565b606091505b50509050808015610419575073ffffffffffffffffffffffffffffffffffffffff84163b15155b61047f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f494e495449414c495a4154494f4e5f4641494c454400000000000000000000006044820152606401610374565b5050509392505050565b6000806040838503121561049c57600080fd5b823573ffffffffffffffffffffffffffffffffffffffff811681146104c057600080fd5b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806040838503121561051057600080fd5b82359150602083013567ffffffffffffffff8082111561052f57600080fd5b818501915085601f83011261054357600080fd5b813581811115610555576105556104ce565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561059b5761059b6104ce565b816040528281528860208487010111156105b457600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b6000825160005b818110156105f757602081860181015185830152016105dd565b50600092019182525091905056fea2646970667358221220fd377c185926b3110b7e8a544f897646caf36a0e82b2629de851045e2a5f937764736f6c63430008100033";

function setUp() public virtual {
// do nothing
}

function run() public {
vm.startBroadcast();
// only deploy if the destination is not already deployed
// with Anvil, pass `--disable-default-create2-deployer` to test this case
if (CREATE2_DESTINATION.code.length == 0) {
deployCreate2Factory();
console.log("Deployed create2 factory");
} else {
vm.assertEq(CREATE2_DESTINATION.code, CREATE2_RUNTIME_CODE);
console.log("Create2 factory already deployed");
}
// only deploy if the destination i1s not already deployed
if (CREATE3_DESTINATION.code.length == 0) {
deployCreate3Factory();
console.log("Deployed create3 factory");
} else {
vm.assertEq(CREATE3_DESTINATION.code, CREATE3_RUNTIME_CODE);
console.log("Create3 factory already deployed");
}
vm.stopBroadcast();
}

function deployCreate2Factory() public {
// provide gas funds to the deployer
uint256 currentBalance = CREATE2_DEPLOYER.balance;
if (currentBalance < CREATE2_BALANCE) {
payable(CREATE2_DEPLOYER).transfer(CREATE2_BALANCE - currentBalance);
}
// forge-std lib added this function in newer versions
vm.broadcastRawTransaction(CREATE2_RAW_TRANSACTION);
}

function deployCreate3Factory() public {
// any sender can deploy the create3 factory because it is deployed via create2
// the address will be the same across all chains as long as the code and
// the salt are the same. that is why we use a constant code instead of compilation.
(bool success,) = CREATE2_DESTINATION.call(abi.encodePacked(CREATE3_SALT, CREATE3_INIT_CODE));
require(success, "DeployCreate3: failed to deploy create3 factory");
}

}
3 changes: 1 addition & 2 deletions script/7_DeployBootstrap.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,8 @@ contract DeployBootstrapOnly is BaseScript {
new ClientChainGateway(address(clientChainLzEndpoint), config, address(rewardVaultBeacon));

// then the client chain initialization
address[] memory emptyList;
bytes memory initialization =
abi.encodeWithSelector(clientGatewayLogic.initialize.selector, exocoreValidatorSet.addr, emptyList);
abi.encodeWithSelector(clientGatewayLogic.initialize.selector, exocoreValidatorSet.addr);

// bootstrap implementation
Bootstrap bootstrap = Bootstrap(
Expand Down
2 changes: 1 addition & 1 deletion script/BaseScript.sol
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ contract BaseScript is Script, StdCheats {
console.log("NOTICE: using exocore precompiles mock", useExocorePrecompileMock);

clientChainRPCURL = vm.envString("CLIENT_CHAIN_RPC");
exocoreRPCURL = vm.envString("EXOCORE_TESETNET_RPC");
exocoreRPCURL = vm.envString("EXOCORE_TESTNET_RPC");
}

function _bindPrecompileMocks() internal {
Expand Down
2 changes: 1 addition & 1 deletion script/TokenTransfer.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ contract DeployScript is Script {
relayer.addr = vm.addr(relayer.privateKey);

clientChainRPCURL = vm.envString("CLIENT_CHAIN_RPC");
exocoreRPCURL = vm.envString("EXOCORE_TESETNET_RPC");
exocoreRPCURL = vm.envString("EXOCORE_TESTNET_RPC");

string memory deployedContracts = vm.readFile("script/deployedContracts.json");

Expand Down
3 changes: 1 addition & 2 deletions script/integration/1_DeployBootstrap.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -241,9 +241,8 @@ contract DeployContracts is Script {
ClientChainGateway clientGatewayLogic =
new ClientChainGateway(address(clientChainLzEndpoint), config, address(rewardVaultBeacon));

address[] memory emptyList;
bytes memory initialization =
abi.encodeWithSelector(clientGatewayLogic.initialize.selector, vm.addr(contractDeployer), emptyList);
abi.encodeWithSelector(clientGatewayLogic.initialize.selector, vm.addr(contractDeployer));

bootstrap.setClientChainGatewayLogic(address(clientGatewayLogic), initialization);

Expand Down
Loading

0 comments on commit 7c4c128

Please sign in to comment.