Skip to content

Commit

Permalink
feat: add a deployment bash script for multiple chains
Browse files Browse the repository at this point in the history
feat: add deploy protocol 2 scripts
build: remove etherscan api keys from foundry.toml
build: improve .env.example
chore: git ignore deployments
  • Loading branch information
andreivladbrg committed Dec 7, 2023
1 parent 93edc66 commit 6a3ca31
Show file tree
Hide file tree
Showing 6 changed files with 324 additions and 22 deletions.
47 changes: 37 additions & 10 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,12 +1,39 @@
export API_KEY_ARBISCAN="YOUR_API_KEY_ARBISCAN"
export API_KEY_BSCSCAN="YOUR_API_KEY_BSCSCAN"
export API_KEY_ETHERSCAN="YOUR_API_KEY_ETHERSCAN"
export API_KEY_GNOSISSCAN="YOUR_API_KEY_GNOSISSCAN"
export API_KEY_INFURA="YOUR_API_KEY_INFURA"
export API_KEY_OPTIMISTIC_ETHERSCAN="YOUR_API_KEY_OPTIMISTIC_ETHERSCAN"
export API_KEY_POLYGONSCAN="YOUR_API_KEY_POLYGONSCAN"
export API_KEY_SNOWTRACE="YOUR_API_KEY_SNOWTRACE"
# Run `cp .env.example .env` command to create your .env file

# General
export API_KEY_INFURA="YOUR_INFURA_KEY"
export FOUNDRY_PROFILE="lite"
export MNEMONIC="YOUR_MNEMONIC"
export RPC_URL_GOERLI="YOUR_RPC_URL_GOERLI"
export RPC_URL_MAINNET="YOUR_RPC_URL_MAINNET"

# RPC URLs
export ARBITRUM_RPC_URL="YOUR_RPC_URL"
export AVALANCHE_RPC_URL="YOUR_RPC_URL"
export BASE_RPC_URL="YOUR_RPC_URL"
export BINANCE_RPC_URL="YOUR_RPC_URL"
export GNOSIS_RPC_URL="YOUR_RPC_URL"
export MAINNET_RPC_URL="YOUR_RPC_URL"
export OPTIMISM_RPC_URL="YOUR_RPC_URL"
export POLYGON_RPC_URL="YOUR_RPC_URL"
export SCROLL_RPC_URL="YOUR_RPC_URL"

# Etherscan API keys
export ARBISCAN_API_KEY="YOUR_API_KEY"
export BASESCAN_API_KEY="YOUR_API_KEY"
export BSCSCAN_API_KEY="YOUR_API_KEY"
export ETHERSCAN_API_KEY="YOUR_API_KEY"
export GNOSISSCAN_API_KEY="YOUR_API_KEY"
export OPTIMISTIC_API_KEY="YOUR_API_KEY"
export POLYGONSCAN_API_KEY="YOUR_API_KEY"
export SCROLL_API_KEY="YOUR_API_KEY"
export SNOWTRACE_API_KEY="YOUR_API_KEY"

# Script variables
export ARBITRUM_ADMIN="YOUR_ADMIN_ADDRESS"
export AVALANCHE_ADMIN="YOUR_ADMIN_ADDRESS"
export BASE_ADMIN="YOUR_ADMIN_ADDRESS"
export BSC_ADMIN="YOUR_ADMIN_ADDRESS"
export GNOSIS_ADMIN="YOUR_ADMIN_ADDRESS"
export MAINNET_ADMIN="YOUR_ADMIN_ADDRESS"
export OPTIMISM_ADMIN="YOUR_ADMIN_ADDRESS"
export POLYGON_ADMIN="YOUR_ADMIN_ADDRESS"
export SCROLL_ADMIN="YOUR_ADMIN_ADDRESS"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ artifacts
broadcast
cache
coverage
deployments
docs
node_modules
out-optimized
Expand Down
13 changes: 1 addition & 12 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,6 @@
out = "docs"
repository = "https://github.com/sablier-labs/v2-periphery"

[etherscan]
arbitrum_one = { key = "${API_KEY_ARBISCAN}" }
avalanche = { key = "${API_KEY_SNOWTRACE" }
bnb_smart_chain = { key = "${API_KEY_BSCSCAN}" }
gnosis_chain = { key = "${API_KEY_GNOSISSCAN}" }
goerli = { key = "${API_KEY_ETHERSCAN}" }
mainnet = { key = "${API_KEY_ETHERSCAN}" }
optimism = { key = "${API_KEY_OPTIMISTIC_ETHERSCAN}" }
polygon = { key = "${API_KEY_POLYGONSCAN}" }
sepolia = { key = "${API_KEY_ETHERSCAN}" }

