Skip to content

Commit

Permalink
Add operator delegate-to command (#233)
Browse files Browse the repository at this point in the history
  • Loading branch information
aivarasko authored Oct 24, 2024
1 parent 39cf782 commit 4af04b1
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 0 deletions.
8 changes: 8 additions & 0 deletions pkg/internal/common/flags/general.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,12 @@ var (
Usage: "Suppress unnecessary output",
EnvVars: []string{"SILENT"},
}

ExpiryFlag = cli.Int64Flag{
Name: "expiry",
Aliases: []string{"e"},
Usage: "expiry in seconds",
EnvVars: []string{"EXPIRY"},
Value: 3600,
}
)
54 changes: 54 additions & 0 deletions pkg/internal/common/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package common

import (
"context"
"crypto/ecdsa"
"encoding/json"
"errors"
"fmt"
Expand All @@ -28,6 +29,7 @@ import (
eigenSdkUtils "github.com/Layr-Labs/eigensdk-go/utils"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
Expand Down Expand Up @@ -124,6 +126,7 @@ func getWallet(
if err != nil {
return nil, common.Address{}, err
}

return keyWallet, sender, nil
} else if cfg.SignerType == types.FireBlocksSigner {
var secretKey string
Expand Down Expand Up @@ -479,3 +482,54 @@ func GetNoSendTxOpts(from common.Address) *bind.TransactOpts {
func Trim0x(s string) string {
return strings.TrimPrefix(s, "0x")
}

func Sign(digest []byte, cfg types.SignerConfig, p utils.Prompter) ([]byte, error) {
var privateKey *ecdsa.PrivateKey

if cfg.SignerType == types.LocalKeystoreSigner {
ecdsaPassword, readFromPipe := utils.GetStdInPassword()
var err error
if !readFromPipe {
ecdsaPassword, err = p.InputHiddenString("Enter password to decrypt the ecdsa private key:", "",
func(password string) error {
return nil
},
)
if err != nil {
fmt.Println("Error while reading ecdsa key password")
return nil, err
}
}

jsonContent, err := os.ReadFile(cfg.PrivateKeyStorePath)
if err != nil {
return nil, err
}
key, err := keystore.DecryptKey(jsonContent, ecdsaPassword)
if err != nil {
return nil, err
}

privateKey = key.PrivateKey
} else if cfg.SignerType == types.FireBlocksSigner {
return nil, errors.New("FireBlocksSigner is not implemented")
} else if cfg.SignerType == types.Web3Signer {
return nil, errors.New("Web3Signer is not implemented")
} else if cfg.SignerType == types.PrivateKeySigner {
privateKey = cfg.PrivateKey
} else {
return nil, errors.New("signer is not implemented")
}

signed, err := crypto.Sign(digest, privateKey)
if err != nil {
return nil, err
}

// account for EIP-155 by incrementing V if necessary
if signed[crypto.RecoveryIDOffset] < 27 {
signed[crypto.RecoveryIDOffset] += 27
}

return signed, nil
}
1 change: 1 addition & 0 deletions pkg/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func OperatorCmd(p utils.Prompter) *cli.Command {
operator.StatusCmd(p),
operator.UpdateCmd(p),
operator.UpdateMetadataURICmd(p),
operator.GetApprovalCmd(p),
},
}

Expand Down
167 changes: 167 additions & 0 deletions pkg/operator/get_approval.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package operator

import (
"context"
"crypto/rand"
"encoding/hex"
"fmt"
"math/big"
"time"

"github.com/Layr-Labs/eigenlayer-cli/pkg/operator/keys"
eigenSdkUtils "github.com/Layr-Labs/eigensdk-go/utils"

"github.com/Layr-Labs/eigenlayer-cli/pkg/internal/common"
"github.com/Layr-Labs/eigenlayer-cli/pkg/internal/common/flags"
"github.com/Layr-Labs/eigenlayer-cli/pkg/telemetry"
"github.com/Layr-Labs/eigenlayer-cli/pkg/utils"

"github.com/Layr-Labs/eigensdk-go/chainio/clients/elcontracts"
elContracts "github.com/Layr-Labs/eigensdk-go/chainio/clients/elcontracts"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"

"github.com/urfave/cli/v2"
)

func GetApprovalCmd(p utils.Prompter) *cli.Command {
getApprovalCmd := &cli.Command{
Name: "get-approval",
Usage: "Generate the smart contract approval details for the delegateTo method",
UsageText: "get-approval <configuration-file> <staker-address>",
Description: `
Generate the smart contract approval details for the delegateTo method.
It expects the same configuration yaml file as an argument to the register command, along with the staker address.
Use the --expiry flag to override the default expiration of 3600 seconds.
`,
After: telemetry.AfterRunAction(),
Flags: []cli.Flag{
&flags.VerboseFlag,
&flags.ExpiryFlag,
},
Action: func(cCtx *cli.Context) error {
logger := common.GetLogger(cCtx)
args := cCtx.Args()
if args.Len() != 2 {
return fmt.Errorf("%w: accepts 2 arg, received %d", keys.ErrInvalidNumberOfArgs, args.Len())
}

expirySeconds := cCtx.Int64(flags.ExpiryFlag.Name)

configurationFilePath := args.Get(0)
stakerAddress := args.Get(1)

if !eigenSdkUtils.IsValidEthereumAddress(stakerAddress) {
return fmt.Errorf("staker address %s is not valid address", stakerAddress)
}

operatorCfg, err := common.ReadConfigFile(configurationFilePath)
if err != nil {
return err
}
cCtx.App.Metadata["network"] = operatorCfg.ChainId.String()

logger.Infof(
"%s Operator configuration file read successfully %s",
utils.EmojiCheckMark,
operatorCfg.Operator.Address,
)
logger.Info("%s validating operator config: %s", utils.EmojiWait, operatorCfg.Operator.Address)

ethClient, err := ethclient.Dial(operatorCfg.EthRPCUrl)
if err != nil {
return err
}
id, err := ethClient.ChainID(context.Background())
if err != nil {
return err
}

if id.Cmp(&operatorCfg.ChainId) != 0 {
return fmt.Errorf(
"%w: chain ID in config file %d does not match the chain ID of the network %d",
ErrInvalidYamlFile,
&operatorCfg.ChainId,
id,
)
}

logger.Infof(
"%s Operator configuration file validated successfully %s",
utils.EmojiCheckMark,
operatorCfg.Operator.Address,
)

contractCfg := elcontracts.Config{
DelegationManagerAddress: gethcommon.HexToAddress(operatorCfg.ELDelegationManagerAddress),
AvsDirectoryAddress: gethcommon.HexToAddress(operatorCfg.ELAVSDirectoryAddress),
}
reader, err := elContracts.NewReaderFromConfig(
contractCfg,
ethClient,
logger,
)
if err != nil {
return err
}

var staker = gethcommon.HexToAddress(stakerAddress)
var operator = gethcommon.HexToAddress(operatorCfg.Operator.Address)
var delegationApprover = gethcommon.HexToAddress(operatorCfg.Operator.DelegationApproverAddress)
salt := make([]byte, 32)

if _, err := rand.Read(salt); err != nil {
return err
}
var expiry = new(big.Int).SetInt64(time.Now().Unix() + expirySeconds)

callOpts := &bind.CallOpts{Context: context.Background()}

hash, err := reader.CalculateDelegationApprovalDigestHash(
callOpts,
staker,
operator,
delegationApprover,
[32]byte(salt),
expiry,
)
if err != nil {
return err
}

signed, err := common.Sign(hash[:], operatorCfg.SignerConfig, p)
if err != nil {
return err
}

fmt.Println()
fmt.Println("--------------------------- delegateTo for the staker ---------------------------")
fmt.Println()
fmt.Printf("operator: %s\n", operator)
fmt.Printf("approverSignatureAndExpiry.signature: %s\n", eigenSdkUtils.Add0x(hex.EncodeToString(signed)))
fmt.Printf("approverSignatureAndExpiry.expiry: %d\n", expiry)
fmt.Printf("approverSalt: %s\n", eigenSdkUtils.Add0x(hex.EncodeToString(salt)))
fmt.Println()
fmt.Println(
"--------------------------- CalculateDelegationApprovalDigestHash details ---------------------------",
)
fmt.Println()
fmt.Printf("staker: %s\n", staker)
fmt.Printf("operator: %s\n", operator)
fmt.Printf("_delegationApprover: %s\n", delegationApprover)
fmt.Printf("approverSalt: %s\n", eigenSdkUtils.Add0x(hex.EncodeToString(salt)))
fmt.Printf("expiry: %d\n", expiry)
fmt.Println()
fmt.Printf("result: %s\n", eigenSdkUtils.Add0x(hex.EncodeToString(hash[:])))
fmt.Println()
fmt.Println("------------------------------------------------------------------------")
fmt.Println()

return nil
},
}
return getApprovalCmd
}

0 comments on commit 4af04b1

Please sign in to comment.