diff --git a/chainio/clients/elcontracts/reader.go b/chainio/clients/elcontracts/reader.go index 1f46ab3d..858c8875 100644 --- a/chainio/clients/elcontracts/reader.go +++ b/chainio/clients/elcontracts/reader.go @@ -685,111 +685,147 @@ func (r *ChainReader) GetRegisteredSets( func (r *ChainReader) CanCall( ctx context.Context, - accountAddress gethcommon.Address, - appointeeAddress gethcommon.Address, - target gethcommon.Address, - selector [4]byte, -) (bool, error) { + request CanCallRequest, +) (CanCallResponse, error) { + if r.permissionController == nil { + return CanCallResponse{}, errors.New("PermissionController contract not provided") + } + canCall, err := r.permissionController.CanCall( - &bind.CallOpts{Context: ctx}, - accountAddress, - appointeeAddress, - target, - selector, + &bind.CallOpts{Context: ctx, BlockNumber: request.BlockNumber}, + request.AccountAddress, + request.AppointeeAddress, + request.Target, + request.Selector, ) // This call should not fail since it's a getter if err != nil { - return false, utils.WrapError("call to permission controller failed", err) + return CanCallResponse{}, utils.WrapError("call to permission controller failed", err) } - return canCall, nil + return CanCallResponse{CanCall: canCall}, nil } +// ListAppointees returns the list of appointees of an account func (r *ChainReader) ListAppointees( ctx context.Context, - accountAddress gethcommon.Address, - target gethcommon.Address, - selector [4]byte, -) ([]gethcommon.Address, error) { + request AppointeesListRequest, +) (AppointeesResponse, error) { + if r.permissionController == nil { + return AppointeesResponse{}, errors.New("PermissionController contract not provided") + } + appointees, err := r.permissionController.GetAppointees( - &bind.CallOpts{Context: ctx}, - accountAddress, - target, - selector, + &bind.CallOpts{Context: ctx, BlockNumber: request.BlockNumber}, + request.AccountAddress, + request.TargetAddress, + request.Selector, ) // This call should not fail since it's a getter if err != nil { - return nil, utils.WrapError("call to permission controller failed", err) + return AppointeesResponse{}, utils.WrapError("call to permission controller failed", err) } - return appointees, nil + return AppointeesResponse{Appointees: appointees}, nil } +// ListAppointeePermissions returns the list of permissions of an appointee func (r *ChainReader) ListAppointeePermissions( ctx context.Context, - accountAddress gethcommon.Address, - appointeeAddress gethcommon.Address, -) ([]gethcommon.Address, [][4]byte, error) { + request AppointeePermissionsListRequest, +) (AppointeePermissionsListResponse, error) { + if r.permissionController == nil { + return AppointeePermissionsListResponse{}, errors.New("PermissionController contract not provided") + } + targets, selectors, err := r.permissionController.GetAppointeePermissions( - &bind.CallOpts{Context: ctx}, - accountAddress, - appointeeAddress, + &bind.CallOpts{Context: ctx, BlockNumber: request.BlockNumber}, + request.AccountAddress, + request.AppointeeAddress, ) // This call should not fail since it's a getter if err != nil { - return nil, nil, utils.WrapError("call to permission controller failed", err) + return AppointeePermissionsListResponse{}, utils.WrapError("call to permission controller failed", err) } - return targets, selectors, nil + return AppointeePermissionsListResponse{TargetAddresses: targets, Selectors: selectors}, nil } +// ListPendingAdmins returns the list of pending admins of an account func (r *ChainReader) ListPendingAdmins( ctx context.Context, - accountAddress gethcommon.Address, -) ([]gethcommon.Address, error) { - pendingAdmins, err := r.permissionController.GetPendingAdmins(&bind.CallOpts{Context: ctx}, accountAddress) + request AccountRequest, +) (PendingAdminsResponse, error) { + if r.permissionController == nil { + return PendingAdminsResponse{}, errors.New("PermissionController contract not provided") + } + + pendingAdmins, err := r.permissionController.GetPendingAdmins( + &bind.CallOpts{Context: ctx, BlockNumber: request.BlockNumber}, + request.AccountAddress, + ) // This call should not fail since it's a getter if err != nil { - return nil, utils.WrapError("call to permission controller failed", err) + return PendingAdminsResponse{}, utils.WrapError("call to permission controller failed", err) } - return pendingAdmins, nil + return PendingAdminsResponse{PendingAdmins: pendingAdmins}, nil } +// ListAdmins returns the list of admins of an account func (r *ChainReader) ListAdmins( ctx context.Context, - accountAddress gethcommon.Address, -) ([]gethcommon.Address, error) { - pendingAdmins, err := r.permissionController.GetAdmins(&bind.CallOpts{Context: ctx}, accountAddress) + request AccountRequest, +) (AdminsResponse, error) { + if r.permissionController == nil { + return AdminsResponse{}, errors.New("PermissionController contract not provided") + } + + admins, err := r.permissionController.GetAdmins( + &bind.CallOpts{Context: ctx, BlockNumber: request.BlockNumber}, + request.AccountAddress, + ) // This call should not fail since it's a getter if err != nil { - return nil, utils.WrapError("call to permission controller failed", err) + return AdminsResponse{}, utils.WrapError("call to permission controller failed", err) } - return pendingAdmins, nil + return AdminsResponse{Admins: admins}, nil } +// IsPendingAdmin returns if an address is a pending admin of an account func (r *ChainReader) IsPendingAdmin( ctx context.Context, - accountAddress gethcommon.Address, - pendingAdminAddress gethcommon.Address, -) (bool, error) { + request PendingAdminCheckRequest, +) (IsPendingAdminResponse, error) { + if r.permissionController == nil { + return IsPendingAdminResponse{}, errors.New("PermissionController contract not provided") + } + isPendingAdmin, err := r.permissionController.IsPendingAdmin( - &bind.CallOpts{Context: ctx}, - accountAddress, - pendingAdminAddress, + &bind.CallOpts{Context: ctx, BlockNumber: request.BlockNumber}, + request.AccountAddress, + request.PendingAdminAddress, ) // This call should not fail since it's a getter if err != nil { - return false, utils.WrapError("call to permission controller failed", err) + return IsPendingAdminResponse{}, utils.WrapError("call to permission controller failed", err) } - return isPendingAdmin, nil + return IsPendingAdminResponse{IsPendingAdmin: isPendingAdmin}, nil } +// IsAdmin returns if an address is an admin of an account func (r *ChainReader) IsAdmin( ctx context.Context, - accountAddress gethcommon.Address, - adminAddress gethcommon.Address, -) (bool, error) { - isAdmin, err := r.permissionController.IsAdmin(&bind.CallOpts{Context: ctx}, accountAddress, adminAddress) + request AdminCheckRequest, +) (IsAdminResponse, error) { + if r.permissionController == nil { + return IsAdminResponse{}, errors.New("PermissionController contract not provided") + } + + isAdmin, err := r.permissionController.IsAdmin( + &bind.CallOpts{Context: ctx, BlockNumber: request.BlockNumber}, + request.AccountAddress, + request.AdminAddress, + ) // This call should not fail since it's a getter if err != nil { - return false, utils.WrapError("call to permission controller failed", err) + return IsAdminResponse{}, utils.WrapError("call to permission controller failed", err) } - return isAdmin, nil + return IsAdminResponse{IsAdmin: isAdmin}, nil } diff --git a/chainio/clients/elcontracts/reader_test.go b/chainio/clients/elcontracts/reader_test.go index 9244c698..5db62aa8 100644 --- a/chainio/clients/elcontracts/reader_test.go +++ b/chainio/clients/elcontracts/reader_test.go @@ -624,15 +624,22 @@ func TestAdminFunctions(t *testing.T) { assert.NoError(t, err) t.Run("non-existent pending admin", func(t *testing.T) { - isPendingAdmin, err := chainReader.IsPendingAdmin(context.Background(), operatorAddr, pendingAdminAddr) + pendingAdminRequest := elcontracts.PendingAdminCheckRequest{ + AccountAddress: operatorAddr, + PendingAdminAddress: pendingAdminAddr, + } + response, err := chainReader.IsPendingAdmin(context.Background(), pendingAdminRequest) assert.NoError(t, err) - assert.False(t, isPendingAdmin) + assert.False(t, response.IsPendingAdmin) }) t.Run("list pending admins when empty", func(t *testing.T) { - listPendingAdmins, err := chainReader.ListPendingAdmins(context.Background(), operatorAddr) + accountRequest := elcontracts.AccountRequest{ + AccountAddress: operatorAddr, + } + response, err := chainReader.ListPendingAdmins(context.Background(), accountRequest) assert.NoError(t, err) - assert.Empty(t, listPendingAdmins) + assert.Empty(t, response.PendingAdmins) }) t.Run("add pending admin and list", func(t *testing.T) { @@ -646,19 +653,30 @@ func TestAdminFunctions(t *testing.T) { assert.NoError(t, err) assert.Equal(t, receipt.Status, gethtypes.ReceiptStatusSuccessful) - isPendingAdmin, err := chainReader.IsPendingAdmin(context.Background(), operatorAddr, pendingAdminAddr) + pendingAdminRequest := elcontracts.PendingAdminCheckRequest{ + AccountAddress: operatorAddr, + PendingAdminAddress: pendingAdminAddr, + } + pendingResponse, err := chainReader.IsPendingAdmin(context.Background(), pendingAdminRequest) assert.NoError(t, err) - assert.True(t, isPendingAdmin) + assert.True(t, pendingResponse.IsPendingAdmin) - listPendingAdmins, err := chainReader.ListPendingAdmins(context.Background(), operatorAddr) + accountRequest := elcontracts.AccountRequest{ + AccountAddress: operatorAddr, + } + pendingAdminResponse, err := chainReader.ListPendingAdmins(context.Background(), accountRequest) assert.NoError(t, err) - assert.NotEmpty(t, listPendingAdmins) + assert.NotEmpty(t, pendingAdminResponse.PendingAdmins) }) t.Run("non-existent admin", func(t *testing.T) { - isAdmin, err := chainReader.IsAdmin(context.Background(), operatorAddr, pendingAdminAddr) + adminRequest := elcontracts.AdminCheckRequest{ + AccountAddress: operatorAddr, + AdminAddress: pendingAdminAddr, + } + response, err := chainReader.IsAdmin(context.Background(), adminRequest) assert.NoError(t, err) - assert.False(t, isAdmin) + assert.False(t, response.IsAdmin) }) t.Run("list admins", func(t *testing.T) { @@ -671,14 +689,20 @@ func TestAdminFunctions(t *testing.T) { assert.NoError(t, err) assert.Equal(t, receipt.Status, gethtypes.ReceiptStatusSuccessful) - listAdmins, err := chainReader.ListAdmins(context.Background(), operatorAddr) + adminsRequest := elcontracts.AccountRequest{ + AccountAddress: operatorAddr, + } + adminListResponse, err := chainReader.ListAdmins(context.Background(), adminsRequest) assert.NoError(t, err) - assert.Len(t, listAdmins, 1) + assert.Len(t, adminListResponse.Admins, 1) - admin := listAdmins[0] - isAdmin, err := chainReader.IsAdmin(context.Background(), operatorAddr, admin) + adminRequest := elcontracts.AdminCheckRequest{ + AccountAddress: operatorAddr, + AdminAddress: adminListResponse.Admins[0], + } + adminResponse, err := chainReader.IsAdmin(context.Background(), adminRequest) assert.NoError(t, err) - assert.True(t, isAdmin) + assert.True(t, adminResponse.IsAdmin) }) } @@ -709,9 +733,14 @@ func TestAppointeesFunctions(t *testing.T) { selector := [4]byte{0, 1, 2, 3} t.Run("list appointees when empty", func(t *testing.T) { - appointees, err := chainReader.ListAppointees(context.Background(), accountAddress, target, selector) + appointeesRequest := elcontracts.AppointeesListRequest{ + AccountAddress: accountAddress, + TargetAddress: target, + Selector: selector, + } + response, err := chainReader.ListAppointees(context.Background(), appointeesRequest) assert.NoError(t, err) - assert.Empty(t, appointees) + assert.Empty(t, response.Appointees) }) t.Run("list appointees", func(t *testing.T) { @@ -727,23 +756,34 @@ func TestAppointeesFunctions(t *testing.T) { require.NoError(t, err) require.Equal(t, receipt.Status, gethtypes.ReceiptStatusSuccessful) - canCall, err := chainReader.CanCall(context.Background(), accountAddress, appointeeAddress, target, selector) + callRequest := elcontracts.CanCallRequest{ + AccountAddress: accountAddress, + AppointeeAddress: appointeeAddress, + Target: target, + Selector: selector, + } + response, err := chainReader.CanCall(context.Background(), callRequest) require.NoError(t, err) - require.True(t, canCall) + require.True(t, response.CanCall) - appointees, err := chainReader.ListAppointees(context.Background(), accountAddress, target, selector) + appointeesRequest := elcontracts.AppointeesListRequest{ + AccountAddress: accountAddress, + TargetAddress: target, + Selector: selector, + } + appointeesResponse, err := chainReader.ListAppointees(context.Background(), appointeesRequest) assert.NoError(t, err) - assert.NotEmpty(t, appointees) + assert.NotEmpty(t, appointeesResponse.Appointees) }) t.Run("list appointees permissions", func(t *testing.T) { - appointeesPermission, _, err := chainReader.ListAppointeePermissions( - context.Background(), - accountAddress, - appointeeAddress, - ) + request := elcontracts.AppointeePermissionsListRequest{ + AccountAddress: accountAddress, + AppointeeAddress: appointeeAddress, + } + response, err := chainReader.ListAppointeePermissions(context.Background(), request) assert.NoError(t, err) - assert.NotEmpty(t, appointeesPermission) + assert.NotEmpty(t, response.TargetAddresses) }) } diff --git a/chainio/clients/elcontracts/types.go b/chainio/clients/elcontracts/types.go index 21dd8ea4..aa93d41f 100644 --- a/chainio/clients/elcontracts/types.go +++ b/chainio/clients/elcontracts/types.go @@ -81,3 +81,89 @@ type RemovePendingAdminRequest struct { AdminAddress common.Address WaitForReceipt bool } + +// IsAdminRequest represents a request to check if the caller is an admin of an account. +// If `BlockNumber` is nil, the latest block will be used +type AdminCheckRequest struct { + BlockNumber *big.Int + AccountAddress common.Address + AdminAddress common.Address +} + +// IsAdminResponse shows whether an address is an admin. +type IsAdminResponse struct { + IsAdmin bool +} + +// IsPendingAdminRequest represents a request to check if an address is a pending admin of the account. +// If `BlockNumber` is nil, the latest block will be used +type PendingAdminCheckRequest struct { + BlockNumber *big.Int + AccountAddress common.Address + PendingAdminAddress common.Address +} + +// IsPendingAdminResponse shows whether an address is a pending admin. +type IsPendingAdminResponse struct { + IsPendingAdmin bool +} + +// AccountRequest represents the address of an specific account. // If `BlockNumber` is nil, the latest block will be +// used +type AccountRequest struct { + BlockNumber *big.Int + AccountAddress common.Address +} + +// AdminsResponse shows the list of admins of an account +type AdminsResponse struct { + Admins []common.Address +} + +// PendingAdminsResponse shows the list of pending admins of an account +type PendingAdminsResponse struct { + PendingAdmins []common.Address +} + +// AppointeesListRequest represents a request to get the list of appointees of an account. +// If `BlockNumber` is nil, the latest block will be used +type AppointeesListRequest struct { + BlockNumber *big.Int + AccountAddress common.Address + TargetAddress common.Address + Selector [4]byte +} + +// AppointeesResponse shows the list of appointees of an account +type AppointeesResponse struct { + Appointees []common.Address +} + +// AppointeePermissionsListRequest represents a request to get the list of permissions of an appointee. +// If `BlockNumber` is nil, the latest block will be used +type AppointeePermissionsListRequest struct { + BlockNumber *big.Int + AccountAddress common.Address + AppointeeAddress common.Address +} + +// AppointeePermissionsListResponse shows the list of permissions of an appointee +type AppointeePermissionsListResponse struct { + TargetAddresses []common.Address + Selectors [][4]byte +} + +// CanCallRequest represents a request to check if an account can call a target. +// If `BlockNumber` is nil, the latest block will be used +type CanCallRequest struct { + BlockNumber *big.Int + AccountAddress common.Address + AppointeeAddress common.Address + Target common.Address + Selector [4]byte +} + +// CanCallResponse shows whether an account can call a target +type CanCallResponse struct { + CanCall bool +} diff --git a/chainio/clients/elcontracts/writer_test.go b/chainio/clients/elcontracts/writer_test.go index ab067949..c18184f2 100644 --- a/chainio/clients/elcontracts/writer_test.go +++ b/chainio/clients/elcontracts/writer_test.go @@ -553,9 +553,15 @@ func TestSetAndRemovePermission(t *testing.T) { require.NoError(t, err) require.Equal(t, gethtypes.ReceiptStatusSuccessful, receipt.Status) - canCall, err := chainReader.CanCall(context.Background(), accountAddress, appointeeAddress, target, selector) + request := elcontracts.CanCallRequest{ + AccountAddress: accountAddress, + AppointeeAddress: appointeeAddress, + Target: target, + Selector: selector, + } + response, err := chainReader.CanCall(context.Background(), request) require.NoError(t, err) - require.True(t, canCall) + require.True(t, response.CanCall) }) t.Run("set permission to account when already set", func(t *testing.T) { @@ -568,9 +574,15 @@ func TestSetAndRemovePermission(t *testing.T) { require.NoError(t, err) require.Equal(t, gethtypes.ReceiptStatusSuccessful, receipt.Status) - canCall, err := chainReader.CanCall(context.Background(), accountAddress, appointeeAddress, target, selector) + request := elcontracts.CanCallRequest{ + AccountAddress: accountAddress, + AppointeeAddress: appointeeAddress, + Target: target, + Selector: selector, + } + response, err := chainReader.CanCall(context.Background(), request) require.NoError(t, err) - require.False(t, canCall) + require.False(t, response.CanCall) }) t.Run("remove permission from account when not set", func(t *testing.T) { @@ -708,9 +720,13 @@ func TestAddAndRemovePendingAdmin(t *testing.T) { require.NoError(t, err) require.Equal(t, gethtypes.ReceiptStatusSuccessful, receipt.Status) - isPendingAdmin, err := chainReader.IsPendingAdmin(context.Background(), operatorAddr, pendingAdmin) + pendingAdminRequest := elcontracts.PendingAdminCheckRequest{ + AccountAddress: operatorAddr, + PendingAdminAddress: pendingAdmin, + } + response, err := chainReader.IsPendingAdmin(context.Background(), pendingAdminRequest) require.NoError(t, err) - require.True(t, isPendingAdmin) + require.True(t, response.IsPendingAdmin) }) t.Run("add pending admin when already added", func(t *testing.T) { @@ -723,9 +739,13 @@ func TestAddAndRemovePendingAdmin(t *testing.T) { require.NoError(t, err) require.Equal(t, gethtypes.ReceiptStatusSuccessful, receipt.Status) - isPendingAdmin, err := chainReader.IsPendingAdmin(context.Background(), operatorAddr, pendingAdmin) + pendingAdminRequest := elcontracts.PendingAdminCheckRequest{ + AccountAddress: operatorAddr, + PendingAdminAddress: pendingAdmin, + } + response, err := chainReader.IsPendingAdmin(context.Background(), pendingAdminRequest) require.NoError(t, err) - require.False(t, isPendingAdmin) + require.False(t, response.IsPendingAdmin) }) } @@ -778,9 +798,13 @@ func TestAcceptAdmin(t *testing.T) { require.NoError(t, err) require.Equal(t, gethtypes.ReceiptStatusSuccessful, receipt.Status) - isAdmin, err := chainReader.IsAdmin(context.Background(), accountAddr, pendingAdminAddr) + adminRequest := elcontracts.AdminCheckRequest{ + AccountAddress: accountAddr, + AdminAddress: pendingAdminAddr, + } + response, err := chainReader.IsAdmin(context.Background(), adminRequest) require.NoError(t, err) - require.True(t, isAdmin) + require.True(t, response.IsAdmin) }) t.Run("accept admin when already accepted", func(t *testing.T) { @@ -869,9 +893,13 @@ func TestRemoveAdmin(t *testing.T) { require.NoError(t, err) require.Equal(t, gethtypes.ReceiptStatusSuccessful, receipt.Status) - isAdmin, err := chainReader.IsAdmin(context.Background(), accountAddr, admin2) + adminRequest := elcontracts.AdminCheckRequest{ + AccountAddress: accountAddr, + AdminAddress: admin2, + } + response, err := chainReader.IsAdmin(context.Background(), adminRequest) require.NoError(t, err) - require.False(t, isAdmin) + require.False(t, response.IsAdmin) }) t.Run("remove admin 2 when already removed", func(t *testing.T) {