[fmt]
bracket_spacing = true
int_types = "long"
Expand All @@ -69,7 +58,7 @@
gnosis_chain = "https://rpc.gnosischain.com"
goerli = "${RPC_URL_GOERLI}"
localhost = "http://localhost:8545"
mainnet = "${RPC_URL_MAINNET}"
mainnet = "${MAINNET_RPC_URL}"
optimism = "https://optimism-mainnet.infura.io/v3/${API_KEY_INFURA}"
polygon = "https://polygon-mainnet.infura.io/v3/${API_KEY_INFURA}"
sepolia = "RPC_URL_SEPOLIA"
47 changes: 47 additions & 0 deletions script/DeployDeterministicProtocol2.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.19 <0.9.0;

import { SablierV2Comptroller } from "@sablier/v2-core/src/SablierV2Comptroller.sol";
import { SablierV2LockupDynamic } from "@sablier/v2-core/src/SablierV2LockupDynamic.sol";
import { SablierV2LockupLinear } from "@sablier/v2-core/src/SablierV2LockupLinear.sol";
import { SablierV2NFTDescriptor } from "@sablier/v2-core/src/SablierV2NFTDescriptor.sol";

import { BaseScript } from "./Base.s.sol";

import { SablierV2Batch } from "../src/SablierV2Batch.sol";
import { SablierV2MerkleStreamerFactory } from "../src/SablierV2MerkleStreamerFactory.sol";

/// @notice Deploys the Sablier V2 Protocol deterministically expect for comptroller.
contract DeployDeterministicPeriphery is BaseScript {
/// @dev The presence of the salt instructs Forge to deploy the contract via a deterministic CREATE2 factory.
/// https://github.com/Arachnid/deterministic-deployment-proxy
function run(
string memory create2Salt,
address initialAdmin,
SablierV2Comptroller comptroller
)
public
virtual
broadcast
returns (
SablierV2LockupDynamic lockupDynamic,
SablierV2LockupLinear lockupLinear,
SablierV2NFTDescriptor nftDescriptor,
SablierV2Batch batch,
SablierV2MerkleStreamerFactory merkleStreamerFactory
)
{
bytes32 salt = bytes32(abi.encodePacked(create2Salt));

nftDescriptor = new SablierV2NFTDescriptor{ salt: salt }();
lockupLinear = new SablierV2LockupLinear{ salt: salt }(initialAdmin, comptroller, nftDescriptor);

uint256 maxSegmentCount = 300;

lockupDynamic =
new SablierV2LockupDynamic{ salt: salt }(initialAdmin, comptroller, nftDescriptor, maxSegmentCount);

batch = new SablierV2Batch{ salt: salt }();
merkleStreamerFactory = new SablierV2MerkleStreamerFactory{ salt: salt }();
}
}
40 changes: 40 additions & 0 deletions script/DeployProtocol2.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.19 <0.9.0;

import { SablierV2Comptroller } from "@sablier/v2-core/src/SablierV2Comptroller.sol";
import { SablierV2LockupDynamic } from "@sablier/v2-core/src/SablierV2LockupDynamic.sol";
import { SablierV2LockupLinear } from "@sablier/v2-core/src/SablierV2LockupLinear.sol";
import { SablierV2NFTDescriptor } from "@sablier/v2-core/src/SablierV2NFTDescriptor.sol";
import { BaseScript } from "./Base.s.sol";

import { SablierV2MerkleStreamerFactory } from "../src/SablierV2MerkleStreamerFactory.sol";
import { SablierV2Batch } from "../src/SablierV2Batch.sol";

/// @notice Deploys the Sablier V2 Protocol expect for comptroller.
contract DeployProtocol is BaseScript {
function run(
address initialAdmin,
SablierV2Comptroller comptroller
)
public
virtual
broadcast
returns (
SablierV2LockupDynamic lockupDynamic,
SablierV2LockupLinear lockupLinear,
SablierV2NFTDescriptor nftDescriptor,
SablierV2Batch batch,
SablierV2MerkleStreamerFactory merkleStreamerFactory
)
{
uint256 maxSegmentCount = 300;

// Deploy V2 Core.
nftDescriptor = new SablierV2NFTDescriptor();
lockupDynamic = new SablierV2LockupDynamic(initialAdmin, comptroller, nftDescriptor, maxSegmentCount);
lockupLinear = new SablierV2LockupLinear(initialAdmin, comptroller, nftDescriptor);

batch = new SablierV2Batch();
merkleStreamerFactory = new SablierV2MerkleStreamerFactory();
}
}
198 changes: 198 additions & 0 deletions shell/deploy-multi-chains.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
#!/usr/bin/env bash

