diff --git a/access/grpc/client.go b/access/grpc/client.go index 7171ed797..5ca1729aa 100644 --- a/access/grpc/client.go +++ b/access/grpc/client.go @@ -200,6 +200,14 @@ func (c *Client) GetAccountAtBlockHeight(ctx context.Context, address flow.Addre return c.grpc.GetAccountAtBlockHeight(ctx, address, blockHeight) } +func (c *Client) GetAccountBalanceAtLatestBlock(ctx context.Context, address flow.Address) (uint64, error) { + return c.grpc.GetAccountBalanceAtLatestBlock(ctx, address) +} + +func (c *Client) GetAccountBalanceAtBlockHeight(ctx context.Context, address flow.Address, blockHeight uint64) (uint64, error) { + return c.grpc.GetAccountBalanceAtBlockHeight(ctx, address, blockHeight) +} + func (c *Client) ExecuteScriptAtLatestBlock(ctx context.Context, script []byte, arguments []cadence.Value) (cadence.Value, error) { return c.grpc.ExecuteScriptAtLatestBlock(ctx, script, arguments) } diff --git a/access/grpc/grpc.go b/access/grpc/grpc.go index 51df44442..b70a55bed 100644 --- a/access/grpc/grpc.go +++ b/access/grpc/grpc.go @@ -516,6 +516,42 @@ func (c *BaseClient) GetAccountAtBlockHeight( return &account, nil } +func (c *BaseClient) GetAccountBalanceAtLatestBlock( + ctx context.Context, + address flow.Address, + opts ...grpc.CallOption, +) (uint64, error) { + request := &access.GetAccountBalanceAtLatestBlockRequest{ + Address: address.Bytes(), + } + + response, err := c.rpcClient.GetAccountBalanceAtLatestBlock(ctx, request, opts...) + if err != nil { + return 0, newRPCError(err) + } + + return response.GetBalance(), nil +} + +func (c *BaseClient) GetAccountBalanceAtBlockHeight( + ctx context.Context, + address flow.Address, + blockHeight uint64, + opts ...grpc.CallOption, +) (uint64, error) { + request := &access.GetAccountBalanceAtBlockHeightRequest{ + Address: address.Bytes(), + BlockHeight: blockHeight, + } + + response, err := c.rpcClient.GetAccountBalanceAtBlockHeight(ctx, request, opts...) + if err != nil { + return 0, newRPCError(err) + } + + return response.GetBalance(), nil +} + func (c *BaseClient) ExecuteScriptAtLatestBlock( ctx context.Context, script []byte, diff --git a/access/grpc/grpc_test.go b/access/grpc/grpc_test.go index d818f898e..58a7939f6 100644 --- a/access/grpc/grpc_test.go +++ b/access/grpc/grpc_test.go @@ -709,6 +709,73 @@ func TestClient_GetAccountAtBlockHeight(t *testing.T) { })) } +func TestClient_GetAccountBalanceAtLatestBlock(t *testing.T) { + accounts := test.AccountGenerator() + addresses := test.AddressGenerator() + + t.Run("Success", clientTest(func(t *testing.T, ctx context.Context, rpc *mocks.MockRPCClient, c *BaseClient) { + account := accounts.New() + + response := &access.AccountBalanceResponse{ + Balance: account.Balance, + } + + rpc.On("GetAccountBalanceAtLatestBlock", ctx, mock.Anything).Return(response, nil) + + balance, err := c.GetAccountBalanceAtLatestBlock(ctx, account.Address) + require.NoError(t, err) + + assert.Equal(t, account.Balance, balance) + + })) + + t.Run("Not found error", clientTest(func(t *testing.T, ctx context.Context, rpc *mocks.MockRPCClient, c *BaseClient) { + address := addresses.New() + + rpc.On("GetAccountBalanceAtLatestBlock", ctx, mock.Anything). + Return(nil, errNotFound) + + balance, err := c.GetAccountBalanceAtLatestBlock(ctx, address) + assert.Error(t, err) + assert.Equal(t, codes.NotFound, status.Code(err)) + assert.Equal(t, balance, uint64(0)) + })) +} + +func TestClient_GetAccountBalanceAtBlockHeight(t *testing.T) { + accounts := test.AccountGenerator() + addresses := test.AddressGenerator() + blockHeight := uint64(42) + + t.Run("Success", clientTest(func(t *testing.T, ctx context.Context, rpc *mocks.MockRPCClient, c *BaseClient) { + account := accounts.New() + + response := &access.AccountBalanceResponse{ + Balance: account.Balance, + } + + rpc.On("GetAccountBalanceAtBlockHeight", ctx, mock.Anything).Return(response, nil) + + balance, err := c.GetAccountBalanceAtBlockHeight(ctx, account.Address, blockHeight) + require.NoError(t, err) + + assert.Equal(t, account.Balance, balance) + + })) + + t.Run("Not found error", clientTest(func(t *testing.T, ctx context.Context, rpc *mocks.MockRPCClient, c *BaseClient) { + address := addresses.New() + + rpc.On("GetAccountBalanceAtBlockHeight", ctx, mock.Anything). + Return(nil, errNotFound) + + balance, err := c.GetAccountBalanceAtBlockHeight(ctx, address, blockHeight) + assert.Error(t, err) + assert.Equal(t, codes.NotFound, status.Code(err)) + assert.Equal(t, balance, uint64(0)) + })) +} + func TestClient_ExecuteScriptAtLatestBlock(t *testing.T) { t.Run("Success", clientTest(func(t *testing.T, ctx context.Context, rpc *mocks.MockRPCClient, c *BaseClient) { expectedValue := cadence.NewInt(42) diff --git a/access/grpc/mocks/RPCClient.go b/access/grpc/mocks/RPCClient.go index 3d4698561..18de91845 100644 --- a/access/grpc/mocks/RPCClient.go +++ b/access/grpc/mocks/RPCClient.go @@ -197,6 +197,186 @@ func (_m *MockRPCClient) GetAccountAtLatestBlock(ctx context.Context, in *access return r0, r1 } +// GetAccountBalanceAtBlockHeight provides a mock function with given fields: ctx, in, opts +func (_m *MockRPCClient) GetAccountBalanceAtBlockHeight(ctx context.Context, in *access.GetAccountBalanceAtBlockHeightRequest, opts ...grpc.CallOption) (*access.AccountBalanceResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *access.AccountBalanceResponse + if rf, ok := ret.Get(0).(func(context.Context, *access.GetAccountBalanceAtBlockHeightRequest, ...grpc.CallOption) *access.AccountBalanceResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*access.AccountBalanceResponse) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *access.GetAccountBalanceAtBlockHeightRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetAccountBalanceAtLatestBlock provides a mock function with given fields: ctx, in, opts +func (_m *MockRPCClient) GetAccountBalanceAtLatestBlock(ctx context.Context, in *access.GetAccountBalanceAtLatestBlockRequest, opts ...grpc.CallOption) (*access.AccountBalanceResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *access.AccountBalanceResponse + if rf, ok := ret.Get(0).(func(context.Context, *access.GetAccountBalanceAtLatestBlockRequest, ...grpc.CallOption) *access.AccountBalanceResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*access.AccountBalanceResponse) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *access.GetAccountBalanceAtLatestBlockRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetAccountKeyAtBlockHeight provides a mock function with given fields: ctx, in, opts +func (_m *MockRPCClient) GetAccountKeyAtBlockHeight(ctx context.Context, in *access.GetAccountKeyAtBlockHeightRequest, opts ...grpc.CallOption) (*access.AccountKeyResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *access.AccountKeyResponse + if rf, ok := ret.Get(0).(func(context.Context, *access.GetAccountKeyAtBlockHeightRequest, ...grpc.CallOption) *access.AccountKeyResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*access.AccountKeyResponse) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *access.GetAccountKeyAtBlockHeightRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetAccountKeyAtLatestBlock provides a mock function with given fields: ctx, in, opts +func (_m *MockRPCClient) GetAccountKeyAtLatestBlock(ctx context.Context, in *access.GetAccountKeyAtLatestBlockRequest, opts ...grpc.CallOption) (*access.AccountKeyResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *access.AccountKeyResponse + if rf, ok := ret.Get(0).(func(context.Context, *access.GetAccountKeyAtLatestBlockRequest, ...grpc.CallOption) *access.AccountKeyResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*access.AccountKeyResponse) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *access.GetAccountKeyAtLatestBlockRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetAccountKeysAtBlockHeight provides a mock function with given fields: ctx, in, opts +func (_m *MockRPCClient) GetAccountKeysAtBlockHeight(ctx context.Context, in *access.GetAccountKeysAtBlockHeightRequest, opts ...grpc.CallOption) (*access.AccountKeysResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *access.AccountKeysResponse + if rf, ok := ret.Get(0).(func(context.Context, *access.GetAccountKeysAtBlockHeightRequest, ...grpc.CallOption) *access.AccountKeysResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*access.AccountKeysResponse) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *access.GetAccountKeysAtBlockHeightRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetAccountKeysAtLatestBlock provides a mock function with given fields: ctx, in, opts +func (_m *MockRPCClient) GetAccountKeysAtLatestBlock(ctx context.Context, in *access.GetAccountKeysAtLatestBlockRequest, opts ...grpc.CallOption) (*access.AccountKeysResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *access.AccountKeysResponse + if rf, ok := ret.Get(0).(func(context.Context, *access.GetAccountKeysAtLatestBlockRequest, ...grpc.CallOption) *access.AccountKeysResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*access.AccountKeysResponse) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *access.GetAccountKeysAtLatestBlockRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetBlockByHeight provides a mock function with given fields: ctx, in, opts func (_m *MockRPCClient) GetBlockByHeight(ctx context.Context, in *access.GetBlockByHeightRequest, opts ...grpc.CallOption) (*access.BlockResponse, error) { _va := make([]interface{}, len(opts)) @@ -467,6 +647,36 @@ func (_m *MockRPCClient) GetExecutionResultForBlockID(ctx context.Context, in *a return r0, r1 } +// GetFullCollectionByID provides a mock function with given fields: ctx, in, opts +func (_m *MockRPCClient) GetFullCollectionByID(ctx context.Context, in *access.GetFullCollectionByIDRequest, opts ...grpc.CallOption) (*access.FullCollectionResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *access.FullCollectionResponse + if rf, ok := ret.Get(0).(func(context.Context, *access.GetFullCollectionByIDRequest, ...grpc.CallOption) *access.FullCollectionResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*access.FullCollectionResponse) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *access.GetFullCollectionByIDRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetLatestBlock provides a mock function with given fields: ctx, in, opts func (_m *MockRPCClient) GetLatestBlock(ctx context.Context, in *access.GetLatestBlockRequest, opts ...grpc.CallOption) (*access.BlockResponse, error) { _va := make([]interface{}, len(opts)) diff --git a/examples/go.mod b/examples/go.mod index b1c8aae8f..6fd2b9c50 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -7,7 +7,7 @@ toolchain go1.22.4 replace github.com/onflow/flow-go-sdk => ../ require ( - github.com/onflow/cadence v1.0.0-preview.38 + github.com/onflow/cadence v1.0.0-preview.52 github.com/onflow/flow-cli/flowkit v1.11.0 github.com/onflow/flow-go-sdk v0.41.17 github.com/spf13/afero v1.11.0 @@ -41,9 +41,9 @@ require ( github.com/logrusorgru/aurora/v4 v4.0.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/onflow/atree v0.7.0-rc.2 // indirect + github.com/onflow/atree v0.8.0-rc.6 // indirect github.com/onflow/crypto v0.25.1 // indirect - github.com/onflow/flow/protobuf/go/flow v0.4.3 // indirect + github.com/onflow/flow/protobuf/go/flow v0.4.7 // indirect github.com/onflow/sdks v0.6.0-preview.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/examples/go.sum b/examples/go.sum index cbdeadaee..1a3ba9dca 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -104,6 +104,7 @@ github.com/onflow/atree v0.6.0/go.mod h1:gBHU0M05qCbv9NN0kijLWMgC47gHVNBIp4KmsVF github.com/onflow/atree v0.6.1-0.20230711151834-86040b30171f/go.mod h1:xvP61FoOs95K7IYdIYRnNcYQGf4nbF/uuJ0tHf4DRuM= github.com/onflow/atree v0.6.1-0.20240429171449-cb486ceb1f9c/go.mod h1:xvP61FoOs95K7IYdIYRnNcYQGf4nbF/uuJ0tHf4DRuM= github.com/onflow/atree v0.7.0-rc.2/go.mod h1:xvP61FoOs95K7IYdIYRnNcYQGf4nbF/uuJ0tHf4DRuM= +github.com/onflow/atree v0.8.0-rc.6/go.mod h1:yccR+LR7xc1Jdic0mrjocbHvUD7lnVvg8/Ct1AA5zBo= github.com/onflow/cadence v0.42.7 h1:Qp9VYX901saO7wPwF/rwV4cMS+0mfWxnm9EqbYElYy4= github.com/onflow/cadence v0.42.7/go.mod h1:raU8va8QRyTa/eUbhej4mbyW2ETePfSaywoo36MddgE= github.com/onflow/cadence v1.0.0-M3/go.mod h1:odXGZZ/wGNA5mwT8bC9v8u8EXACHllB2ABSZK65TGL8= @@ -115,6 +116,7 @@ github.com/onflow/cadence v1.0.0-preview.31/go.mod h1:3LM1VgE9HkJ815whY/F0LYWULw github.com/onflow/cadence v1.0.0-preview.35/go.mod h1:jOwvPSSLTr9TvaKMs7KKiBYMmpdpNNAFxBsjMlrqVD0= github.com/onflow/cadence v1.0.0-preview.36/go.mod h1:jOwvPSSLTr9TvaKMs7KKiBYMmpdpNNAFxBsjMlrqVD0= github.com/onflow/cadence v1.0.0-preview.38/go.mod h1:jOwvPSSLTr9TvaKMs7KKiBYMmpdpNNAFxBsjMlrqVD0= +github.com/onflow/cadence v1.0.0-preview.52/go.mod h1:7wvvecnAZtYOspLOS3Lh+FuAmMeSrXhAWiycC3kQ1UU= github.com/onflow/crypto v0.25.0 h1:BeWbLsh3ZD13Ej+Uky6kg1PL1ZIVBDVX+2MVBNwqddg= github.com/onflow/crypto v0.25.0/go.mod h1:C8FbaX0x8y+FxWjbkHy0Q4EASCDR9bSPWZqlpCLYyVI= github.com/onflow/crypto v0.25.1/go.mod h1:C8FbaX0x8y+FxWjbkHy0Q4EASCDR9bSPWZqlpCLYyVI= @@ -124,6 +126,7 @@ github.com/onflow/flow/protobuf/go/flow v0.3.2-0.20231124194313-106cc495def6 h1: github.com/onflow/flow/protobuf/go/flow v0.3.2-0.20231124194313-106cc495def6/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= github.com/onflow/flow/protobuf/go/flow v0.4.0/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= github.com/onflow/flow/protobuf/go/flow v0.4.3/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= +github.com/onflow/flow/protobuf/go/flow v0.4.7/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= github.com/onflow/sdks v0.5.0 h1:2HCRibwqDaQ1c9oUApnkZtEAhWiNY2GTpRD5+ftdkN8= github.com/onflow/sdks v0.5.0/go.mod h1:F0dj0EyHC55kknLkeD10js4mo14yTdMotnWMslPirrU= github.com/onflow/sdks v0.5.1-0.20230912225508-b35402f12bba/go.mod h1:F0dj0EyHC55kknLkeD10js4mo14yTdMotnWMslPirrU= diff --git a/go.mod b/go.mod index 7ab67be1c..91996ab06 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/kms v1.31.0 github.com/onflow/cadence v1.0.0-preview.52 github.com/onflow/crypto v0.25.1 - github.com/onflow/flow/protobuf/go/flow v0.4.3 + github.com/onflow/flow/protobuf/go/flow v0.4.7 github.com/onflow/go-ethereum v1.13.4 github.com/onflow/sdks v0.6.0-preview.1 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 089bf087a..0ea372414 100644 --- a/go.sum +++ b/go.sum @@ -137,8 +137,8 @@ github.com/onflow/cadence v1.0.0-preview.52 h1:hZ92e6lL2+PQa3C1i5jJh0zZYFdW89+X1 github.com/onflow/cadence v1.0.0-preview.52/go.mod h1:7wvvecnAZtYOspLOS3Lh+FuAmMeSrXhAWiycC3kQ1UU= github.com/onflow/crypto v0.25.1 h1:0txy2PKPMM873JbpxQNbJmuOJtD56bfs48RQfm0ts5A= github.com/onflow/crypto v0.25.1/go.mod h1:C8FbaX0x8y+FxWjbkHy0Q4EASCDR9bSPWZqlpCLYyVI= -github.com/onflow/flow/protobuf/go/flow v0.4.3 h1:gdY7Ftto8dtU+0wI+6ZgW4oE+z0DSDUMIDwVx8mqae8= -github.com/onflow/flow/protobuf/go/flow v0.4.3/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= +github.com/onflow/flow/protobuf/go/flow v0.4.7 h1:iP6DFx4wZ3ETORsyeqzHu7neFT3d1CXF6wdK+AOOjmc= +github.com/onflow/flow/protobuf/go/flow v0.4.7/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= github.com/onflow/go-ethereum v1.13.4 h1:iNO86fm8RbBbhZ87ZulblInqCdHnAQVY8okBrNsTevc= github.com/onflow/go-ethereum v1.13.4/go.mod h1:cE/gEUkAffhwbVmMJYz+t1dAfVNHNwZCgc3BWtZxBGY= github.com/onflow/sdks v0.6.0-preview.1 h1:mb/cUezuqWEP1gFZNAgUI4boBltudv4nlfxke1KBp9k=