Skip to content

Commit

Permalink
Added the ability to load multiple 'PRIVATE_KEYs'
Browse files Browse the repository at this point in the history
  • Loading branch information
whonion committed Feb 26, 2024
1 parent 466b279 commit 058ba68
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 119 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Private files
seeds.txt
private_keys.txt
proxies.txt
.env
*.env
/.env
# Binaries for programs and plugins
/compiled_contracts/
*.exe
*.exe~
*.dll
Expand Down
250 changes: 131 additions & 119 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,17 @@ func main() {
if err != nil {
log.Fatal("Error loading .env file")
}
// Load your private key
privateKey, err := crypto.HexToECDSA(os.Getenv("PRIVATE_KEY"))
if err != nil {

// Load your private keys
privateKeysStr, ok := os.LookupEnv("PRIVATE_KEY")
if !ok {
log.Fatal("PRIVATE_KEY environment variable not set")
}
privateKeys := strings.Split(privateKeysStr, "\n")

// Print the number of private keys for testing
fmt.Printf("Loaded wallets: %d\n", len(privateKeys))

// Define EVM chains with corresponding explorer and RPC URLs
evmChains := map[int64]evmChain{
1: {
Expand Down Expand Up @@ -145,7 +151,7 @@ func main() {
ChainName: "Scroll Mainnet",
ExplorerURL: "https://scrollscan.com/",
RPCURL: "https://rpc.scroll.io",
},
},
59140: {
ChainID: 59140,
ChainName: "Linea(Testnet)",
Expand All @@ -165,140 +171,146 @@ func main() {
RPCURL: "https://testnet.cascadia.foundation",
},
}
// 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)
}
}

// Get the absolute path to the bin folder
binDir, err := filepath.Abs("./compiled_contracts")
if err != nil {
log.Fatal(err)
}

// 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)
for _, privateKeyStr := range privateKeys {
privateKey, err := crypto.HexToECDSA(strings.TrimSpace(privateKeyStr))
if err != nil {
log.Fatal(err)
log.Fatalf("Error parsing private key: %s", err)
}
bytecodeStr := string(bytecodeBytes)
constructorBytes, err := hex.DecodeString(bytecodeStr[:len(bytecodeStr)-68])
if err != nil {
log.Fatal(err)
// Load your chainID
chainIDStr, ok := os.LookupEnv("CHAIN_ID")
if !ok {
log.Fatal("CHAIN_ID environment variable not set")
}

abiBytes, err := os.ReadFile(abiPath)
chainID, err := strconv.ParseInt(chainIDStr, 10, 64)
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)
// Check if chainID exists in the evmChains map
chain, ok := evmChains[chainID]
if !ok {
log.Fatalf("Unsupported CHAIN_ID: %d", chainID)
}

// 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)
// 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 {
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))
// Load and deploy each contract in succession
contractFiles, err := os.ReadDir("./contracts")
if err != nil {
log.Fatal(err)
}
gasLimit := estimateGas
auth.GasPrice = gasPrice
auth.GasLimit = gasLimit + uint64(10000)
for _, file := range contractFiles {
if filepath.Ext(file.Name()) == ".sol" {
// Compile contract
contractPath := filepath.Join("contracts", file.Name())
var cmd *exec.Cmd

// Load the contract's ABI
contractABI, err := abi.JSON(bytes.NewReader(abiBytes))
if err != nil {
log.Fatal(err)
}
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)
}

// Deploy the contract
address, tx, _, err := bind.DeployContract(auth, contractABI, constructorBytes, client)
if err != nil {
log.Fatal(err)
}
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
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())
}
// Get the absolute path to the bin folder
binDir, err := filepath.Abs("./compiled_contracts")
if err != nil {
log.Fatal(err)
}

// 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(10000)

// 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 058ba68

Please sign in to comment.