Skip to content

Commit f8cecdd

Browse files
committed
feat: set and get operator split command (#247)
1 parent 26dee03 commit f8cecdd

File tree

8 files changed

+392
-6
lines changed

8 files changed

+392
-6
lines changed

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ require (
77
github.com/Layr-Labs/eigenlayer-contracts v0.3.2-mainnet-rewards
88
github.com/Layr-Labs/eigenlayer-rewards-proofs v0.2.12
99
github.com/Layr-Labs/eigenpod-proofs-generation v0.0.14-stable.0.20240730152248-5c11a259293e
10-
github.com/Layr-Labs/eigensdk-go v0.1.14-0.20241121204729-7d2cd162ffe8
10+
github.com/Layr-Labs/eigensdk-go v0.1.14-0.20241211225219-79336bf6e886
1111
github.com/blang/semver/v4 v4.0.0
1212
github.com/consensys/gnark-crypto v0.12.1
1313
github.com/ethereum/go-ethereum v1.14.5
@@ -19,6 +19,7 @@ require (
1919
github.com/tyler-smith/go-bip39 v1.1.0
2020
github.com/urfave/cli/v2 v2.27.2
2121
github.com/wagslane/go-password-validator v0.3.0
22+
github.com/wealdtech/go-merkletree/v2 v2.5.2-0.20240302222400-69219c450662
2223
github.com/wk8/go-ordered-map/v2 v2.1.8
2324
go.uber.org/mock v0.4.0
2425
gopkg.in/yaml.v2 v2.4.0
@@ -100,7 +101,6 @@ require (
100101
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
101102
github.com/tklauser/go-sysconf v0.3.12 // indirect
102103
github.com/tklauser/numcpus v0.6.1 // indirect
103-
github.com/wealdtech/go-merkletree/v2 v2.5.2-0.20240302222400-69219c450662 // indirect
104104
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
105105
go.opentelemetry.io/otel v1.24.0 // indirect
106106
go.opentelemetry.io/otel/metric v1.24.0 // indirect

go.sum

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,8 @@ github.com/Layr-Labs/eigenlayer-rewards-proofs v0.2.12 h1:G5Q1SnLmFbEjhOkky3vIHk
1212
github.com/Layr-Labs/eigenlayer-rewards-proofs v0.2.12/go.mod h1:OlJd1QjqEW53wfWG/lJyPCGvrXwWVEjPQsP4TV+gttQ=
1313
github.com/Layr-Labs/eigenpod-proofs-generation v0.0.14-stable.0.20240730152248-5c11a259293e h1:DvW0/kWHV9mZsbH2KOjEHKTSIONNPUj6X05FJvUohy4=
1414
github.com/Layr-Labs/eigenpod-proofs-generation v0.0.14-stable.0.20240730152248-5c11a259293e/go.mod h1:T7tYN8bTdca2pkMnz9G2+ZwXYWw5gWqQUIu4KLgC/vM=
15-
github.com/Layr-Labs/eigensdk-go v0.1.13-0.20241023200243-565bb4438918 h1:Itl141PoMFzq58ZTo4Nu/CyH+x8f4BH6OmBNhZ6Z2/I=
16-
github.com/Layr-Labs/eigensdk-go v0.1.13-0.20241023200243-565bb4438918/go.mod h1:aYdNURUhaqeYOS+Cq12TfSdPbjFfiLaHkxPdR4Exq/s=
17-
github.com/Layr-Labs/eigensdk-go v0.1.14-0.20241121204729-7d2cd162ffe8 h1:6wuVq+Elto+yF7bQ3QYqD2psxGXR3wcJh2koNcUjIQM=
18-
github.com/Layr-Labs/eigensdk-go v0.1.14-0.20241121204729-7d2cd162ffe8/go.mod h1:aYdNURUhaqeYOS+Cq12TfSdPbjFfiLaHkxPdR4Exq/s=
15+
github.com/Layr-Labs/eigensdk-go v0.1.14-0.20241211225219-79336bf6e886 h1:+7AijqdfRXdDc3zvj02Alqsk6Qd3owvlqPYQN1Hc1ME=
16+
github.com/Layr-Labs/eigensdk-go v0.1.14-0.20241211225219-79336bf6e886/go.mod h1:aYdNURUhaqeYOS+Cq12TfSdPbjFfiLaHkxPdR4Exq/s=
1917
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
2018
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
2119
github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8=

pkg/internal/common/flags/general.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,13 @@ var (
9191
Value: 3600,
9292
}
9393

94+
OperatorAddressFlag = cli.StringFlag{
95+
Name: "operator-address",
96+
Aliases: []string{"oa", "operator"},
97+
Usage: "Operator address",
98+
EnvVars: []string{"OPERATOR_ADDRESS"},
99+
}
100+
94101
BatchClaimFile = cli.StringFlag{
95102
Name: "batch-claim-file",
96103
Aliases: []string{"bcf"},

pkg/operator.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ func OperatorCmd(p utils.Prompter) *cli.Command {
1818
operator.UpdateCmd(p),
1919
operator.UpdateMetadataURICmd(p),
2020
operator.GetApprovalCmd(p),
21+
operator.SetOperatorSplitCmd(p),
22+
operator.GetOperatorSplitCmd(p),
2123
},
2224
}
2325

pkg/operator/get_operator_split.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package operator
2+
3+
import (
4+
"sort"
5+
6+
"github.com/Layr-Labs/eigenlayer-cli/pkg/internal/common"
7+
"github.com/Layr-Labs/eigenlayer-cli/pkg/internal/common/flags"
8+
"github.com/Layr-Labs/eigenlayer-cli/pkg/operator/split"
9+
"github.com/Layr-Labs/eigenlayer-cli/pkg/rewards"
10+
"github.com/Layr-Labs/eigenlayer-cli/pkg/telemetry"
11+
"github.com/Layr-Labs/eigenlayer-cli/pkg/utils"
12+
"github.com/Layr-Labs/eigensdk-go/chainio/clients/elcontracts"
13+
"github.com/Layr-Labs/eigensdk-go/logging"
14+
eigenSdkUtils "github.com/Layr-Labs/eigensdk-go/utils"
15+
gethcommon "github.com/ethereum/go-ethereum/common"
16+
"github.com/ethereum/go-ethereum/ethclient"
17+
"github.com/urfave/cli/v2"
18+
)
19+
20+
func GetOperatorSplitCmd(p utils.Prompter) *cli.Command {
21+
var operatorSplitCmd = &cli.Command{
22+
Name: "get-rewards-split",
23+
Usage: "Get operator rewards split",
24+
Action: func(cCtx *cli.Context) error {
25+
return GetOperatorSplit(cCtx)
26+
},
27+
After: telemetry.AfterRunAction(),
28+
Flags: getGetOperatorSplitFlags(),
29+
}
30+
31+
return operatorSplitCmd
32+
}
33+
34+
func getGetOperatorSplitFlags() []cli.Flag {
35+
baseFlags := []cli.Flag{
36+
&flags.NetworkFlag,
37+
&flags.ETHRpcUrlFlag,
38+
&flags.OperatorAddressFlag,
39+
&split.OperatorSplitFlag,
40+
&rewards.RewardsCoordinatorAddressFlag,
41+
&split.AVSAddressFlag,
42+
}
43+
44+
sort.Sort(cli.FlagsByName(baseFlags))
45+
return baseFlags
46+
}
47+
48+
func GetOperatorSplit(cCtx *cli.Context) error {
49+
ctx := cCtx.Context
50+
logger := common.GetLogger(cCtx)
51+
52+
config, err := readAndValidateGetOperatorSplitConfig(cCtx, logger)
53+
if err != nil {
54+
return eigenSdkUtils.WrapError("failed to read and validate operator split config", err)
55+
}
56+
57+
cCtx.App.Metadata["network"] = config.ChainID.String()
58+
59+
ethClient, err := ethclient.Dial(config.RPCUrl)
60+
if err != nil {
61+
return eigenSdkUtils.WrapError("failed to create new eth client", err)
62+
}
63+
64+
elReader, err := elcontracts.NewReaderFromConfig(
65+
elcontracts.Config{
66+
RewardsCoordinatorAddress: config.RewardsCoordinatorAddress,
67+
},
68+
ethClient,
69+
logger,
70+
)
71+
72+
if err != nil {
73+
return eigenSdkUtils.WrapError("failed to get EL writer", err)
74+
}
75+
76+
logger.Infof("Getting operator split...")
77+
78+
split, err := elReader.GetOperatorAVSSplit(ctx, config.OperatorAddress, config.AVSAddress)
79+
80+
if err != nil {
81+
return eigenSdkUtils.WrapError("failed to get operator split", err)
82+
}
83+
84+
logger.Infof("Operator split is %d", split)
85+
86+
return nil
87+
}
88+
89+
func readAndValidateGetOperatorSplitConfig(
90+
cCtx *cli.Context,
91+
logger logging.Logger,
92+
) (*split.GetOperatorAVSSplitConfig, error) {
93+
network := cCtx.String(flags.NetworkFlag.Name)
94+
rpcUrl := cCtx.String(flags.ETHRpcUrlFlag.Name)
95+
96+
rewardsCoordinatorAddress := cCtx.String(rewards.RewardsCoordinatorAddressFlag.Name)
97+
98+
var err error
99+
if common.IsEmptyString(rewardsCoordinatorAddress) {
100+
rewardsCoordinatorAddress, err = common.GetRewardCoordinatorAddress(utils.NetworkNameToChainId(network))
101+
if err != nil {
102+
return nil, err
103+
}
104+
}
105+
logger.Debugf("Using Rewards Coordinator address: %s", rewardsCoordinatorAddress)
106+
107+
operatorAddress := gethcommon.HexToAddress(cCtx.String(flags.OperatorAddressFlag.Name))
108+
logger.Infof("Using operator address: %s", operatorAddress.String())
109+
110+
avsAddress := gethcommon.HexToAddress(cCtx.String(split.AVSAddressFlag.Name))
111+
logger.Infof("Using AVS address: %s", avsAddress.String())
112+
113+
chainID := utils.NetworkNameToChainId(network)
114+
logger.Debugf("Using chain ID: %s", chainID.String())
115+
116+
return &split.GetOperatorAVSSplitConfig{
117+
Network: network,
118+
RPCUrl: rpcUrl,
119+
RewardsCoordinatorAddress: gethcommon.HexToAddress(rewardsCoordinatorAddress),
120+
ChainID: chainID,
121+
OperatorAddress: operatorAddress,
122+
AVSAddress: avsAddress,
123+
}, nil
124+
}

pkg/operator/set_operator_split.go

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
package operator
2+
3+
import (
4+
"fmt"
5+
"sort"
6+
7+
"github.com/Layr-Labs/eigenlayer-cli/pkg/internal/common"
8+
"github.com/Layr-Labs/eigenlayer-cli/pkg/internal/common/flags"
9+
"github.com/Layr-Labs/eigenlayer-cli/pkg/operator/split"
10+
"github.com/Layr-Labs/eigenlayer-cli/pkg/rewards"
11+
"github.com/Layr-Labs/eigenlayer-cli/pkg/telemetry"
12+
"github.com/Layr-Labs/eigenlayer-cli/pkg/utils"
13+
"github.com/Layr-Labs/eigensdk-go/chainio/clients/elcontracts"
14+
"github.com/Layr-Labs/eigensdk-go/logging"
15+
eigenSdkUtils "github.com/Layr-Labs/eigensdk-go/utils"
16+
gethcommon "github.com/ethereum/go-ethereum/common"
17+
"github.com/ethereum/go-ethereum/ethclient"
18+
"github.com/urfave/cli/v2"
19+
)
20+
21+
func SetOperatorSplitCmd(p utils.Prompter) *cli.Command {
22+
var operatorSplitCmd = &cli.Command{
23+
Name: "set-rewards-split",
24+
Usage: "Set operator rewards split",
25+
Action: func(cCtx *cli.Context) error {
26+
return SetOperatorSplit(cCtx, p)
27+
},
28+
After: telemetry.AfterRunAction(),
29+
Flags: getSetOperatorSplitFlags(),
30+
}
31+
32+
return operatorSplitCmd
33+
}
34+
35+
func SetOperatorSplit(cCtx *cli.Context, p utils.Prompter) error {
36+
ctx := cCtx.Context
37+
logger := common.GetLogger(cCtx)
38+
39+
config, err := readAndValidateSetOperatorSplitConfig(cCtx, logger)
40+
if err != nil {
41+
return eigenSdkUtils.WrapError("failed to read and validate operator split config", err)
42+
}
43+
44+
cCtx.App.Metadata["network"] = config.ChainID.String()
45+
46+
ethClient, err := ethclient.Dial(config.RPCUrl)
47+
if err != nil {
48+
return eigenSdkUtils.WrapError("failed to create new eth client", err)
49+
}
50+
51+
if config.Broadcast {
52+
53+
eLWriter, err := common.GetELWriter(
54+
config.OperatorAddress,
55+
config.SignerConfig,
56+
ethClient,
57+
elcontracts.Config{
58+
RewardsCoordinatorAddress: config.RewardsCoordinatorAddress,
59+
},
60+
p,
61+
config.ChainID,
62+
logger,
63+
)
64+
65+
if err != nil {
66+
return eigenSdkUtils.WrapError("failed to get EL writer", err)
67+
}
68+
69+
logger.Infof("Broadcasting set operator transaction...")
70+
71+
receipt, err := eLWriter.SetOperatorAVSSplit(ctx, config.OperatorAddress, config.AVSAddress, config.Split, true)
72+
73+
if err != nil {
74+
return eigenSdkUtils.WrapError("failed to process claim", err)
75+
}
76+
77+
logger.Infof("Set operator transaction submitted successfully")
78+
common.PrintTransactionInfo(receipt.TxHash.String(), config.ChainID)
79+
} else {
80+
noSendTxOpts := common.GetNoSendTxOpts(config.OperatorAddress)
81+
_, _, contractBindings, err := elcontracts.BuildClients(elcontracts.Config{
82+
RewardsCoordinatorAddress: config.RewardsCoordinatorAddress,
83+
}, ethClient, nil, logger, nil)
84+
if err != nil {
85+
return err
86+
}
87+
88+
code, err := ethClient.CodeAt(ctx, config.OperatorAddress, nil)
89+
if err != nil {
90+
return eigenSdkUtils.WrapError("failed to get code at address", err)
91+
}
92+
if len(code) > 0 {
93+
// Operator is a smart contract
94+
noSendTxOpts.GasLimit = 150_000
95+
}
96+
97+
unsignedTx, err := contractBindings.RewardsCoordinator.SetOperatorAVSSplit(noSendTxOpts, config.OperatorAddress, config.AVSAddress, config.Split)
98+
99+
if err != nil {
100+
return eigenSdkUtils.WrapError("failed to create unsigned tx", err)
101+
}
102+
if config.OutputType == string(common.OutputType_Calldata) {
103+
calldataHex := gethcommon.Bytes2Hex(unsignedTx.Data())
104+
105+
if !common.IsEmptyString(config.OutputFile) {
106+
err = common.WriteToFile([]byte(calldataHex), config.OutputFile)
107+
if err != nil {
108+
return err
109+
}
110+
logger.Infof("Call data written to file: %s", config.OutputFile)
111+
} else {
112+
fmt.Println(calldataHex)
113+
}
114+
} else {
115+
logger.Infof("This transaction would set the operator split to %d", config.Split)
116+
}
117+
118+
if !config.IsSilent {
119+
txFeeDetails := common.GetTxFeeDetails(unsignedTx)
120+
fmt.Println()
121+
txFeeDetails.Print()
122+
123+
fmt.Println("To broadcast the operator set split, use the --broadcast flag")
124+
}
125+
}
126+
return nil
127+
}
128+
129+
func getSetOperatorSplitFlags() []cli.Flag {
130+
baseFlags := []cli.Flag{
131+
&flags.NetworkFlag,
132+
&flags.ETHRpcUrlFlag,
133+
&flags.OperatorAddressFlag,
134+
&split.OperatorSplitFlag,
135+
&rewards.RewardsCoordinatorAddressFlag,
136+
&split.AVSAddressFlag,
137+
&flags.BroadcastFlag,
138+
&flags.OutputTypeFlag,
139+
&flags.OutputFileFlag,
140+
&flags.SilentFlag,
141+
}
142+
143+
allFlags := append(baseFlags, flags.GetSignerFlags()...)
144+
sort.Sort(cli.FlagsByName(allFlags))
145+
return allFlags
146+
}
147+
148+
func readAndValidateSetOperatorSplitConfig(
149+
cCtx *cli.Context,
150+
logger logging.Logger,
151+
) (*split.SetOperatorAVSSplitConfig, error) {
152+
network := cCtx.String(flags.NetworkFlag.Name)
153+
rpcUrl := cCtx.String(flags.ETHRpcUrlFlag.Name)
154+
opSplit := cCtx.Int(split.OperatorSplitFlag.Name)
155+
broadcast := cCtx.Bool(flags.BroadcastFlag.Name)
156+
outputType := cCtx.String(flags.OutputTypeFlag.Name)
157+
outputFile := cCtx.String(flags.OutputFileFlag.Name)
158+
isSilent := cCtx.Bool(flags.SilentFlag.Name)
159+
160+
rewardsCoordinatorAddress := cCtx.String(rewards.RewardsCoordinatorAddressFlag.Name)
161+
162+
var err error
163+
if common.IsEmptyString(rewardsCoordinatorAddress) {
164+
rewardsCoordinatorAddress, err = common.GetRewardCoordinatorAddress(utils.NetworkNameToChainId(network))
165+
if err != nil {
166+
return nil, err
167+
}
168+
}
169+
logger.Debugf("Using Rewards Coordinator address: %s", rewardsCoordinatorAddress)
170+
171+
operatorAddress := gethcommon.HexToAddress(cCtx.String(flags.OperatorAddressFlag.Name))
172+
logger.Infof("Using operator address: %s", operatorAddress.String())
173+
174+
avsAddress := gethcommon.HexToAddress(cCtx.String(split.AVSAddressFlag.Name))
175+
logger.Infof("Using AVS address: %s", avsAddress.String())
176+
177+
chainID := utils.NetworkNameToChainId(network)
178+
logger.Debugf("Using chain ID: %s", chainID.String())
179+
180+
// Get SignerConfig
181+
signerConfig, err := common.GetSignerConfig(cCtx, logger)
182+
if err != nil {
183+
// We don't want to throw error since people can still use it to generate the claim
184+
// without broadcasting it
185+
logger.Debugf("Failed to get signer config: %s", err)
186+
}
187+
188+
return &split.SetOperatorAVSSplitConfig{
189+
Network: network,
190+
RPCUrl: rpcUrl,
191+
RewardsCoordinatorAddress: gethcommon.HexToAddress(rewardsCoordinatorAddress),
192+
ChainID: chainID,
193+
SignerConfig: signerConfig,
194+
OperatorAddress: operatorAddress,
195+
AVSAddress: avsAddress,
196+
Split: uint16(opSplit),
197+
Broadcast: broadcast,
198+
OutputType: outputType,
199+
OutputFile: outputFile,
200+
IsSilent: isSilent,
201+
}, nil
202+
}

0 commit comments

Comments
 (0)