complex chain generator
This commit is contained in:
parent
6d3943cf6f
commit
3cff19fc83
61
cmd/autoSend.go
Normal file
61
cmd/autoSend.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright © 2020 Vulcanize, Inc
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/vulcanize/tx_spammer/pkg/auto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// autoSendCmd represents the autoSend command
|
||||||
|
var autoSendCmd = &cobra.Command{
|
||||||
|
Use: "autoSend",
|
||||||
|
Short: "Send large volumes of different tx types to different nodes for testing purposes",
|
||||||
|
Long: `Loads tx configuration from .toml config file
|
||||||
|
Generates txs from configuration and provided private keys and sends them to designated node according to set frequency and number
|
||||||
|
Support standard, optimism L2, optimism L1 to L2, and EIP1559 transactions`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
subCommand = cmd.CalledAs()
|
||||||
|
logWithCommand = *logrus.WithField("SubCommand", subCommand)
|
||||||
|
autoSend()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func autoSend() {
|
||||||
|
config, err := auto.NewConfig()
|
||||||
|
if err != nil {
|
||||||
|
logWithCommand.Fatal(err)
|
||||||
|
}
|
||||||
|
txSpammer := auto.NewTxSpammer(config)
|
||||||
|
quitChan := make(chan bool)
|
||||||
|
doneChan := txSpammer.Loop(quitChan)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
shutdown := make(chan os.Signal)
|
||||||
|
signal.Notify(shutdown, os.Interrupt)
|
||||||
|
<-shutdown
|
||||||
|
close(quitChan)
|
||||||
|
}()
|
||||||
|
<-doneChan
|
||||||
|
|
||||||
|
}
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(autoSendCmd)
|
||||||
|
}
|
@ -18,7 +18,6 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
@ -46,9 +45,8 @@ func sendTxs() {
|
|||||||
logWithCommand.Fatal(err)
|
logWithCommand.Fatal(err)
|
||||||
}
|
}
|
||||||
txSpammer := manual.NewTxSpammer(params)
|
txSpammer := manual.NewTxSpammer(params)
|
||||||
wg := new(sync.WaitGroup)
|
|
||||||
quitChan := make(chan bool)
|
quitChan := make(chan bool)
|
||||||
txSpammer.Loop(wg, quitChan)
|
doneChan := txSpammer.Loop(quitChan)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
shutdown := make(chan os.Signal)
|
shutdown := make(chan os.Signal)
|
||||||
@ -56,7 +54,7 @@ func sendTxs() {
|
|||||||
<-shutdown
|
<-shutdown
|
||||||
close(quitChan)
|
close(quitChan)
|
||||||
}()
|
}()
|
||||||
wg.Wait()
|
<-doneChan
|
||||||
|
|
||||||
}
|
}
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -48,7 +48,7 @@ type Config struct {
|
|||||||
Client *rpc.Client
|
Client *rpc.Client
|
||||||
|
|
||||||
// Key pairs for the accounts we will use to deploy contracts and send txs
|
// Key pairs for the accounts we will use to deploy contracts and send txs
|
||||||
SenderKeys []*ecdsa.PrivateKey
|
SenderKeys []*ecdsa.PrivateKey
|
||||||
SenderAddrs []common.Address
|
SenderAddrs []common.Address
|
||||||
|
|
||||||
// Type of the txs we are working with
|
// Type of the txs we are working with
|
||||||
@ -92,8 +92,8 @@ type DeploymentConfig struct {
|
|||||||
|
|
||||||
// CallConfig holds the parameters for the contract calling txs
|
// CallConfig holds the parameters for the contract calling txs
|
||||||
type CallConfig struct {
|
type CallConfig struct {
|
||||||
GasLimit uint64
|
GasLimit uint64
|
||||||
GasPrice *big.Int
|
GasPrice *big.Int
|
||||||
|
|
||||||
MethodName string
|
MethodName string
|
||||||
ABI abi.ABI
|
ABI abi.ABI
|
||||||
@ -112,13 +112,12 @@ type SendConfig struct {
|
|||||||
Amount *big.Int
|
Amount *big.Int
|
||||||
|
|
||||||
DestinationAddresses []common.Address
|
DestinationAddresses []common.Address
|
||||||
Frequency time.Duration
|
Frequency time.Duration
|
||||||
Number uint64
|
Number uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: EIP1559Config
|
// todo: EIP1559Config
|
||||||
type EIP1559Config struct {
|
type EIP1559Config struct {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfig() (*Config, error) {
|
func NewConfig() (*Config, error) {
|
||||||
@ -217,7 +216,7 @@ func NewConfig() (*Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewOptimismConfig constructs and returns a new OptimismConfig
|
// NewOptimismConfig constructs and returns a new OptimismConfig
|
||||||
func NewOptimismConfig() *OptimismConfig{
|
func NewOptimismConfig() *OptimismConfig {
|
||||||
l1SenderStr := viper.GetString(ethOptimismL1Sender)
|
l1SenderStr := viper.GetString(ethOptimismL1Sender)
|
||||||
var l1Sender *common.Address
|
var l1Sender *common.Address
|
||||||
if l1SenderStr != "" {
|
if l1SenderStr != "" {
|
||||||
@ -306,11 +305,11 @@ func NewSendConfig(destinationAddrs []common.Address) (*SendConfig, error) {
|
|||||||
}
|
}
|
||||||
return &SendConfig{
|
return &SendConfig{
|
||||||
DestinationAddresses: destinationAddrs,
|
DestinationAddresses: destinationAddrs,
|
||||||
Frequency: viper.GetDuration(ethSendFrequency),
|
Frequency: viper.GetDuration(ethSendFrequency),
|
||||||
Number: viper.GetUint64(ethSendTotalNumber),
|
Number: viper.GetUint64(ethSendTotalNumber),
|
||||||
Amount: amount,
|
Amount: amount,
|
||||||
GasPrice: gasPrice,
|
GasPrice: gasPrice,
|
||||||
GasLimit: viper.GetUint64(ethSendGasLimit),
|
GasLimit: viper.GetUint64(ethSendGasLimit),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
36
pkg/auto/deployer.go
Normal file
36
pkg/auto/deployer.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// VulcanizeDB
|
||||||
|
// Copyright © 2020 Vulcanize
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package auto
|
||||||
|
|
||||||
|
import "crypto/ecdsa"
|
||||||
|
|
||||||
|
type ContractDeployer struct {
|
||||||
|
SenderKeys []*ecdsa.PrivateKey
|
||||||
|
Config *DeploymentConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewContractDeployer(config *Config) *ContractDeployer {
|
||||||
|
return &ContractDeployer{
|
||||||
|
Config: config.DeploymentConfig,
|
||||||
|
SenderKeys: config.SenderKeys,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cp *ContractDeployer) Deploy() error {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -118,4 +118,4 @@ func bindEnv() {
|
|||||||
viper.BindEnv(ethSendAmount, ETH_SEND_AMOUNT)
|
viper.BindEnv(ethSendAmount, ETH_SEND_AMOUNT)
|
||||||
viper.BindEnv(ethSendGasLimit, ETH_SEND_GAS_LIMIT)
|
viper.BindEnv(ethSendGasLimit, ETH_SEND_GAS_LIMIT)
|
||||||
viper.BindEnv(ethSendGasPrice, ETH_SEND_GAS_PRICE)
|
viper.BindEnv(ethSendGasPrice, ETH_SEND_GAS_PRICE)
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ func (s *EthSender) Send(quitChan <-chan bool, txRlpChan <-chan []byte) (<-chan
|
|||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case tx := <- txRlpChan:
|
case tx := <-txRlpChan:
|
||||||
if err := shared.SendRawTransaction(s.client, tx); err != nil {
|
if err := shared.SendRawTransaction(s.client, tx); err != nil {
|
||||||
errChan <- err
|
errChan <- err
|
||||||
}
|
}
|
||||||
|
56
pkg/auto/service.go
Normal file
56
pkg/auto/service.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// VulcanizeDB
|
||||||
|
// Copyright © 2020 Vulcanize
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package auto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/vulcanize/tx_spammer/pkg/shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Spammer underlying struct type for spamming service
|
||||||
|
type Spammer struct {
|
||||||
|
Deployer *ContractDeployer
|
||||||
|
Caller *ContractCaller
|
||||||
|
Sender *EthSender
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTxSpammer creates a new tx spamming service
|
||||||
|
func NewTxSpammer(config *Config) shared.Service {
|
||||||
|
return &Spammer{
|
||||||
|
Deployer: NewContractDeployer(config),
|
||||||
|
Caller: NewContractCaller(config),
|
||||||
|
Sender: NewEthSender(config),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Spammer) Loop(quitChan <-chan bool) <-chan bool {
|
||||||
|
forwardQuit := make(chan bool)
|
||||||
|
doneChan, errChan := s.Sender.Send(forwardQuit)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case err := <-errChan:
|
||||||
|
logrus.Error(err)
|
||||||
|
case forwardQuit <- <-quitChan:
|
||||||
|
return
|
||||||
|
case <-doneChan:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return doneChan
|
||||||
|
}
|
120
pkg/auto/tx_generator.go
Normal file
120
pkg/auto/tx_generator.go
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
// VulcanizeDB
|
||||||
|
// Copyright © 2020 Vulcanize
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package auto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/vulcanize/tx_spammer/pkg/shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TxGenerator generates and signs txs
|
||||||
|
type TxGenerator struct {
|
||||||
|
signer types.Signer
|
||||||
|
optimismConfig *OptimismConfig
|
||||||
|
eip1559Config *EIP1559Config
|
||||||
|
// keep track of account nonces locally so we aren't spamming to determine the nonce
|
||||||
|
// this assumes these accounts are not sending txs outside this process
|
||||||
|
nonces map[common.Address]*uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTxGenerator creates a new tx generator
|
||||||
|
func NewTxGenerator(config Config) *TxGenerator {
|
||||||
|
nonces := make(map[common.Address]*uint64)
|
||||||
|
for _, addr := range config.SenderAddrs {
|
||||||
|
startingNonce := uint64(0)
|
||||||
|
nonces[addr] = &startingNonce
|
||||||
|
}
|
||||||
|
return &TxGenerator{
|
||||||
|
signer: config.Signer,
|
||||||
|
nonces: nonces,
|
||||||
|
optimismConfig: config.OptimismConfig,
|
||||||
|
eip1559Config: config.EIP1559Config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenParams params for GenerateTx method calls
|
||||||
|
type GenParams struct {
|
||||||
|
Signer types.Signer
|
||||||
|
Sender common.Address
|
||||||
|
SenderKey *ecdsa.PrivateKey
|
||||||
|
To *common.Address
|
||||||
|
Amount *big.Int
|
||||||
|
GasLimit uint64
|
||||||
|
GasPrice *big.Int
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tg TxGenerator) GenerateTxs(quitChan <-chan bool) (<-chan bool, <-chan []byte, <-chan error) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateTx generates tx from the provided params
|
||||||
|
func (tg TxGenerator) GenerateTx(ty shared.TxType, params *GenParams) ([]byte, error) {
|
||||||
|
tx := make([]byte, 0)
|
||||||
|
switch ty {
|
||||||
|
case shared.OptimismL2:
|
||||||
|
return tg.genL2(params, tg.optimismConfig)
|
||||||
|
case shared.Standard:
|
||||||
|
return tg.gen(params)
|
||||||
|
case shared.EIP1559:
|
||||||
|
return tg.gen1559(params, tg.eip1559Config)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported tx type: %s", ty.String())
|
||||||
|
}
|
||||||
|
return tx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gen TxGenerator) genL2(params *GenParams, op *OptimismConfig) ([]byte, error) {
|
||||||
|
nonce := atomic.AddUint64(gen.nonces[params.Sender], 1)
|
||||||
|
tx := new(types.Transaction)
|
||||||
|
if params.To == nil {
|
||||||
|
tx = types.NewContractCreation(nonce, params.Amount, params.GasLimit, params.GasPrice, params.Data, op.L1SenderAddr, op.L1RollupTxId, op.QueueOrigin)
|
||||||
|
if err := shared.WriteContractAddr(shared.DefaultDeploymentAddrLogPathPrefix, params.Sender, nonce); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
tx = types.NewTransaction(nonce, *params.To, params.Amount, params.GasLimit, params.GasPrice, params.Data, op.L1SenderAddr, op.L1RollupTxId, op.QueueOrigin, op.SigHashType)
|
||||||
|
}
|
||||||
|
signedTx, err := types.SignTx(tx, params.Signer, params.SenderKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
txRlp, err := rlp.EncodeToBytes(signedTx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return txRlp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gen TxGenerator) gen(params *GenParams) ([]byte, error) {
|
||||||
|
// TODO: support standard geth
|
||||||
|
return nil, errors.New("L1 support not yet available")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gen TxGenerator) gen1559(params *GenParams, eip1559Config *EIP1559Config) ([]byte, error) {
|
||||||
|
// TODO: support EIP1559
|
||||||
|
return nil, errors.New("1559 support not yet available")
|
||||||
|
}
|
@ -19,12 +19,13 @@ package manual
|
|||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/vulcanize/tx_spammer/pkg/shared"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/vulcanize/tx_spammer/pkg/shared"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
@ -54,4 +54,4 @@ const (
|
|||||||
func bindEnv() {
|
func bindEnv() {
|
||||||
viper.BindEnv("eth.txs", ETH_TX_LIST)
|
viper.BindEnv("eth.txs", ETH_TX_LIST)
|
||||||
viper.BindEnv("eth.addrLogPath", ETH_ADDR_LOG)
|
viper.BindEnv("eth.addrLogPath", ETH_ADDR_LOG)
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,11 @@ package manual
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/vulcanize/tx_spammer/pkg/shared"
|
"github.com/vulcanize/tx_spammer/pkg/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -86,4 +87,4 @@ func (s *TxSender) genAndSend(p TxParams) error {
|
|||||||
}
|
}
|
||||||
logrus.Infof("sending tx %s", p.Name)
|
logrus.Infof("sending tx %s", p.Name)
|
||||||
return shared.SendRawTransaction(p.Client, tx)
|
return shared.SendRawTransaction(p.Client, tx)
|
||||||
}
|
}
|
||||||
|
@ -18,4 +18,4 @@ package shared
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultDeploymentAddrLogPathPrefix = "./accounts/addresses/"
|
DefaultDeploymentAddrLogPathPrefix = "./accounts/addresses/"
|
||||||
)
|
)
|
||||||
|
@ -19,4 +19,4 @@ package shared
|
|||||||
// Service looping interface
|
// Service looping interface
|
||||||
type Service interface {
|
type Service interface {
|
||||||
Loop(quitChan <-chan bool) (doneChan <-chan bool)
|
Loop(quitChan <-chan bool) (doneChan <-chan bool)
|
||||||
}
|
}
|
||||||
|
@ -19,14 +19,15 @@ package shared
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/temp/common/hexutil"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChainConfig returns the appropriate ethereum chain config for the provided chain id
|
// ChainConfig returns the appropriate ethereum chain config for the provided chain id
|
||||||
@ -60,4 +61,4 @@ func WriteContractAddr(filePath string, senderAddr common.Address, nonce uint64)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return f.Close()
|
return f.Close()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user