Skip to content

Commit

Permalink
Adding output support to file or terminal without broadcast for user …
Browse files Browse the repository at this point in the history
…admin commands. (#270)

Co-authored-by: Brandon Chatham <[email protected]>
  • Loading branch information
2 people authored and shrimalmadhur committed Dec 19, 2024
1 parent 6b6f34c commit 1739cef
Show file tree
Hide file tree
Showing 9 changed files with 426 additions and 41 deletions.
88 changes: 83 additions & 5 deletions pkg/user/admin/accept.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package admin

import (
"context"
"fmt"
"sort"

"github.com/Layr-Labs/eigenlayer-cli/pkg/internal/common"
Expand All @@ -11,6 +12,7 @@ import (
"github.com/Layr-Labs/eigensdk-go/chainio/clients/elcontracts"
"github.com/Layr-Labs/eigensdk-go/logging"
eigenSdkUtils "github.com/Layr-Labs/eigensdk-go/utils"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
gethcommon "github.com/ethereum/go-ethereum/common"
gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
Expand All @@ -23,6 +25,10 @@ type AcceptAdminWriter interface {
ctx context.Context,
request elcontracts.AcceptAdminRequest,
) (*gethtypes.Receipt, error)
NewAcceptAdminTx(
txOpts *bind.TransactOpts,
request elcontracts.AcceptAdminRequest,
) (*gethtypes.Transaction, error)
}

func AcceptCmd(generator func(logging.Logger, *acceptAdminConfig) (AcceptAdminWriter, error)) *cli.Command {
Expand Down Expand Up @@ -56,17 +62,83 @@ func acceptAdmin(
return err
}

receipt, err := elWriter.AcceptAdmin(
ctx,
elcontracts.AcceptAdminRequest{AccountAddress: config.AccountAddress, WaitForReceipt: true},
)
acceptRequest := elcontracts.AcceptAdminRequest{
AccountAddress: config.AccountAddress,
WaitForReceipt: true,
}

if config.Broadcast {
return broadcastAcceptAdminTx(ctx, elWriter, config, acceptRequest)
}

return printAcceptAdminTx(logger, elWriter, config, acceptRequest)
}

func broadcastAcceptAdminTx(
ctx context.Context,
elWriter AcceptAdminWriter,
config *acceptAdminConfig,
request elcontracts.AcceptAdminRequest,
) error {
receipt, err := elWriter.AcceptAdmin(ctx, request)
if err != nil {
return err
return eigenSdkUtils.WrapError("failed to broadcast AcceptAdmin transaction", err)
}
common.PrintTransactionInfo(receipt.TxHash.String(), config.ChainID)
return nil
}

func printAcceptAdminTx(
logger logging.Logger,
elWriter AcceptAdminWriter,
config *acceptAdminConfig,
request elcontracts.AcceptAdminRequest,
) error {
ethClient, err := ethclient.Dial(config.RPCUrl)
if err != nil {
return err
}

noSendTxOpts := common.GetNoSendTxOpts(config.CallerAddress)
if common.IsSmartContractAddress(config.CallerAddress, ethClient) {
noSendTxOpts.GasLimit = 150_000
}

// Generate unsigned transaction
unsignedTx, err := elWriter.NewAcceptAdminTx(noSendTxOpts, request)
if err != nil {
return eigenSdkUtils.WrapError("failed to create unsigned tx", err)
}

if config.OutputType == string(common.OutputType_Calldata) {
calldataHex := gethcommon.Bytes2Hex(unsignedTx.Data())
if !common.IsEmptyString(config.OutputFile) {
err = common.WriteToFile([]byte(calldataHex), config.OutputFile)
if err != nil {
return err
}
logger.Infof("Call data written to file: %s", config.OutputFile)
} else {
fmt.Println(calldataHex)
}
} else {
if !common.IsEmptyString(config.OutputType) {
fmt.Println("output file not supported for pretty output type")
fmt.Println()
}
fmt.Printf(
"Pending admin at address %s will accept admin role\n",
config.CallerAddress,
)
}

txFeeDetails := common.GetTxFeeDetails(unsignedTx)
fmt.Println()
txFeeDetails.Print()
fmt.Println("To broadcast the transaction, use the --broadcast flag")
return nil
}

func readAndValidateAcceptAdminConfig(
cliContext *cli.Context,
logger logging.Logger,
Expand All @@ -76,6 +148,9 @@ func readAndValidateAcceptAdminConfig(
ethRpcUrl := cliContext.String(flags.ETHRpcUrlFlag.Name)
network := cliContext.String(flags.NetworkFlag.Name)
environment := cliContext.String(flags.EnvironmentFlag.Name)
outputType := cliContext.String(flags.OutputTypeFlag.Name)
outputFile := cliContext.String(flags.OutputFileFlag.Name)
broadcast := cliContext.Bool(flags.BroadcastFlag.Name)
if environment == "" {
environment = common.GetEnvFromNetwork(network)
}
Expand Down Expand Up @@ -120,6 +195,9 @@ func readAndValidateAcceptAdminConfig(
SignerConfig: *signerConfig,
ChainID: chainID,
Environment: environment,
OutputFile: outputFile,
OutputType: outputType,
Broadcast: broadcast,
}, nil
}

Expand Down
26 changes: 23 additions & 3 deletions pkg/user/admin/accept_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/Layr-Labs/eigensdk-go/chainio/clients/elcontracts"
"github.com/Layr-Labs/eigensdk-go/logging"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
gethcommon "github.com/ethereum/go-ethereum/common"
gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/assert"
Expand All @@ -15,7 +16,8 @@ import (
)

type mockAcceptAdminWriter struct {
acceptAdminFunc func(ctx context.Context, request elcontracts.AcceptAdminRequest) (*gethtypes.Receipt, error)
acceptAdminFunc func(ctx context.Context, request elcontracts.AcceptAdminRequest) (*gethtypes.Receipt, error)
newAcceptAdminTxFunc func(txOpts *bind.TransactOpts, request elcontracts.AcceptAdminRequest) (*gethtypes.Transaction, error)
}

func (m *mockAcceptAdminWriter) AcceptAdmin(
Expand All @@ -24,16 +26,29 @@ func (m *mockAcceptAdminWriter) AcceptAdmin(
) (*gethtypes.Receipt, error) {
return m.acceptAdminFunc(ctx, request)
}
func (m *mockAcceptAdminWriter) NewAcceptAdminTx(
txOpts *bind.TransactOpts,
request elcontracts.AcceptAdminRequest,
) (*gethtypes.Transaction, error) {
if m.newAcceptAdminTxFunc == nil {
return nil, errors.New("newAcceptAdminTxFunc not implemented")
}
return m.newAcceptAdminTxFunc(txOpts, request)
}

func generateMockAcceptAdminWriter(
receipt *gethtypes.Receipt,
tx *gethtypes.Transaction,
err error,
) func(logging.Logger, *acceptAdminConfig) (AcceptAdminWriter, error) {
return func(logger logging.Logger, config *acceptAdminConfig) (AcceptAdminWriter, error) {
return &mockAcceptAdminWriter{
acceptAdminFunc: func(ctx context.Context, request elcontracts.AcceptAdminRequest) (*gethtypes.Receipt, error) {
return receipt, err
},
newAcceptAdminTxFunc: func(txOpts *bind.TransactOpts, request elcontracts.AcceptAdminRequest) (*gethtypes.Transaction, error) {
return tx, err
},
}, nil
}
}
Expand All @@ -42,10 +57,11 @@ func TestAcceptCmd_Success(t *testing.T) {
mockReceipt := &gethtypes.Receipt{
TxHash: gethcommon.HexToHash("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"),
}
mockTx := &gethtypes.Transaction{}

app := cli.NewApp()
app.Commands = []*cli.Command{
AcceptCmd(generateMockAcceptAdminWriter(mockReceipt, nil)),
AcceptCmd(generateMockAcceptAdminWriter(mockReceipt, mockTx, nil)),
}

args := []string{
Expand All @@ -55,6 +71,7 @@ func TestAcceptCmd_Success(t *testing.T) {
"--eth-rpc-url", "https://ethereum-holesky.publicnode.com/",
"--network", "holesky",
"--ecdsa-private-key", "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd",
"--broadcast",
}

err := app.Run(args)
Expand Down Expand Up @@ -86,9 +103,11 @@ func TestAcceptCmd_GeneratorError(t *testing.T) {

func TestAcceptCmd_AcceptAdminError(t *testing.T) {
expectedError := "error accepting admin"
mockTx := &gethtypes.Transaction{}

app := cli.NewApp()
app.Commands = []*cli.Command{
AcceptCmd(generateMockAcceptAdminWriter(nil, errors.New(expectedError))),
AcceptCmd(generateMockAcceptAdminWriter(nil, mockTx, errors.New(expectedError))),
}

args := []string{
Expand All @@ -98,6 +117,7 @@ func TestAcceptCmd_AcceptAdminError(t *testing.T) {
"--eth-rpc-url", "https://ethereum-holesky.publicnode.com/",
"--network", "holesky",
"--ecdsa-private-key", "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd",
"--broadcast",
}

err := app.Run(args)
Expand Down
86 changes: 81 additions & 5 deletions pkg/user/admin/add_pending.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package admin

import (
"context"
"fmt"
"sort"

"github.com/Layr-Labs/eigenlayer-cli/pkg/internal/common"
Expand All @@ -11,6 +12,7 @@ import (
"github.com/Layr-Labs/eigensdk-go/chainio/clients/elcontracts"
"github.com/Layr-Labs/eigensdk-go/logging"
eigenSdkUtils "github.com/Layr-Labs/eigensdk-go/utils"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
gethcommon "github.com/ethereum/go-ethereum/common"
gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
Expand All @@ -23,6 +25,10 @@ type AddPendingAdminWriter interface {
ctx context.Context,
request elcontracts.AddPendingAdminRequest,
) (*gethtypes.Receipt, error)
NewAddPendingAdminTx(
txOpts *bind.TransactOpts,
request elcontracts.AddPendingAdminRequest,
) (*gethtypes.Transaction, error)
}

func AddPendingCmd(generator func(logging.Logger, *addPendingAdminConfig) (AddPendingAdminWriter, error)) *cli.Command {
Expand Down Expand Up @@ -56,13 +62,77 @@ func addPendingAdmin(
return err
}

addPendingRequest := elcontracts.AddPendingAdminRequest{
AccountAddress: config.AccountAddress,
AdminAddress: config.AdminAddress,
WaitForReceipt: true,
}

if config.Broadcast {
return broadcastAddPendingAdminTx(ctx, elWriter, config, addPendingRequest)
}
return printAddPendingAdminTx(logger, elWriter, config, addPendingRequest)
}

func printAddPendingAdminTx(
logger logging.Logger,
elWriter AddPendingAdminWriter,
config *addPendingAdminConfig,
request elcontracts.AddPendingAdminRequest,
) error {
ethClient, err := ethclient.Dial(config.RPCUrl)
if err != nil {
return err
}
noSendTxOpts := common.GetNoSendTxOpts(config.CallerAddress)
if common.IsSmartContractAddress(config.CallerAddress, ethClient) {
// address is a smart contract
noSendTxOpts.GasLimit = 150_000
}

unsignedTx, err := elWriter.NewAddPendingAdminTx(noSendTxOpts, request)
if err != nil {
return eigenSdkUtils.WrapError("failed to create unsigned tx", err)
}

if config.OutputType == string(common.OutputType_Calldata) {
calldataHex := gethcommon.Bytes2Hex(unsignedTx.Data())
if !common.IsEmptyString(config.OutputFile) {
err = common.WriteToFile([]byte(calldataHex), config.OutputFile)
if err != nil {
return err
}
logger.Infof("Call data written to file: %s", config.OutputFile)
} else {
fmt.Println(calldataHex)
}
} else {
if !common.IsEmptyString(config.OutputType) {
fmt.Println("output file not supported for pretty output type")
fmt.Println()
}
fmt.Printf(
"Admin %s will be added as pending for account %s\n",
config.AdminAddress,
config.AccountAddress,
)
}
txFeeDetails := common.GetTxFeeDetails(unsignedTx)
fmt.Println()
txFeeDetails.Print()
fmt.Println("To broadcast the transaction, use the --broadcast flag")
return nil
}

func broadcastAddPendingAdminTx(
ctx context.Context,
elWriter AddPendingAdminWriter,
config *addPendingAdminConfig,
request elcontracts.AddPendingAdminRequest,
) error {
receipt, err := elWriter.AddPendingAdmin(
ctx,
elcontracts.AddPendingAdminRequest{
AccountAddress: config.AccountAddress,
AdminAddress: config.AdminAddress,
WaitForReceipt: true,
},
request,
)
if err != nil {
return err
Expand All @@ -81,6 +151,9 @@ func readAndValidateAddPendingAdminConfig(
ethRpcUrl := cliContext.String(flags.ETHRpcUrlFlag.Name)
network := cliContext.String(flags.NetworkFlag.Name)
environment := cliContext.String(flags.EnvironmentFlag.Name)
outputType := cliContext.String(flags.OutputTypeFlag.Name)
outputFile := cliContext.String(flags.OutputFileFlag.Name)
broadcast := cliContext.Bool(flags.BroadcastFlag.Name)
if environment == "" {
environment = common.GetEnvFromNetwork(network)
}
Expand Down Expand Up @@ -126,6 +199,9 @@ func readAndValidateAddPendingAdminConfig(
PermissionManagerAddress: gethcommon.HexToAddress(permissionManagerAddress),
ChainID: chainID,
Environment: environment,
OutputFile: outputFile,
OutputType: outputType,
Broadcast: broadcast,
}, nil
}

Expand Down
Loading

0 comments on commit 1739cef

Please sign in to comment.