Skip to content

Commit

Permalink
Added the ability to deploy multiple 'CHAIN_ID'
Browse files Browse the repository at this point in the history
  • Loading branch information
whonion committed Feb 27, 2024
1 parent d106238 commit b4be48a
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 121 deletions.
5 changes: 3 additions & 2 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ PRIVATE_KEY = '<YOUR_PRIVATE_KEY1>
<YOUR_PRIVATE_KEY8>
<YOUR_PRIVATE_KEYn>'
#Specify your instant RPC-node
RPC_PROVIDER='https://ethereum-goerli.publicnode.com'
CHAIN_ID = 5 #Specify chain_id for deploy contracts
#RPC_PROVIDER='https://ethereum-goerli.publicnode.com'
#Specify CHAIN_IDs for deploy contracts (In Example contracts'll deploy to Goerly and Sepolia chains for all PRIVATE_KEYs)
CHAIN_ID = '5,11155111'

#FAUCETS: https://faucetlink.to/
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ PRIVATE_KEY = '<YOUR_PRIVATE_KEY1>
<YOUR_PRIVATE_KEY7>
<YOUR_PRIVATE_KEY8>
<YOUR_PRIVATE_KEYn>'
CHAIN_ID = 10
RPC_PROVIDER = 'https://rpc.ankr.com/optimism/{YOUR_API_KEY}'
#Specify your instant RPC-node
#RPC_PROVIDER='https://ethereum-goerli.publicnode.com'
#Specify CHAIN_IDs for deploy contracts (In Example contracts'll deploy to Goerly and Sepolia chains for all PRIVATE_KEYs)
CHAIN_ID = '5,11155111'
```

- Add correct files *.sol to the `contacts` folder for deployment on the required chain
Expand Down
253 changes: 136 additions & 117 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,23 @@ func main() {
// Print the number of private keys for testing
fmt.Printf("Loaded wallets: %d\n", len(privateKeys))

// Load your chainIDs as a comma-separated string
chainIDsStr, ok := os.LookupEnv("CHAIN_ID")
if !ok {
log.Fatal("CHAIN_ID environment variable not set")
}
chainIDs := strings.Split(chainIDsStr, ",")

// Convert chain IDs to integers
var chains []int64
for _, id := range chainIDs {
chainID, err := strconv.ParseInt(strings.TrimSpace(id), 10, 64)
if err != nil {
log.Fatal(err)
}
chains = append(chains, chainID)
}

// Define EVM chains with corresponding explorer and RPC URLs
evmChains := map[int64]evmChain{
1: {
Expand Down Expand Up @@ -195,146 +212,148 @@ func main() {
RPCURL: "https://opbnb-rpc.publicnode.com",
},
}

// Iterate over each private key
for _, privateKeyStr := range privateKeys {
privateKey, err := crypto.HexToECDSA(strings.TrimSpace(privateKeyStr))
if err != nil {
log.Fatalf("Error parsing private key: %s", err)
}
// Load your chainID
chainIDStr, ok := os.LookupEnv("CHAIN_ID")
if !ok {
log.Fatal("CHAIN_ID environment variable not set")
}
chainID, err := strconv.ParseInt(chainIDStr, 10, 64)
if err != nil {
log.Fatal(err)
}
// Check if chainID exists in the evmChains map
chain, ok := evmChains[chainID]
if !ok {
log.Fatalf("Unsupported CHAIN_ID: %d", chainID)
}

// Connect to RPC Provider
rpcProvider, ok := os.LookupEnv("RPC_PROVIDER")
if !ok {
fmt.Printf("RPC_PROVIDER environment variable not set\n")
fmt.Printf("RPC will use the public node from https://chainlist.org/\n")
rpcProvider = chain.RPCURL
}

client, err := ethclient.Dial(rpcProvider)
if err != nil {
log.Fatal(err)
}
// Load and deploy each contract in succession
contractFiles, err := os.ReadDir("./contracts")
if err != nil {
log.Fatal(err)
}
for _, file := range contractFiles {
if filepath.Ext(file.Name()) == ".sol" {
// Compile contract
contractPath := filepath.Join("contracts", file.Name())
var cmd *exec.Cmd

if runtime.GOOS == "windows" {
cmd = exec.Command("cmd/solc.exe", "--allow-paths", "./node_modules/openzeppelin-solidity/", "--bin", "--abi", "--optimize", "--output-dir", "compiled_contracts", "--evm-version", "byzantium", "--overwrite", contractPath)
} else {
cmd = exec.Command("solc", "--allow-paths", "./node_modules/openzeppelin-solidity/", "--bin", "--abi", "--optimize", "--output-dir", "compiled_contracts", "--evm-version", "byzantium", "--overwrite", contractPath)
}

cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
// Iterate over each chain
for _, chainID := range chains {
// Check if chainID exists in the evmChains map
chain, ok := evmChains[chainID]
if !ok {
log.Fatalf("⛓️ Unsupported CHAIN_ID: %d", chainID)
}

// Get the absolute path to the bin folder
binDir, err := filepath.Abs("./compiled_contracts")
if err != nil {
log.Fatal(err)
// Connect to RPC Provider
rpcProvider, ok := os.LookupEnv("RPC_PROVIDER")
if !ok {
fmt.Printf("🛑 RPC_PROVIDER environment variable not set\n")
fmt.Printf("RPC will use the public node from 🌎 https://chainlist.org/\n")
rpcProvider = chain.RPCURL
}

// Rename output files
name := strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))
binFilename := fmt.Sprintf("%s.bin", name)
abiFilename := fmt.Sprintf("%s.abi", name)
binPath := filepath.Join(binDir, binFilename)
abiPath := filepath.Join(binDir, abiFilename)

// Get the bytecode and ABI from the compiled contract
bytecodeBytes, err := os.ReadFile(binPath)
if err != nil {
log.Fatal(err)
}
bytecodeStr := string(bytecodeBytes)
constructorBytes, err := hex.DecodeString(bytecodeStr[:len(bytecodeStr)-68])
client, err := ethclient.Dial(rpcProvider)
if err != nil {
log.Fatal(err)
}

abiBytes, err := os.ReadFile(abiPath)
if err != nil {
log.Fatal(err)
}
// Set the gas price and gas limit
gasPrice, err := client.SuggestGasPrice(context.Background())
// Load and deploy each contract in succession
contractFiles, err := os.ReadDir("./contracts")
if err != nil {
log.Fatal(err)
}

// Calculate the gas required for deploying the contract
estimateGas, err := client.EstimateGas(context.Background(), ethereum.CallMsg{
From: crypto.PubkeyToAddress(privateKey.PublicKey),
To: nil,
Data: constructorBytes,
})
if err != nil {
log.Fatal(err)
}
for _, file := range contractFiles {
{
if filepath.Ext(file.Name()) == ".sol" {
// Compile contract
contractPath := filepath.Join("contracts", file.Name())
var cmd *exec.Cmd

if err != nil {
fmt.Printf("Estimate gas overflow uint64\n")
log.Fatal(err)
}
fmt.Printf("Estimated gas for deploy: %v\n", estimateGas)
fmt.Printf("Contract %s will be deploy to: %s chain\n", file.Name(), chain.ChainName)
// Create a new instance of a transaction signer
auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(chainID))
if err != nil {
log.Fatal(err)
}
gasLimit := estimateGas
auth.GasPrice = gasPrice
auth.GasLimit = gasLimit + uint64(10000)
if runtime.GOOS == "windows" {
cmd = exec.Command("cmd/solc.exe", "--allow-paths", "./node_modules/openzeppelin-solidity/", "--bin", "--abi", "--optimize", "--output-dir", "compiled_contracts", "--evm-version", "byzantium", "--overwrite", contractPath)
} else {
cmd = exec.Command("solc", "--allow-paths", "./node_modules/openzeppelin-solidity/", "--bin", "--abi", "--optimize", "--output-dir", "compiled_contracts", "--evm-version", "byzantium", "--overwrite", contractPath)
}

// Load the contract's ABI
contractABI, err := abi.JSON(bytes.NewReader(abiBytes))
if err != nil {
log.Fatal(err)
}
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}

// Deploy the contract
address, tx, _, err := bind.DeployContract(auth, contractABI, constructorBytes, client)
if err != nil {
log.Fatal(err)
}
// Get the absolute path to the bin folder
binDir, err := filepath.Abs("./compiled_contracts")
if err != nil {
log.Fatal(err)
}

// Wait for the transaction to be mined
fmt.Printf("Contract %s waiting to be mined: %s\n", file.Name(), chain.ExplorerURL+"/tx/"+tx.Hash().Hex())
receipt, err := bind.WaitMined(context.Background(), client, tx)
if err != nil {
log.Fatal(err)
}
if receipt.Status != types.ReceiptStatusSuccessful {
log.Fatalf("contract %s deployment failed", file.Name())
}
// Rename output files
name := strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))
binFilename := fmt.Sprintf("%s.bin", name)
abiFilename := fmt.Sprintf("%s.abi", name)
binPath := filepath.Join(binDir, binFilename)
abiPath := filepath.Join(binDir, abiFilename)

// Get the bytecode and ABI from the compiled contract
bytecodeBytes, err := os.ReadFile(binPath)
if err != nil {
log.Fatal(err)
}
bytecodeStr := string(bytecodeBytes)
constructorBytes, err := hex.DecodeString(bytecodeStr[:len(bytecodeStr)-68])
if err != nil {
log.Fatal(err)
}

abiBytes, err := os.ReadFile(abiPath)
if err != nil {
log.Fatal(err)
}
// Set the gas price and gas limit
gasPrice, err := client.SuggestGasPrice(context.Background())
if err != nil {
log.Fatal(err)
}

// Print the contract address and the transaction hash
fmt.Printf("Contract %s deployed to: %s\n", file.Name(), chain.ExplorerURL+"/address/"+address.Hex())
// Calculate the gas required for deploying the contract
estimateGas, err := client.EstimateGas(context.Background(), ethereum.CallMsg{
From: crypto.PubkeyToAddress(privateKey.PublicKey),
To: nil,
Data: constructorBytes,
})
if err != nil {
log.Fatal(err)
}

if err != nil {
fmt.Printf("Estimate gas overflow uint64\n")
log.Fatal(err)
}
fmt.Printf("💸 Estimated gas for deploy: %v\n", estimateGas)
fmt.Printf("🛜 Contract %s will be deploy to: %s chain\n", file.Name(), chain.ChainName)
// Create a new instance of a transaction signer
auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(chainID))
if err != nil {
log.Fatal(err)
}
gasLimit := estimateGas
auth.GasPrice = gasPrice
auth.GasLimit = gasLimit + uint64(50000)

// Load the contract's ABI
contractABI, err := abi.JSON(bytes.NewReader(abiBytes))
if err != nil {
log.Fatal(err)
}

// Deploy the contract
address, tx, _, err := bind.DeployContract(auth, contractABI, constructorBytes, client)
if err != nil {
log.Fatal(err)
}

// Wait for the transaction to be mined
fmt.Printf("📃 Contract %s waiting to be mined: %s\n", file.Name(), chain.ExplorerURL+"/tx/"+tx.Hash().Hex())
receipt, err := bind.WaitMined(context.Background(), client, tx)
if err != nil {
log.Fatal(err)
}
if receipt.Status != types.ReceiptStatusSuccessful {
log.Fatalf("❌ contract %s deployment failed", file.Name())
}

// Print the contract address and the transaction hash
fmt.Printf("🚀 Contract %s deployed to: %s\n", file.Name(), chain.ExplorerURL+"/address/"+address.Hex())
}
}
}

}
}

0 comments on commit b4be48a

Please sign in to comment.