# Usage: ./shell/deploy-multi-chains.sh [options] [chain1 [chain2 ...]]
# Options:
# --deterministic Deploy using the deterministic script.
# --broadcast Broadcast the deployment and verify on Etherscan.
# Example: ./shell/deploy-multi-chains.sh # Default deploys only to Sepolia
# Example: ./shell/deploy-multi-chains.sh --broadcast arbitrum_one mainnet
# Example: ./shell/deploy-multi-chains.sh --deterministic --broadcast mainnet

# Make sure you set-up your .env file first. See .env.example.

# Pre-requisites:
# - foundry (https://getfoundry.sh)

# Strict mode: https://gist.github.com/vncsna/64825d5609c146e80de8b1fd623011ca
set -euo pipefail

# Create deployments directory
deployments=./deployments
rm -rf $deployments
mkdir $deployments

# Declare chain IDs
ARBITRUM_CHAIN_ID="42161"
AVALANCHE_CHAIN_ID="43114"
BASE_CHAIN_ID="8453"
BSC_CHAIN_ID="56"
GOERLI_CHAIN_ID="5"
GNOSIS_CHAIN_ID="100"
MAINNET_CHAIN_ID="1"
OPTIMISM_CHAIN_ID="10"
POLYGON_CHAIN_ID="137"
SCROLL_CHAIN_ID="534352"
SEPOLIA_CHAIN_ID="11155111"

# from: https://docs.sablier.com/contracts/v2/deployments
ARBITRUM_COMPTROLLER="0x17Ec73692F0aDf7E7C554822FBEAACB4BE781762"
AVALANCHE_COMPTROLLER="0x66F5431B0765D984f82A4fc4551b2c9ccF7eAC9C"
BASE_COMPTROLLER="0x7Faaedd40B1385C118cA7432952D9DC6b5CbC49e"
BSC_COMPTROLLER="0x33511f69A784Fd958E6713aCaC7c9dCF1A5578E8"
MAINNET_COMPTROLLER="0xC3Be6BffAeab7B297c03383B4254aa3Af2b9a5BA"
GNOSIS_COMPTROLLER="0x73962c44c0fB4cC5e4545FB91732a5c5e87F55C2"
OPTIMISM_COMPTROLLER="0x1EECb6e6EaE6a1eD1CCB4323F3a146A7C5443A10"
POLYGON_COMPTROLLER="0x9761692EDf10F5F2A69f0150e2fd50dcecf05F2E"
SCROLL_COMPTROLLER="0x859708495E3B3c61Bbe19e6E3E1F41dE3A5C5C5b"
SEPOLIA_COMPTROLLER="0x2006d43E65e66C5FF20254836E63947FA8bAaD68"

# Define chain configurations
declare -A chains
chains["arbitrum_one"]="$ARBITRUM_RPC_URL $ARBISCAN_API_KEY $ARBITRUM_CHAIN_ID $ARBITRUM_ADMIN $ARBITRUM_COMPTROLLER"
chains["avalanche"]="$AVALANCHE_RPC_URL $SNOWTRACE_API_KEY $AVALANCHE_CHAIN_ID $AVALANCHE_ADMIN $AVALANCHE_COMPTROLLER"
chains["base"]="$BASE_RPC_URL $BASESCAN_API_KEY $BASE_CHAIN_ID $BASE_ADMIN $BASE_ADMIN $BASE_COMPTROLLER"
chains["bnb_smart_chain"]="$BSC_RPC_URL $BSCSCAN_API_KEY $BSC_CHAIN_ID $BSC_ADMIN $BSC_COMPTROLLER"
chains["gnosis"]="$GNOSIS_RPC_URL $GNOSISSCAN_API_KEY $GNOSIS_CHAIN_ID $GNOSIS_ADMIN $GNOSIS_COMPTROLLER"
chains["mainnet"]="$MAINNET_RPC_URL $ETHERSCAN_API_KEY $MAINNET_CHAIN_ID $MAINNET_ADMIN $MAINNET_COMPTROLLER"
chains["optimism"]="$OPTIMISM_RPC_URL $OPTIMISTIC_API_KEY $OPTIMISM_CHAIN_ID $OPTIMISM_ADMIN $OPTIMISM_COMPTROLLER"
chains["polygon"]="$POLYGON_RPC_URL $POLYGONSCAN_API_KEY $POLYGON_CHAIN_ID $POLYGON_ADMIN $POLYGON_COMPTROLLER"
chains["scroll"]="$SCROLL_RPC_URL $SCROLL_API_KEY $SCROLL_CHAIN_ID $SCROLL_ADMIN $SCROLL_COMPTROLLER"
chains["sepolia"]="$SEPOLIA_RPC_URL $ETHERSCAN_API_KEY $SEPOLIA_CHAIN_ID $SEPOLIA_ADMIN $SEPOLIA_COMPTROLLER"

