From e4148e76d97b9c940d733543c02afd2af6f3ce05 Mon Sep 17 00:00:00 2001 From: Damian Ramirez Date: Mon, 3 Feb 2025 11:08:25 -0300 Subject: [PATCH 1/2] test: `avsregistry` add tests for subscriber (#456) --- .../clients/avsregistry/subscriber_test.go | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 chainio/clients/avsregistry/subscriber_test.go diff --git a/chainio/clients/avsregistry/subscriber_test.go b/chainio/clients/avsregistry/subscriber_test.go new file mode 100644 index 000000000..0cdf2aaaf --- /dev/null +++ b/chainio/clients/avsregistry/subscriber_test.go @@ -0,0 +1,76 @@ +package avsregistry_test + +import ( + "context" + "testing" + "time" + + "github.com/Layr-Labs/eigensdk-go/crypto/bls" + "github.com/Layr-Labs/eigensdk-go/testutils" + "github.com/Layr-Labs/eigensdk-go/testutils/testclients" + "github.com/Layr-Labs/eigensdk-go/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSubscriberAvsRegistry(t *testing.T) { + client, _ := testclients.BuildTestClients(t) + chainSubscriber := client.AvsRegistryChainSubscriber + chainWriter := client.AvsRegistryChainWriter + + t.Run("subscribe to new pubkey registrations", func(t *testing.T) { + pubKeyRegistrationsC, event, err := chainSubscriber.SubscribeToNewPubkeyRegistrations() + require.NoError(t, err) + defer event.Unsubscribe() + + // Emit a NewPubkeyRegistration event creating a new operator + keypair, err := bls.NewKeyPairFromString("0x01") + require.NoError(t, err) + + ecdsaPrivateKey, err := crypto.HexToECDSA(testutils.ANVIL_FIRST_PRIVATE_KEY) + require.NoError(t, err) + + quorumNumbers := types.QuorumNums{0} + + receipt, err := chainWriter.RegisterOperator( + context.Background(), + ecdsaPrivateKey, + keypair, + quorumNumbers, + "", + true, + ) + require.NoError(t, err) + require.NotNil(t, receipt) + + select { + case newPubkeyRegistration := <-pubKeyRegistrationsC: + expectedOperator := crypto.PubkeyToAddress(ecdsaPrivateKey.PublicKey) + assert.Equal(t, expectedOperator, newPubkeyRegistration.Operator) + case <-time.After(10 * time.Second): + // Throw an error if the event is not received within 10 seconds, making the test fail + t.Fatal("Timed out waiting for NewPubkeyRegistration event") + } + }) + + t.Run("subscribe to operator socket updates", func(t *testing.T) { + socketC, event, err := chainSubscriber.SubscribeToOperatorSocketUpdates() + require.NoError(t, err) + defer event.Unsubscribe() + + // Emit a SocketUpdate event + socketUpdate := "socket-update" + receipt, err := chainWriter.UpdateSocket(context.Background(), types.Socket(socketUpdate), true) + require.NoError(t, err) + require.NotNil(t, receipt) + + select { + case operatorSocketUpdate := <-socketC: + assert.Equal(t, socketUpdate, operatorSocketUpdate.Socket) + case <-time.After(10 * time.Second): + // Throw an error if the event is not received within 10 seconds, making the test fail + t.Fatal("Timed out waiting for OperatorSocketUpdate event") + } + }) +} From 48a3026102ad3ac26ca50536aace57217aa75baa Mon Sep 17 00:00:00 2001 From: Mateo Rico <89949621+ricomateo@users.noreply.github.com> Date: Mon, 3 Feb 2025 12:16:34 -0300 Subject: [PATCH 2/2] docs: `elcontracts/writer` (#498) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tomás Grüner <47506558+MegaRedHand@users.noreply.github.com> --- chainio/clients/elcontracts/writer.go | 65 +++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/chainio/clients/elcontracts/writer.go b/chainio/clients/elcontracts/writer.go index fc95ee6c9..ed34e2e22 100644 --- a/chainio/clients/elcontracts/writer.go +++ b/chainio/clients/elcontracts/writer.go @@ -37,6 +37,8 @@ type Reader interface { ) (*strategy.ContractIStrategy, erc20.ContractIERC20Methods, gethcommon.Address, error) } +// The ChainWriter provides methods to call the +// EigenLayer core contract's state-changing functions. type ChainWriter struct { delegationManager *delegationmanager.ContractDelegationManager strategyManager *strategymanager.ContractStrategyManager @@ -51,6 +53,7 @@ type ChainWriter struct { txMgr txmgr.TxManager } +// Returns a new instance of ChainWriter. func NewChainWriter( delegationManager *delegationmanager.ContractDelegationManager, strategyManager *strategymanager.ContractStrategyManager, @@ -82,6 +85,7 @@ func NewChainWriter( } } +// Returns a new instance of ChainWriter from a given config. func NewWriterFromConfig( cfg Config, ethClient eth.HttpBackend, @@ -123,6 +127,8 @@ func NewWriterFromConfig( ), nil } +// Registers the caller as an operator in EigenLayer through the +// DelegationManager contract. func (w *ChainWriter) RegisterAsOperator( ctx context.Context, operator types.Operator, @@ -156,6 +162,9 @@ func (w *ChainWriter) RegisterAsOperator( return receipt, nil } +// Updates an operator's stored `delegationApprover` with +// the given `operator.DelegationApproverAddress` by calling +// the `modifyOperatorDetails` function in the DelegationManager contract. func (w *ChainWriter) UpdateOperatorDetails( ctx context.Context, operator types.Operator, @@ -195,6 +204,7 @@ func (w *ChainWriter) UpdateOperatorDetails( return receipt, nil } +// Updates the metadata URI for the given operator. func (w *ChainWriter) UpdateMetadataURI( ctx context.Context, operatorAddress gethcommon.Address, @@ -227,6 +237,8 @@ func (w *ChainWriter) UpdateMetadataURI( return receipt, nil } +// Deposits `amount` of the `strategyAddr` underlying token +// into the strategy given by `strategyAddr`. func (w *ChainWriter) DepositERC20IntoStrategy( ctx context.Context, strategyAddr gethcommon.Address, @@ -272,6 +284,9 @@ func (w *ChainWriter) DepositERC20IntoStrategy( return receipt, nil } +// Sets `claimer` as the claimer for the earner (in this case the +// earner is the caller). That means that `claimer` can call `processClaim` +// on behalf of the earner. func (w *ChainWriter) SetClaimerFor( ctx context.Context, claimer gethcommon.Address, @@ -298,6 +313,8 @@ func (w *ChainWriter) SetClaimerFor( return receipt, nil } +// Processes the given `claim` for rewards. +// The rewards are transferred to the given `recipientAddress`. func (w *ChainWriter) ProcessClaim( ctx context.Context, claim rewardscoordinator.IRewardsCoordinatorTypesRewardsMerkleClaim, @@ -325,6 +342,10 @@ func (w *ChainWriter) ProcessClaim( return receipt, nil } +// Sets the split for a specific operator for a specific AVS. +// The caller must be a registered operator. +// The split has to be between 0 and 10000 bips (inclusive). +// The split will be activated after activation delay. func (w *ChainWriter) SetOperatorAVSSplit( ctx context.Context, operator gethcommon.Address, @@ -353,6 +374,10 @@ func (w *ChainWriter) SetOperatorAVSSplit( return receipt, nil } +// Sets the split for a specific operator for Programmatic Incentives. +// The caller must be a registered operator. +// The split has to be between 0 and 10000 bips (inclusive). +// The split will be activated after activation delay. func (w *ChainWriter) SetOperatorPISplit( ctx context.Context, operator gethcommon.Address, @@ -380,6 +405,8 @@ func (w *ChainWriter) SetOperatorPISplit( return receipt, nil } +// Processes the claims given by `claims`. +// The rewards are transferred to the given `recipientAddress`. func (w *ChainWriter) ProcessClaims( ctx context.Context, claims []rewardscoordinator.IRewardsCoordinatorTypesRewardsMerkleClaim, @@ -411,6 +438,9 @@ func (w *ChainWriter) ProcessClaims( return receipt, nil } +// Deregisters an operator from each of the operator sets given by +// `operatorSetIds` for the given AVS, by calling the function +// `deregisterFromOperatorSets` in the AllocationManager. func (w *ChainWriter) ForceDeregisterFromOperatorSets( ctx context.Context, operator gethcommon.Address, @@ -448,6 +478,8 @@ func (w *ChainWriter) ForceDeregisterFromOperatorSets( return receipt, nil } +// Modifies the proportions of slashable stake allocated to an operator set +// from a list of strategies, for the given `operatorAddress`. func (w *ChainWriter) ModifyAllocations( ctx context.Context, operatorAddress gethcommon.Address, @@ -476,6 +508,10 @@ func (w *ChainWriter) ModifyAllocations( return receipt, nil } +// Sets the allocation delay for an operator. +// The allocation delay is the number of blocks between the operator +// allocating a magnitude to an operator set, and the magnitude becoming +// slashable. func (w *ChainWriter) SetAllocationDelay( ctx context.Context, operatorAddress gethcommon.Address, @@ -503,6 +539,9 @@ func (w *ChainWriter) SetAllocationDelay( return receipt, nil } +// Deregister an operator from one or more of the AVS's operator sets. +// If the operator has any slashable stake allocated to the AVS, +// it remains slashable until the deallocation delay has passed. func (w *ChainWriter) DeregisterFromOperatorSets( ctx context.Context, operator gethcommon.Address, @@ -536,6 +575,9 @@ func (w *ChainWriter) DeregisterFromOperatorSets( return receipt, nil } +// Register an operator for one or more operator sets for an AVS. +// If the operator has any stake allocated to these operator sets, +// it immediately becomes slashable. func (w *ChainWriter) RegisterForOperatorSets( ctx context.Context, registryCoordinatorAddr gethcommon.Address, @@ -584,6 +626,8 @@ func (w *ChainWriter) RegisterForOperatorSets( return receipt, nil } +// Removes permission of an appointee for a specific function +// (given by request.selector) on a target contract, given an account address. func (w *ChainWriter) RemovePermission( ctx context.Context, request RemovePermissionRequest, @@ -599,6 +643,7 @@ func (w *ChainWriter) RemovePermission( return w.txMgr.Send(ctx, tx, request.WaitForReceipt) } +// Builds a transaction for the PermissionController's removeAppointee function. func (w *ChainWriter) NewRemovePermissionTx( txOpts *bind.TransactOpts, request RemovePermissionRequest, @@ -616,6 +661,7 @@ func (w *ChainWriter) NewRemovePermissionTx( ) } +// Builds a transaction for the PermissionController's setAppointee function. func (w *ChainWriter) NewSetPermissionTx( txOpts *bind.TransactOpts, request SetPermissionRequest, @@ -632,6 +678,10 @@ func (w *ChainWriter) NewSetPermissionTx( ) } +// Set an appointee for a given account. +// Only the admin of the account can set an appointee. +// The appointee will be able to call the function given +// by `request.Selector` on the contract given by `request.Target`. func (w *ChainWriter) SetPermission( ctx context.Context, request SetPermissionRequest, @@ -649,6 +699,7 @@ func (w *ChainWriter) SetPermission( return w.txMgr.Send(ctx, tx, request.WaitForReceipt) } +// Builds a transaction for the PermissionController's acceptAdmin function. func (w *ChainWriter) NewAcceptAdminTx( txOpts *bind.TransactOpts, request AcceptAdminRequest, @@ -659,6 +710,8 @@ func (w *ChainWriter) NewAcceptAdminTx( return w.permissionController.AcceptAdmin(txOpts, request.AccountAddress) } +// Accept a pending admin for the account given by `request.AccountAddress`. +// The sender of the transaction must be the pending admin. func (w *ChainWriter) AcceptAdmin( ctx context.Context, request AcceptAdminRequest, @@ -675,6 +728,7 @@ func (w *ChainWriter) AcceptAdmin( return w.txMgr.Send(ctx, tx, request.WaitForReceipt) } +// Builds a transaction for the PermissionController's addPendingAdmin function. func (w *ChainWriter) NewAddPendingAdminTx( txOpts *bind.TransactOpts, request AddPendingAdminRequest, @@ -685,6 +739,9 @@ func (w *ChainWriter) NewAddPendingAdminTx( return w.permissionController.AddPendingAdmin(txOpts, request.AccountAddress, request.AdminAddress) } +// Set a pending admin. Multiple admins can be set for an account. +// The caller must be an admin. If the account does not have an admin, +// the caller must be the account. func (w *ChainWriter) AddPendingAdmin(ctx context.Context, request AddPendingAdminRequest) (*gethtypes.Receipt, error) { txOpts, err := w.txMgr.GetNoSendTxOpts() if err != nil { @@ -697,6 +754,7 @@ func (w *ChainWriter) AddPendingAdmin(ctx context.Context, request AddPendingAdm return w.txMgr.Send(ctx, tx, request.WaitForReceipt) } +// Builds a transaction for the PermissionController's removeAdmin function. func (w *ChainWriter) NewRemoveAdminTx( txOpts *bind.TransactOpts, request RemoveAdminRequest, @@ -707,6 +765,8 @@ func (w *ChainWriter) NewRemoveAdminTx( return w.permissionController.RemoveAdmin(txOpts, request.AccountAddress, request.AdminAddress) } +// Removes the admin given by `request.AdminAddress` from the account given +// by `request.AccountAddress`. The sender of the transaction must be an admin. func (w *ChainWriter) RemoveAdmin( ctx context.Context, request RemoveAdminRequest, @@ -723,6 +783,7 @@ func (w *ChainWriter) RemoveAdmin( return w.txMgr.Send(ctx, tx, request.WaitForReceipt) } +// Builds a transaction for the PermissionController's removePendingAdmin function. func (w *ChainWriter) NewRemovePendingAdminTx( txOpts *bind.TransactOpts, request RemovePendingAdminRequest, @@ -733,6 +794,8 @@ func (w *ChainWriter) NewRemovePendingAdminTx( return w.permissionController.RemovePendingAdmin(txOpts, request.AccountAddress, request.AdminAddress) } +// Remove pending admin given by `request.AdminAddress` from the account given +// by `request.AccountAddress`. Only the admin of the account can remove a pending admin. func (w *ChainWriter) RemovePendingAdmin( ctx context.Context, request RemovePendingAdminRequest, @@ -750,6 +813,7 @@ func (w *ChainWriter) RemovePendingAdmin( return w.txMgr.Send(ctx, tx, request.WaitForReceipt) } +// Returns the pubkey registration params for the operator given by `operatorAddress`. func getPubkeyRegistrationParams( ethClient bind.ContractBackend, registryCoordinatorAddr, operatorAddress gethcommon.Address, @@ -780,6 +844,7 @@ func getPubkeyRegistrationParams( return &pubkeyRegParams, nil } +// Returns the ABI encoding of the given registration params. func abiEncodeRegistrationParams( socket string, pubkeyRegistrationParams regcoord.IBLSApkRegistryPubkeyRegistrationParams,