diff --git a/allocs/README.md b/allocs/README.md new file mode 100644 index 0000000..8c63adb --- /dev/null +++ b/allocs/README.md @@ -0,0 +1,59 @@ +## Allocs + +These scripts are utilities for setting up the `genesis.json` file for a new chain. + +Developers write Forge script(s) that deploy and configure the contracts they wish to be pre-deployed on their chain. These scripts can then produce an `alloc` JSON file to input to `genesis.json`, which will setup those contracts with the correct code and storage slots. + +## Usage + +### Dependencies + +Install `heimdall` - instructions [here](https://github.com/Jon-Becker/heimdall-rs/tree/main?tab=readme-ov-file#installation--usage) + +Install `foundry` - instructions [here](https://book.getfoundry.sh/getting-started/installation) + +### Setup Scripts + +First, the developer must setup their `deploy-scripts.json` with the Forge scripts they want to run. + +Each script is represented in JSON with the following fields: +- `relativePath`: the relative path to the repo containing the Forge script to run. +- `deployFile`: the file name that contains the Forge script. +- `deployContract`: the name of the contract in which the script is defined. +- `deploySignature`: the function signature of the deploy script + +Using Uniswap's [Permit2](https://github.com/Uniswap/permit2/blob/main/script/DeployPermit2.s.sol) as an example, the JSON would look like this: +``` + { + "relativePath": "../permit2", + "deployFile": "DeployPermit2.s.sol", + "deployContract": "DeployPermit2", + "deploySignature": "run" + } +``` + +### Deploy + +This bash script will run each of the deploy scripts within their respective repos, then output the addresses of every contract deployed: + +```shell +$ ./allocs/deploy.sh $RPC_URL $PRIVATE_KEY +``` + +### Generate Alloc + +This script will read from the outputs generated by the deploy script, then use `cast code` and `heimdall dump` to generate a complete `alloc` JSON: + +```shell +$ ./allocs/generate-alloc.sh $ARCHIVE_RPC_URL +``` + +NOTE: You MUST provide an archive RPC endpoint that supports `trace_replayBlockTransactions`, as `heimdall dump` relies on this endpoint. Unfortuntately, at the time of writing, `anvil` does not support this endpoint. + +### Check Alloc + +If you have an `alloc` JSON and you wish to check the storage slots against a deployed chain, you can run the following script: +```shell +$ ./allocs/check-alloc.sh $RPC_URL +``` +Note that a non-archive RPC endpoint is fine for this command. \ No newline at end of file diff --git a/allocs/check-alloc.sh b/allocs/check-alloc.sh new file mode 100755 index 0000000..71289c2 --- /dev/null +++ b/allocs/check-alloc.sh @@ -0,0 +1,30 @@ +# ./allocs/check-alloc.sh $RPC_URL + +rpc=$1 +chainid=$(cast chain-id --rpc-url $rpc) + +# Pull the current alloc JSON +contents=$(cat allocs/$chainid/alloc.json) + +# Get all addresses JSON +addrs=$(echo $contents | jq -r '.alloc | keys[]') + +# For each contract, +for addr in $addrs; do + # Get the code using `cast` + code=$(cast code $addr --rpc-url $rpc) + # replace the value in the JSON with the queried code + contents=$(echo $contents | jq --arg addy "$addr" --arg newCode "$code" '.alloc[$addy].code = $newCode') + + # Get the storage slots in the JSON + slots=$(echo $contents | jq -r --arg addy "$addr" '.alloc[$addy].storage | select(. != null) | keys[]') + for slot in $slots; do + # Get the storage value using `cast` + value=$(cast storage $addr $slot --rpc-url $rpc) + # replace the value in the JSON with the queried storage value + contents=$(echo $contents | jq --arg addy "$addr" --arg slot "$slot" --arg newValue "$value" '.alloc[$addy].storage[$slot] = $newValue') + done +done + +echo "$contents" > ./allocs/$chainid/alloc.json +echo "Done - allocs re-written to alloc.json!" \ No newline at end of file diff --git a/allocs/deploy-scripts.json b/allocs/deploy-scripts.json new file mode 100644 index 0000000..769cd61 --- /dev/null +++ b/allocs/deploy-scripts.json @@ -0,0 +1,30 @@ +[ + { + "relativePath": "../permit2", + "deployFilePath": "script", + "deployFile": "DeployPermit2.s.sol", + "deployContract": "DeployPermit2", + "deploySignature": "run" + }, + { + "relativePath": "../zenith", + "deployFilePath": "script", + "deployFile": "ZenithL2.s.sol", + "deployContract": "L2Script", + "deploySignature": "deploySystem" + }, + { + "relativePath": "../zenith", + "deployFilePath": "script", + "deployFile": "DeployGnosisSafe.s.sol", + "deployContract": "GnosisScript", + "deploySignature": "deployGnosis" + }, + { + "relativePath": "../stablecoin-evm", + "deployFilePath": "scripts/deploy", + "deployFile": "deploy-fiat-token.s.sol", + "deployContract": "DeployFiatToken", + "deploySignature": "run" + } +] \ No newline at end of file diff --git a/allocs/deploy.sh b/allocs/deploy.sh new file mode 100755 index 0000000..7ce7c30 --- /dev/null +++ b/allocs/deploy.sh @@ -0,0 +1,26 @@ +# ./allocs/deploy.sh $RPC_URL $PRIVATE_KEY +# NOTE: before running, setup the forge scripts that will run at `allocs/deploy-scripts.json` + +rpc=$1 +privateKey=$2 + +# parse array of script commands `allocs/deploy-scripts.json`` +scripts=$(cat allocs/deploy-scripts.json | jq -c '.[]') + +echo "$scripts" | while IFS= read -r script; do + # pull the script vars + relativePath=$(echo "$script" | jq -r '.relativePath') + deployFilePath=$(echo "$script" | jq -r '.deployFilePath') + deployFile=$(echo "$script" | jq -r '.deployFile') + deployContract=$(echo "$script" | jq -r '.deployContract') + deploySignature=$(echo "$script" | jq -r '.deploySignature') + + # cd to a new repo if necessary + cd $relativePath + + # run the deploy script + forge script $deployFilePath/${deployFile}:${deployContract} --sig $deploySignature --rpc-url $rpc --private-key $privateKey --broadcast +done + +# write the addresses +./allocs/write-output.sh $rpc \ No newline at end of file diff --git a/allocs/generate-alloc.sh b/allocs/generate-alloc.sh new file mode 100755 index 0000000..4568707 --- /dev/null +++ b/allocs/generate-alloc.sh @@ -0,0 +1,36 @@ +# ./allocs/generate.sh $ARCHIVE_RPC_URL + +rpc=$1 +chainid=$(cast chain-id --rpc-url $rpc) + +# set empty allocs +alloc="{}" + +# parse the JSON at `addresses.json` and get an array of addresses +addresses=$(cat allocs/$chainid/addresses.json | jq -r '.[] | .contractAddress') +fromBlock=$(printf "%d\n" $(cat allocs/$chainid/blocks.json | jq -r 'sort | .[0]')) +toBlock=$(printf "%d\n" $(cat allocs/$chainid/blocks.json | jq -r 'sort | .[-1]')) + +# loop through addresses, +for addr in $addresses; do + # CODE + # Get the code using `cast` + code=$(cast code $addr --rpc-url $rpc --block $toBlock) + # replace the value in the JSON with the queried code + alloc=$(echo $alloc | jq --arg addy "$addr" --arg newCode "$code" '.alloc[$addy].code = $newCode') + + # STORAGE + # Get the storage slots using heimdall + echo "Dumping storage for $addr..." + heimdall dump $addr --from-block $fromBlock --to-block $toBlock --rpc-url $rpc --output ./allocs/$chainid/$addr + # parse the .csv output to json + storage=$(awk -F, 'NR>1 {printf "\"%s\": \"%s\", ", $1, $2}' ./allocs/$chainid/$addr/dump.csv | sed 's/, $//') + # replace the value in the JSON with the queried storage values + alloc=$(echo $alloc | jq --arg addy "$addr" --argjson storage "{$storage}" '.alloc[$addy].storage = $storage') + # remove the .csv files + rm -rf ./allocs/$chainid/$addr/ +done + +touch ./allocs/$chainid/alloc.json +echo "$alloc" | jq '.' > ./allocs/$chainid/alloc.json +echo "Done - generated allocs written to ./allocs/$chainid/alloc.json!" \ No newline at end of file diff --git a/allocs/write-output.sh b/allocs/write-output.sh new file mode 100755 index 0000000..2f9d11e --- /dev/null +++ b/allocs/write-output.sh @@ -0,0 +1,43 @@ +# ./allocs/write-addresses.sh $RPC_URL +# NOTE: before running, setup the forge scripts to inspect outputs from at `allocs/deploy-scripts.json` + +rpc=$1 +chainid=$(cast chain-id --rpc-url $rpc) + +contracts="[]" +blocks="[]" + +workingdir=$(pwd) +mkdir -p ./allocs/$chainid + +# parse array of script commands `allocs/deploy-scripts.json`` +scripts=$(cat allocs/deploy-scripts.json | jq -c '.[]') + +echo "$scripts" | while IFS= read -r script; do + # pull the script vars + relativePath=$(echo "$script" | jq -r '.relativePath') + deployFile=$(echo "$script" | jq -r '.deployFile') + deployContract=$(echo "$script" | jq -r '.deployContract') + deploySignature=$(echo "$script" | jq -r '.deploySignature') + + # cd to the script repo + cd $relativePath + + # get contracts deployed via CREATE or CREATE2 + newContracts=$(cat broadcast/${deployFile}/${chainid}/${deploySignature}-latest.json | jq '[.transactions[] | select(.transactionType == ("CREATE", "CREATE2")) | {contractName, contractAddress}]') + # get contracts deployed in sub-calls + additionalContracts=$(cat broadcast/${deployFile}/${chainid}/${deploySignature}-latest.json | jq '[.transactions[].additionalContracts[] | {contractAddress: .address}]') + # append all new contracts to the running total + contracts=$(echo "$contracts" "$newContracts" "$additionalContracts" | jq -s '.[0] + .[1] + .[2]') + # write addresses to file + echo "$contracts" > $workingdir/allocs/$chainid/addresses.json + + # get blocks + newBlocks=$(cat broadcast/${deployFile}/${chainid}/${deploySignature}-latest.json | jq '[.receipts[].blockNumber]') + # append new blocks to the running total + blocks=$(echo "$blocks" "$newBlocks" | jq -s '.[0] + .[1] | sort | unique') + # write blocks to file + echo "$blocks" > $workingdir/allocs/$chainid/blocks.json +done + +echo "Done! Outputs written to ./allocs/$chainid/" \ No newline at end of file