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

Added execute transfer functionality #334

Merged
merged 7 commits into from
Sep 6, 2024
Merged
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
127 changes: 106 additions & 21 deletions cmd/migration/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ import (
"github.com/ethereum/go-ethereum/ethclient"
ethereumClient "github.com/multiversx/mx-bridge-eth-go/clients/ethereum"
"github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract"
"github.com/multiversx/mx-bridge-eth-go/clients/ethereum/wrappers"
"github.com/multiversx/mx-bridge-eth-go/clients/gasManagement"
"github.com/multiversx/mx-bridge-eth-go/clients/gasManagement/factory"
"github.com/multiversx/mx-bridge-eth-go/clients/multiversx"
"github.com/multiversx/mx-bridge-eth-go/cmd/migration/disabled"
"github.com/multiversx/mx-bridge-eth-go/config"
"github.com/multiversx/mx-bridge-eth-go/core"
"github.com/multiversx/mx-bridge-eth-go/executors/ethereum"
chainCore "github.com/multiversx/mx-chain-core-go/core"
logger "github.com/multiversx/mx-chain-logger-go"
Expand All @@ -37,6 +41,12 @@ const (

var log = logger.GetOrCreate("main")

type internalComponents struct {
batch *ethereum.BatchInfo
cryptoHandler ethereumClient.CryptoHandler
ethClient *ethclient.Client
}

func main() {
app := cli.NewApp()
app.Name = "Funds migration CLI tool"
Expand Down Expand Up @@ -80,15 +90,17 @@ func execute(ctx *cli.Context) error {
operationMode := strings.ToLower(ctx.GlobalString(mode.Name))
switch operationMode {
case signMode:
return generateAndSign(ctx, cfg)

_, err = generateAndSign(ctx, cfg)
return err
case executeMode:
//TODO: implement
return executeTransfer(ctx, cfg)
}

return fmt.Errorf("unknown execution mode: %s", operationMode)
}

func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error {
func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) (*internalComponents, error) {
argsProxy := blockchain.ArgsProxy{
ProxyURL: cfg.MultiversX.NetworkAddress,
SameScState: false,
Expand All @@ -100,18 +112,18 @@ func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error {
}
proxy, err := blockchain.NewProxy(argsProxy)
if err != nil {
return err
return nil, err
}

dummyAddress := data.NewAddressFromBytes(bytes.Repeat([]byte{0x1}, 32))
multisigAddress, err := data.NewAddressFromBech32String(cfg.MultiversX.MultisigContractAddress)
if err != nil {
return err
return nil, err
}

safeAddress, err := data.NewAddressFromBech32String(cfg.MultiversX.SafeContractAddress)
if err != nil {
return err
return nil, err
}

argsMXClientDataGetter := multiversx.ArgsMXClientDataGetter{
Expand All @@ -123,12 +135,12 @@ func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error {
}
mxDataGetter, err := multiversx.NewMXClientDataGetter(argsMXClientDataGetter)
if err != nil {
return err
return nil, err
}

ethClient, err := ethclient.Dial(cfg.Eth.NetworkAddress)
if err != nil {
return err
return nil, err
}

argsContractsHolder := ethereumClient.ArgsErc20SafeContractsHolder{
Expand All @@ -137,13 +149,13 @@ func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error {
}
erc20ContractsHolder, err := ethereumClient.NewErc20SafeContractsHolder(argsContractsHolder)
if err != nil {
return err
return nil, err
}

safeEthAddress := common.HexToAddress(cfg.Eth.SafeContractAddress)
safeInstance, err := contract.NewERC20Safe(safeEthAddress, ethClient)
if err != nil {
return err
return nil, err
}

argsCreator := ethereum.ArgsMigrationBatchCreator{
Expand All @@ -156,36 +168,36 @@ func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error {

creator, err := ethereum.NewMigrationBatchCreator(argsCreator)
if err != nil {
return err
return nil, err
}

newSafeAddressString := ctx.GlobalString(newSafeAddress.Name)
if len(newSafeAddressString) == 0 {
return fmt.Errorf("invalid new safe address for Ethereum")
return nil, fmt.Errorf("invalid new safe address for Ethereum")
}
newSafeAddressValue := common.HexToAddress(ctx.GlobalString(newSafeAddress.Name))

batchInfo, err := creator.CreateBatchInfo(context.Background(), newSafeAddressValue)
if err != nil {
return err
return nil, err
}

val, err := json.MarshalIndent(batchInfo, "", " ")
if err != nil {
return err
return nil, err
}

cryptoHandler, err := ethereumClient.NewCryptoHandler(cfg.Eth.PrivateKeyFile)
if err != nil {
return err
return nil, err
}

log.Info("signing batch", "message hash", batchInfo.MessageHash.String(),
"public key", cryptoHandler.GetAddress().String())

signature, err := cryptoHandler.Sign(batchInfo.MessageHash)
if err != nil {
return err
return nil, err
}

log.Info("Migration .json file contents: \n" + string(val))
Expand All @@ -194,26 +206,99 @@ func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error {
jsonFilename = applyTimestamp(jsonFilename)
err = os.WriteFile(jsonFilename, val, os.ModePerm)
if err != nil {
return err
return nil, err
}

sigInfo := &ethereum.SignatureInfo{
PublicKey: cryptoHandler.GetAddress().String(),
Address: cryptoHandler.GetAddress().String(),
MessageHash: batchInfo.MessageHash.String(),
Signature: hex.EncodeToString(signature),
}

sigFilename := ctx.GlobalString(signatureJsonFile.Name)
sigFilename = applyTimestamp(sigFilename)
sigFilename = applyPublicKey(sigFilename, sigInfo.PublicKey)
sigFilename = applyPublicKey(sigFilename, sigInfo.Address)
val, err = json.MarshalIndent(sigInfo, "", " ")
if err != nil {
return err
return nil, err
}

log.Info("Signature .json file contents: \n" + string(val))

return os.WriteFile(sigFilename, val, os.ModePerm)
err = os.WriteFile(sigFilename, val, os.ModePerm)
if err != nil {
return nil, err
}

return &internalComponents{
batch: batchInfo,
cryptoHandler: cryptoHandler,
ethClient: ethClient,
}, nil
}

func executeTransfer(ctx *cli.Context, cfg config.MigrationToolConfig) error {
components, err := generateAndSign(ctx, cfg)
if err != nil {
return err
}

bridgeEthAddress := common.HexToAddress(cfg.Eth.MultisigContractAddress)
multiSigInstance, err := contract.NewBridge(bridgeEthAddress, components.ethClient)
if err != nil {
return err
}

safeEthAddress := common.HexToAddress(cfg.Eth.SafeContractAddress)
safeInstance, err := contract.NewERC20Safe(safeEthAddress, components.ethClient)
if err != nil {
return err
}

argsClientWrapper := wrappers.ArgsEthereumChainWrapper{
StatusHandler: &disabled.StatusHandler{},
MultiSigContract: multiSigInstance,
SafeContract: safeInstance,
BlockchainClient: components.ethClient,
}
ethereumChainWrapper, err := wrappers.NewEthereumChainWrapper(argsClientWrapper)
if err != nil {
return err
}

gasStationConfig := cfg.Eth.GasStation
argsGasStation := gasManagement.ArgsGasStation{
RequestURL: gasStationConfig.URL,
RequestPollingInterval: time.Duration(gasStationConfig.PollingIntervalInSeconds) * time.Second,
RequestRetryDelay: time.Duration(gasStationConfig.RequestRetryDelayInSeconds) * time.Second,
MaximumFetchRetries: gasStationConfig.MaxFetchRetries,
RequestTime: time.Duration(gasStationConfig.RequestTimeInSeconds) * time.Second,
MaximumGasPrice: gasStationConfig.MaximumAllowedGasPrice,
GasPriceSelector: core.EthGasPriceSelector(gasStationConfig.GasPriceSelector),
GasPriceMultiplier: gasStationConfig.GasPriceMultiplier,
}
gs, err := factory.CreateGasStation(argsGasStation, gasStationConfig.Enabled)
if err != nil {
return err
}

args := ethereum.ArgsMigrationBatchExecutor{
EthereumChainWrapper: ethereumChainWrapper,
CryptoHandler: components.cryptoHandler,
Batch: *components.batch,
Signatures: ethereum.LoadAllSignatures(log, configPath),
Logger: log,
GasHandler: gs,
TransferGasLimitBase: cfg.Eth.GasLimitBase,
TransferGasLimitForEach: cfg.Eth.GasLimitForEach,
}

executor, err := ethereum.NewMigrationBatchExecutor(args)
if err != nil {
return err
}

return executor.ExecuteTransfer(context.Background())
}

func loadConfig(filepath string) (config.MigrationToolConfig, error) {
Expand Down
2 changes: 1 addition & 1 deletion executors/ethereum/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type BatchInfo struct {

// SignatureInfo is the struct holding signature info
type SignatureInfo struct {
PublicKey string `json:"PublicKey"`
Address string `json:"Address"`
MessageHash string `json:"MessageHash"`
Signature string `json:"Signature"`
}
6 changes: 6 additions & 0 deletions executors/ethereum/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,10 @@ var (
errNilSafeContractWrapper = errors.New("nil safe contract wrapper")
errWrongERC20AddressResponse = errors.New("wrong ERC20 address response")
errNilLogger = errors.New("nil logger")
errNilCryptoHandler = errors.New("nil crypto handler")
errNilEthereumChainWrapper = errors.New("nil Ethereum chain wrapper")
errQuorumNotReached = errors.New("quorum not reached")
errInvalidSignature = errors.New("invalid signature")
errMultisigContractPaused = errors.New("multisig contract paused")
errNilGasHandler = errors.New("nil gas handler")
)
28 changes: 28 additions & 0 deletions executors/ethereum/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)

// TokensMapper can convert a token bytes from one chain to another
Expand All @@ -32,3 +33,30 @@ type MvxDataGetter interface {
GetERC20AddressForTokenId(ctx context.Context, tokenId []byte) ([][]byte, error)
IsInterfaceNil() bool
}

// EthereumChainWrapper defines the operations of the Ethereum wrapper
type EthereumChainWrapper interface {
ExecuteTransfer(opts *bind.TransactOpts, tokens []common.Address,
recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int,
signatures [][]byte) (*types.Transaction, error)
ChainID(ctx context.Context) (*big.Int, error)
BlockNumber(ctx context.Context) (uint64, error)
NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error)
Quorum(ctx context.Context) (*big.Int, error)
GetRelayers(ctx context.Context) ([]common.Address, error)
IsPaused(ctx context.Context) (bool, error)
}

// CryptoHandler defines the operations for a component that expose some crypto primitives
type CryptoHandler interface {
Sign(msgHash common.Hash) ([]byte, error)
GetAddress() common.Address
CreateKeyedTransactor(chainId *big.Int) (*bind.TransactOpts, error)
IsInterfaceNil() bool
}

// GasHandler defines the component able to fetch the current gas price
type GasHandler interface {
GetCurrentGasPrice() (*big.Int, error)
IsInterfaceNil() bool
}
Loading
Loading