Skip to content

Commit 0e5a6eb

Browse files
authored
Adding output support to file or terminal without broadcast for user admin commands. (#270)
Co-authored-by: Brandon Chatham <[email protected]>
1 parent 1dd031d commit 0e5a6eb

File tree

9 files changed

+426
-41
lines changed

9 files changed

+426
-41
lines changed

pkg/user/admin/accept.go

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package admin
22

33
import (
44
"context"
5+
"fmt"
56
"sort"
67

78
"github.com/Layr-Labs/eigenlayer-cli/pkg/internal/common"
@@ -11,6 +12,7 @@ import (
1112
"github.com/Layr-Labs/eigensdk-go/chainio/clients/elcontracts"
1213
"github.com/Layr-Labs/eigensdk-go/logging"
1314
eigenSdkUtils "github.com/Layr-Labs/eigensdk-go/utils"
15+
"github.com/ethereum/go-ethereum/accounts/abi/bind"
1416
gethcommon "github.com/ethereum/go-ethereum/common"
1517
gethtypes "github.com/ethereum/go-ethereum/core/types"
1618
"github.com/ethereum/go-ethereum/ethclient"
@@ -23,6 +25,10 @@ type AcceptAdminWriter interface {
2325
ctx context.Context,
2426
request elcontracts.AcceptAdminRequest,
2527
) (*gethtypes.Receipt, error)
28+
NewAcceptAdminTx(
29+
txOpts *bind.TransactOpts,
30+
request elcontracts.AcceptAdminRequest,
31+
) (*gethtypes.Transaction, error)
2632
}
2733

2834
func AcceptCmd(generator func(logging.Logger, *acceptAdminConfig) (AcceptAdminWriter, error)) *cli.Command {
@@ -56,17 +62,83 @@ func acceptAdmin(
5662
return err
5763
}
5864

59-
receipt, err := elWriter.AcceptAdmin(
60-
ctx,
61-
elcontracts.AcceptAdminRequest{AccountAddress: config.AccountAddress, WaitForReceipt: true},
62-
)
65+
acceptRequest := elcontracts.AcceptAdminRequest{
66+
AccountAddress: config.AccountAddress,
67+
WaitForReceipt: true,
68+
}
69+
70+
if config.Broadcast {
71+
return broadcastAcceptAdminTx(ctx, elWriter, config, acceptRequest)
72+
}
73+
74+
return printAcceptAdminTx(logger, elWriter, config, acceptRequest)
75+
}
76+
77+
func broadcastAcceptAdminTx(
78+
ctx context.Context,
79+
elWriter AcceptAdminWriter,
80+
config *acceptAdminConfig,
81+
request elcontracts.AcceptAdminRequest,
82+
) error {
83+
receipt, err := elWriter.AcceptAdmin(ctx, request)
6384
if err != nil {
64-
return err
85+
return eigenSdkUtils.WrapError("failed to broadcast AcceptAdmin transaction", err)
6586
}
6687
common.PrintTransactionInfo(receipt.TxHash.String(), config.ChainID)
6788
return nil
6889
}
6990

91+
func printAcceptAdminTx(
92+
logger logging.Logger,
93+
elWriter AcceptAdminWriter,
94+
config *acceptAdminConfig,
95+
request elcontracts.AcceptAdminRequest,
96+
) error {
97+
ethClient, err := ethclient.Dial(config.RPCUrl)
98+
if err != nil {
99+
return err
100+
}
101+
102+
noSendTxOpts := common.GetNoSendTxOpts(config.CallerAddress)
103+
if common.IsSmartContractAddress(config.CallerAddress, ethClient) {
104+
noSendTxOpts.GasLimit = 150_000
105+
}
106+
107+
// Generate unsigned transaction
108+
unsignedTx, err := elWriter.NewAcceptAdminTx(noSendTxOpts, request)
109+
if err != nil {
110+
return eigenSdkUtils.WrapError("failed to create unsigned tx", err)
111+
}
112+
113+
if config.OutputType == string(common.OutputType_Calldata) {
114+
calldataHex := gethcommon.Bytes2Hex(unsignedTx.Data())
115+
if !common.IsEmptyString(config.OutputFile) {
116+
err = common.WriteToFile([]byte(calldataHex), config.OutputFile)
117+
if err != nil {
118+
return err
119+
}
120+
logger.Infof("Call data written to file: %s", config.OutputFile)
121+
} else {
122+
fmt.Println(calldataHex)
123+
}
124+
} else {
125+
if !common.IsEmptyString(config.OutputType) {
126+
fmt.Println("output file not supported for pretty output type")
127+
fmt.Println()
128+
}
129+
fmt.Printf(
130+
"Pending admin at address %s will accept admin role\n",
131+
config.CallerAddress,
132+
)
133+
}
134+
135+
txFeeDetails := common.GetTxFeeDetails(unsignedTx)
136+
fmt.Println()
137+
txFeeDetails.Print()
138+
fmt.Println("To broadcast the transaction, use the --broadcast flag")
139+
return nil
140+
}
141+
70142
func readAndValidateAcceptAdminConfig(
71143
cliContext *cli.Context,
72144
logger logging.Logger,
@@ -76,6 +148,9 @@ func readAndValidateAcceptAdminConfig(
76148
ethRpcUrl := cliContext.String(flags.ETHRpcUrlFlag.Name)
77149
network := cliContext.String(flags.NetworkFlag.Name)
78150
environment := cliContext.String(flags.EnvironmentFlag.Name)
151+
outputType := cliContext.String(flags.OutputTypeFlag.Name)
152+
outputFile := cliContext.String(flags.OutputFileFlag.Name)
153+
broadcast := cliContext.Bool(flags.BroadcastFlag.Name)
79154
if environment == "" {
80155
environment = common.GetEnvFromNetwork(network)
81156
}
@@ -120,6 +195,9 @@ func readAndValidateAcceptAdminConfig(
120195
SignerConfig: *signerConfig,
121196
ChainID: chainID,
122197
Environment: environment,
198+
OutputFile: outputFile,
199+
OutputType: outputType,
200+
Broadcast: broadcast,
123201
}, nil
124202
}
125203

pkg/user/admin/accept_test.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/Layr-Labs/eigensdk-go/chainio/clients/elcontracts"
99
"github.com/Layr-Labs/eigensdk-go/logging"
10+
"github.com/ethereum/go-ethereum/accounts/abi/bind"
1011
gethcommon "github.com/ethereum/go-ethereum/common"
1112
gethtypes "github.com/ethereum/go-ethereum/core/types"
1213
"github.com/stretchr/testify/assert"
@@ -15,7 +16,8 @@ import (
1516
)
1617

1718
type mockAcceptAdminWriter struct {
18-
acceptAdminFunc func(ctx context.Context, request elcontracts.AcceptAdminRequest) (*gethtypes.Receipt, error)
19+
acceptAdminFunc func(ctx context.Context, request elcontracts.AcceptAdminRequest) (*gethtypes.Receipt, error)
20+
newAcceptAdminTxFunc func(txOpts *bind.TransactOpts, request elcontracts.AcceptAdminRequest) (*gethtypes.Transaction, error)
1921
}
2022

2123
func (m *mockAcceptAdminWriter) AcceptAdmin(
@@ -24,16 +26,29 @@ func (m *mockAcceptAdminWriter) AcceptAdmin(
2426
) (*gethtypes.Receipt, error) {
2527
return m.acceptAdminFunc(ctx, request)
2628
}
29+
func (m *mockAcceptAdminWriter) NewAcceptAdminTx(
30+
txOpts *bind.TransactOpts,
31+
request elcontracts.AcceptAdminRequest,
32+
) (*gethtypes.Transaction, error) {
33+
if m.newAcceptAdminTxFunc == nil {
34+
return nil, errors.New("newAcceptAdminTxFunc not implemented")
35+
}
36+
return m.newAcceptAdminTxFunc(txOpts, request)
37+
}
2738

2839
func generateMockAcceptAdminWriter(
2940
receipt *gethtypes.Receipt,
41+
tx *gethtypes.Transaction,
3042
err error,
3143
) func(logging.Logger, *acceptAdminConfig) (AcceptAdminWriter, error) {
3244
return func(logger logging.Logger, config *acceptAdminConfig) (AcceptAdminWriter, error) {
3345
return &mockAcceptAdminWriter{
3446
acceptAdminFunc: func(ctx context.Context, request elcontracts.AcceptAdminRequest) (*gethtypes.Receipt, error) {
3547
return receipt, err
3648
},
49+
newAcceptAdminTxFunc: func(txOpts *bind.TransactOpts, request elcontracts.AcceptAdminRequest) (*gethtypes.Transaction, error) {
50+
return tx, err
51+
},
3752
}, nil
3853
}
3954
}
@@ -42,10 +57,11 @@ func TestAcceptCmd_Success(t *testing.T) {
4257
mockReceipt := &gethtypes.Receipt{
4358
TxHash: gethcommon.HexToHash("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"),
4459
}
60+
mockTx := &gethtypes.Transaction{}
4561

4662
app := cli.NewApp()
4763
app.Commands = []*cli.Command{
48-
AcceptCmd(generateMockAcceptAdminWriter(mockReceipt, nil)),
64+
AcceptCmd(generateMockAcceptAdminWriter(mockReceipt, mockTx, nil)),
4965
}
5066

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

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

87104
func TestAcceptCmd_AcceptAdminError(t *testing.T) {
88105
expectedError := "error accepting admin"
106+
mockTx := &gethtypes.Transaction{}
107+
89108
app := cli.NewApp()
90109
app.Commands = []*cli.Command{
91-
AcceptCmd(generateMockAcceptAdminWriter(nil, errors.New(expectedError))),
110+
AcceptCmd(generateMockAcceptAdminWriter(nil, mockTx, errors.New(expectedError))),
92111
}
93112

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

103123
err := app.Run(args)

pkg/user/admin/add_pending.go

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package admin
22

33
import (
44
"context"
5+
"fmt"
56
"sort"
67

78
"github.com/Layr-Labs/eigenlayer-cli/pkg/internal/common"
@@ -11,6 +12,7 @@ import (
1112
"github.com/Layr-Labs/eigensdk-go/chainio/clients/elcontracts"
1213
"github.com/Layr-Labs/eigensdk-go/logging"
1314
eigenSdkUtils "github.com/Layr-Labs/eigensdk-go/utils"
15+
"github.com/ethereum/go-ethereum/accounts/abi/bind"
1416
gethcommon "github.com/ethereum/go-ethereum/common"
1517
gethtypes "github.com/ethereum/go-ethereum/core/types"
1618
"github.com/ethereum/go-ethereum/ethclient"
@@ -23,6 +25,10 @@ type AddPendingAdminWriter interface {
2325
ctx context.Context,
2426
request elcontracts.AddPendingAdminRequest,
2527
) (*gethtypes.Receipt, error)
28+
NewAddPendingAdminTx(
29+
txOpts *bind.TransactOpts,
30+
request elcontracts.AddPendingAdminRequest,
31+
) (*gethtypes.Transaction, error)
2632
}
2733

2834
func AddPendingCmd(generator func(logging.Logger, *addPendingAdminConfig) (AddPendingAdminWriter, error)) *cli.Command {
@@ -56,13 +62,77 @@ func addPendingAdmin(
5662
return err
5763
}
5864

65+
addPendingRequest := elcontracts.AddPendingAdminRequest{
66+
AccountAddress: config.AccountAddress,
67+
AdminAddress: config.AdminAddress,
68+
WaitForReceipt: true,
69+
}
70+
71+
if config.Broadcast {
72+
return broadcastAddPendingAdminTx(ctx, elWriter, config, addPendingRequest)
73+
}
74+
return printAddPendingAdminTx(logger, elWriter, config, addPendingRequest)
75+
}
76+
77+
func printAddPendingAdminTx(
78+
logger logging.Logger,
79+
elWriter AddPendingAdminWriter,
80+
config *addPendingAdminConfig,
81+
request elcontracts.AddPendingAdminRequest,
82+
) error {
83+
ethClient, err := ethclient.Dial(config.RPCUrl)
84+
if err != nil {
85+
return err
86+
}
87+
noSendTxOpts := common.GetNoSendTxOpts(config.CallerAddress)
88+
if common.IsSmartContractAddress(config.CallerAddress, ethClient) {
89+
// address is a smart contract
90+
noSendTxOpts.GasLimit = 150_000
91+
}
92+
93+
unsignedTx, err := elWriter.NewAddPendingAdminTx(noSendTxOpts, request)
94+
if err != nil {
95+
return eigenSdkUtils.WrapError("failed to create unsigned tx", err)
96+
}
97+
98+
if config.OutputType == string(common.OutputType_Calldata) {
99+
calldataHex := gethcommon.Bytes2Hex(unsignedTx.Data())
100+
if !common.IsEmptyString(config.OutputFile) {
101+
err = common.WriteToFile([]byte(calldataHex), config.OutputFile)
102+
if err != nil {
103+
return err
104+
}
105+
logger.Infof("Call data written to file: %s", config.OutputFile)
106+
} else {
107+
fmt.Println(calldataHex)
108+
}
109+
} else {
110+
if !common.IsEmptyString(config.OutputType) {
111+
fmt.Println("output file not supported for pretty output type")
112+
fmt.Println()
113+
}
114+
fmt.Printf(
115+
"Admin %s will be added as pending for account %s\n",
116+
config.AdminAddress,
117+
config.AccountAddress,
118+
)
119+
}
120+
txFeeDetails := common.GetTxFeeDetails(unsignedTx)
121+
fmt.Println()
122+
txFeeDetails.Print()
123+
fmt.Println("To broadcast the transaction, use the --broadcast flag")
124+
return nil
125+
}
126+
127+
func broadcastAddPendingAdminTx(
128+
ctx context.Context,
129+
elWriter AddPendingAdminWriter,
130+
config *addPendingAdminConfig,
131+
request elcontracts.AddPendingAdminRequest,
132+
) error {
59133
receipt, err := elWriter.AddPendingAdmin(
60134
ctx,
61-
elcontracts.AddPendingAdminRequest{
62-
AccountAddress: config.AccountAddress,
63-
AdminAddress: config.AdminAddress,
64-
WaitForReceipt: true,
65-
},
135+
request,
66136
)
67137
if err != nil {
68138
return err
@@ -81,6 +151,9 @@ func readAndValidateAddPendingAdminConfig(
81151
ethRpcUrl := cliContext.String(flags.ETHRpcUrlFlag.Name)
82152
network := cliContext.String(flags.NetworkFlag.Name)
83153
environment := cliContext.String(flags.EnvironmentFlag.Name)
154+
outputType := cliContext.String(flags.OutputTypeFlag.Name)
155+
outputFile := cliContext.String(flags.OutputFileFlag.Name)
156+
broadcast := cliContext.Bool(flags.BroadcastFlag.Name)
84157
if environment == "" {
85158
environment = common.GetEnvFromNetwork(network)
86159
}
@@ -126,6 +199,9 @@ func readAndValidateAddPendingAdminConfig(
126199
PermissionManagerAddress: gethcommon.HexToAddress(permissionManagerAddress),
127200
ChainID: chainID,
128201
Environment: environment,
202+
OutputFile: outputFile,
203+
OutputType: outputType,
204+
Broadcast: broadcast,
129205
}, nil
130206
}
131207

0 commit comments

Comments
 (0)