# Flag for broadcast deployment
BROADCAST_DEPLOYMENT=false

# Flag for deterministic deployment
DETERMINISTIC_DEPLOYMENT=false

# Flag for gas price
WITH_GAS_PRICE=false
GAS_PRICE=0

# Requested chains
requested_chains=()

# Check for arguments passed to the script
for ((i=1; i<=$#; i++)); do
arg=${!i}

# Check for '--broadcast' flag in the arguments
if [[ $arg == "--broadcast" ]]; then
BROADCAST_DEPLOYMENT=true
fi

# Check for '--broadcast' flag in the arguments
if [[ $arg == "--deterministic" ]]; then
DETERMINISTIC_DEPLOYMENT=true
fi

# Check for '--with-gas-price' flag in the arguments
if [[ $arg == "--with-gas-price" ]]; then
WITH_GAS_PRICE=true
# Increment index to get the next argument, which should be the gas price
((i++))
GAS_PRICE=${!i}
if ! [[ $GAS_PRICE =~ ^[0-9]+$ ]]; then
echo "Error: Gas price must be a number."
exit 1
fi
fi

# Check for '--all' flag in the arguments
if [[ $arg == "--all" ]]; then
requested_chains=("${!chains[@]}")
fi

# Check for passed chains
if [[ $arg != "--all" && $arg != "--deterministic" && $arg != "--broadcast" && $arg != "--with-gas-price" ]]; then
requested_chains+=("$arg")
fi
done

# Set the default chain to Sepolia if no chains are requested
if [ ${#requested_chains[@]} -eq 0 ]; then
requested_chains=("sepolia")
fi

# Compile the contracts
echo "Compiling the contracts..."
FOUNDRY_PROFILE=optimized forge build

# Deploy to requested chains
for chain in "${requested_chains[@]}"; do
# Check if the requested chain is defined
if [[ ! -v "chains[$chain]" ]]; then
echo "Chain configuration for '$chain' not found."
continue
fi

# Split the configuration into RPC, API key and the Chain ID
IFS=' ' read -r rpc_url api_key chain_id admin comptroller<<< "${chains[$chain]}"

# Declare a deployment command
deployment_command="";

# Choose the script based on the flag
if [[ $DETERMINISTIC_DEPLOYMENT == true ]]; then
echo "Deploying deterministic contracts to $chain..."
# Construct the command
deployment_command="forge script script/DeployDeterministicProtocol2.s.sol \
--rpc-url $rpc_url \
--sig run(string,address,address) \
\"ChainID $chain_id, Version 1.1.0\" \
$admin \
$comptroller \
-vvv"
else
echo "Deploying contracts to $chain..."
# Construct the command
deployment_command="forge script script/DeployProtocol2.s.sol \
--rpc-url $rpc_url \
--sig run(address,address) \
$admin \
$comptroller \
-vvv"
fi

# Append additional options if broadcast is enabled
if [[ $BROADCAST_DEPLOYMENT == true ]]; then
echo "This deployment is broadcasted on $chain"
deployment_command+=" --broadcast --verify --etherscan-api-key \"$api_key\""
else
echo "This deployment is simulated on $chain"
fi

# Append additional options if gas price is enabled
if [[ $WITH_GAS_PRICE == true ]]; then
gas_price_in_gwei=$(echo "scale=2; $GAS_PRICE / 1000000000" | bc)
echo "This deployment is using gas price of $gas_price_in_gwei gwei"
deployment_command+=" --with-gas-price $GAS_PRICE"
fi

# Run the deployment command
output=$(FOUNDRY_PROFILE=optimized $deployment_command)

# Create a file for the chain
chain_file="$deployments/$chain.txt"
touch "$chain_file"

# Extract and save contract addresses
nftDescriptor_address=$(echo "$output" | awk '/nftDescriptor: contract/{print $NF}')
lockupLinear_address=$(echo "$output" | awk '/lockupLinear: contract/{print $NF}')
lockupDynamic_address=$(echo "$output" | awk '/lockupDynamic: contract/{print $NF}')
batch_address=$(echo "$output" | awk '/batch: contract/{print $NF}')
merkleStreamerFactory_address=$(echo "$output" | awk '/merkleStreamerFactory: contract/{print $NF}')

# Save to the chain file
{
echo "NFTDescriptor = $nftDescriptor_address"
echo "LockupLinear = $lockupLinear_address"
echo "LockupDynamic = $lockupDynamic_address"
echo "SablierV2Batch = $batch_address"
echo "SablierV2MerkleStreamerFactory = $merkleStreamerFactory_address"
} >> "$chain_file"

echo "Deployment for $chain done. Addresses saved in $chain_file"
done

echo "All deployments completed."

0 comments on commit 6a3ca31

Please sign in to comment.