Skip to content
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

feat: bash scripts to deploy, generate allocs genesis file, and check it #77

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions allocs/README.md
Original file line number Diff line number Diff line change
@@ -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.
30 changes: 30 additions & 0 deletions allocs/check-alloc.sh
Original file line number Diff line number Diff line change
@@ -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!"
30 changes: 30 additions & 0 deletions allocs/deploy-scripts.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
26 changes: 26 additions & 0 deletions allocs/deploy.sh
Original file line number Diff line number Diff line change
@@ -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
36 changes: 36 additions & 0 deletions allocs/generate-alloc.sh
Original file line number Diff line number Diff line change
@@ -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!"
43 changes: 43 additions & 0 deletions allocs/write-output.sh
Original file line number Diff line number Diff line change
@@ -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/"