Skip to content

Commit

Permalink
Implement the eth_getBalance JSON-RPC endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
m-Peter committed Feb 6, 2024
1 parent c8c43be commit bf48fb2
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 9 deletions.
35 changes: 30 additions & 5 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ var (
)

//go:embed cadence/scripts/bridged_account_call.cdc
var bridgedAccountCall []byte
var BridgedAccountCall []byte

//go:embed cadence/transactions/evm_run.cdc
var evmRunTx []byte
var EVMRunTx []byte

//go:embed cadence/scripts/evm_address_balance.cdc
var EVMAddressBalance []byte

func SupportedAPIs(blockChainAPI *BlockChainAPI) []rpc.API {
return []rpc.API{
Expand Down Expand Up @@ -141,7 +144,7 @@ func (api *BlockChainAPI) SendRawTransaction(
}

tx := flow.NewTransaction().
SetScript(evmRunTx).
SetScript(EVMRunTx).
SetProposalKey(account.Address, accountKey.Index, accountKey.SequenceNumber).
SetReferenceBlockID(block.ID).
SetPayer(account.Address).
Expand Down Expand Up @@ -228,7 +231,29 @@ func (s *BlockChainAPI) GetBalance(
address common.Address,
blockNumberOrHash *rpc.BlockNumberOrHash,
) (*hexutil.Big, error) {
return (*hexutil.Big)(big.NewInt(101)), nil
addressBytes := make([]cadence.Value, 0)
for _, bt := range address.Bytes() {
addressBytes = append(addressBytes, cadence.UInt8(bt))
}
evmAddress := cadence.NewArray(
addressBytes,
).WithType(cadence.NewConstantSizedArrayType(20, cadence.TheUInt8Type))

value, err := s.FlowClient.ExecuteScriptAtLatestBlock(
ctx,
EVMAddressBalance,
[]cadence.Value{evmAddress},
)
if err != nil {
return nil, err
}

balance, ok := value.(cadence.UFix64)
if !ok {
return nil, fmt.Errorf("script doesn't return UFix64 as it should")
}

return (*hexutil.Big)(big.NewInt(int64(balance))), nil
}

// eth_getCode (returns the code for the given address)
Expand Down Expand Up @@ -795,7 +820,7 @@ func (s *BlockChainAPI) Call(

value, err := s.FlowClient.ExecuteScriptAtLatestBlock(
ctx,
bridgedAccountCall,
BridgedAccountCall,
[]cadence.Value{encodedTx, encodedTo},
)
if err != nil {
Expand Down
9 changes: 8 additions & 1 deletion api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,13 @@ func TestBlockChainAPI(t *testing.T) {
})

t.Run("GetBalance", func(t *testing.T) {
mockFlowClient := new(mocks.MockAccessClient)
blockchainAPI = api.NewBlockChainAPI(config, store, mockFlowClient)

result, err := cadence.NewUFix64("1500.0")
require.NoError(t, err)
mockFlowClient.On("ExecuteScriptAtLatestBlock", mock.Anything, mock.Anything, mock.Anything).Return(result, nil)

key, _ := crypto.GenerateKey()
addr := crypto.PubkeyToAddress(key.PublicKey)
balance, err := blockchainAPI.GetBalance(
Expand All @@ -207,7 +214,7 @@ func TestBlockChainAPI(t *testing.T) {
)
require.NoError(t, err)

assert.Equal(t, balance, (*hexutil.Big)(big.NewInt(101)))
assert.Equal(t, balance, (*hexutil.Big)(big.NewInt(150000000000)))
})

t.Run("GetProof", func(t *testing.T) {
Expand Down
8 changes: 8 additions & 0 deletions api/cadence/scripts/evm_address_balance.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import EVM from 0xf8d6e0586b0a20c7

access(all)
fun main(addressBytes: [UInt8; 20]): UFix64 {
let address = EVM.EVMAddress(bytes: addressBytes)

return address.balance().flow
}
1 change: 0 additions & 1 deletion api/fixtures/eth_json_rpc_requests.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params": []}
{"jsonrpc":"2.0","id":1,"method":"eth_syncing","params": []}
{"jsonrpc":"2.0","id":1,"method":"eth_gasPrice","params":[]}
{"jsonrpc":"2.0","id":1,"method":"eth_getBalance","params":["0x407d73d8a49eeb85d32cf465507dd71d507100c1","latest"]}
{"jsonrpc":"2.0","id":1,"method":"eth_getCode","params":["0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b","0x2"]}
{"jsonrpc":"2.0","id":1,"method":"eth_getStorageAt","params":["0x295a70b2de5e3953354a6a8344e616ed314d7251","0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9","latest"]}
{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionCount","params":["0x407d73d8a49eeb85d32cf465507dd71d507100c1","latest"]}
Expand Down
1 change: 0 additions & 1 deletion api/fixtures/eth_json_rpc_responses.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
{"jsonrpc":"2.0","id":1,"result":"0x0"}
{"jsonrpc":"2.0","id":1,"result":false}
{"jsonrpc":"2.0","id":1,"result":"0x1dfd14000"}
{"jsonrpc":"2.0","id":1,"result":"0x65"}
{"jsonrpc":"2.0","id":1,"result":"0x600160008035811a818181146012578301005b601b6001356025565b8060005260206000f25b600060078202905091905056"}
{"jsonrpc":"2.0","id":1,"result":"0x600160008035811a818181146012578301005b601b6001356025565b8060005260206000f25b600060078202905091905056"}
{"jsonrpc":"2.0","id":1,"result":"0x0"}
Expand Down
31 changes: 30 additions & 1 deletion api/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,12 @@ func TestServerJSONRPCOveHTTPHandler(t *testing.T) {
returnValue := cadence.NewArray(
toBytes,
).WithType(cadence.NewVariableSizedArrayType(cadence.TheUInt8Type))
mockFlowClient.On("ExecuteScriptAtLatestBlock", mock.Anything, mock.Anything, mock.Anything).Return(returnValue, nil)
mockFlowClient.On(
"ExecuteScriptAtLatestBlock",
mock.Anything,
api.BridgedAccountCall,
mock.Anything,
).Once().Return(returnValue, nil)

blockchainAPI = api.NewBlockChainAPI(config, store, mockFlowClient)

Expand Down Expand Up @@ -223,6 +228,30 @@ func TestServerJSONRPCOveHTTPHandler(t *testing.T) {

assert.Equal(t, expectedResponse, strings.TrimSuffix(string(content), "\n"))
})

t.Run("eth_getBalance", func(t *testing.T) {
request := `{"jsonrpc":"2.0","id":1,"method":"eth_getBalance","params":["0x407d73d8a49eeb85d32cf465507dd71d507100c1","latest"]}`
expectedResponse := `{"jsonrpc":"2.0","id":1,"result":"0x22ecb25c00"}`

result, err := cadence.NewUFix64("1500.0")
require.NoError(t, err)
mockFlowClient.On(
"ExecuteScriptAtLatestBlock",
mock.Anything,
api.EVMAddressBalance,
mock.Anything,
).Once().Return(result, nil)

resp := rpcRequest(url, request, "origin", "test.com")
defer resp.Body.Close()

content, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}

assert.Equal(t, expectedResponse, strings.TrimSuffix(string(content), "\n"))
})
}

func TestServerJSONRPCOveWebSocketHandler(t *testing.T) {
Expand Down

0 comments on commit bf48fb2

Please sign in to comment.