status-go/services/connector/api.go

158 lines
4.0 KiB
Go

package connector
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/status-im/status-go/services/connector/commands"
persistence "github.com/status-im/status-go/services/connector/database"
)
var (
ErrInvalidResponseFromForwardedRpc = errors.New("invalid response from forwarded RPC")
)
type API struct {
s *Service
r *CommandRegistry
c *commands.ClientSideHandler
}
func NewAPI(s *Service) *API {
r := NewCommandRegistry()
c := commands.NewClientSideHandler(s.db)
// Transactions and signing
r.Register("eth_sendTransaction", &commands.SendTransactionCommand{
RpcClient: s.rpc,
Db: s.db,
ClientHandler: c,
})
r.Register("personal_sign", &commands.SignCommand{
Db: s.db,
ClientHandler: c,
})
r.Register("eth_signTypedData_v4", &commands.SignCommand{
Db: s.db,
ClientHandler: c,
})
// Accounts query and dapp permissions
// NOTE: Some dApps expect same behavior for both eth_accounts and eth_requestAccounts
accountsCommand := &commands.RequestAccountsCommand{
ClientHandler: c,
Db: s.db,
}
r.Register("eth_accounts", accountsCommand)
r.Register("eth_requestAccounts", accountsCommand)
// Active chain per dapp management
r.Register("eth_chainId", &commands.ChainIDCommand{
Db: s.db,
NetworkManager: s.nm,
})
r.Register("wallet_switchEthereumChain", &commands.SwitchEthereumChainCommand{
Db: s.db,
NetworkManager: s.nm,
})
// Permissions
r.Register("wallet_requestPermissions", &commands.RequestPermissionsCommand{})
r.Register("wallet_revokePermissions", &commands.RevokePermissionsCommand{
Db: s.db,
})
return &API{
s: s,
r: r,
c: c,
}
}
func (api *API) forwardRPC(URL string, request commands.RPCRequest) (interface{}, error) {
dApp, err := persistence.SelectDAppByUrl(api.s.db, URL)
if err != nil {
return "", err
}
if dApp == nil {
return "", commands.ErrDAppIsNotPermittedByUser
}
if request.ChainID != dApp.ChainID {
request.ChainID = dApp.ChainID
}
var response map[string]interface{}
byteRequest, err := json.Marshal(request)
if err != nil {
return "", err
}
rawResponse := api.s.rpc.CallRaw(string(byteRequest))
if err := json.Unmarshal([]byte(rawResponse), &response); err != nil {
return "", err
}
if errorField, ok := response["error"]; ok {
errorMap, _ := errorField.(map[string]interface{})
errorCode, _ := errorMap["code"].(float64)
errorMessage, _ := errorMap["message"].(string)
return nil, fmt.Errorf("error code %v: %s", errorCode, errorMessage)
}
if result, ok := response["result"]; ok {
return result, nil
}
return nil, ErrInvalidResponseFromForwardedRpc
}
func (api *API) CallRPC(ctx context.Context, inputJSON string) (interface{}, error) {
request, err := commands.RPCRequestFromJSON(inputJSON)
if err != nil {
return "", err
}
if command, exists := api.r.GetCommand(request.Method); exists {
return command.Execute(ctx, request)
}
return api.forwardRPC(request.URL, request)
}
func (api *API) RecallDAppPermission(origin string) error {
// TODO: close the websocket connection
return api.c.RecallDAppPermissions(commands.RecallDAppPermissionsArgs{URL: origin})
}
func (api *API) GetPermittedDAppsList() ([]persistence.DApp, error) {
return persistence.SelectAllDApps(api.s.db)
}
func (api *API) RequestAccountsAccepted(args commands.RequestAccountsAcceptedArgs) error {
return api.c.RequestAccountsAccepted(args)
}
func (api *API) RequestAccountsRejected(args commands.RejectedArgs) error {
return api.c.RequestAccountsRejected(args)
}
func (api *API) SendTransactionAccepted(args commands.SendTransactionAcceptedArgs) error {
return api.c.SendTransactionAccepted(args)
}
func (api *API) SendTransactionRejected(args commands.RejectedArgs) error {
return api.c.SendTransactionRejected(args)
}
func (api *API) SignAccepted(args commands.SignAcceptedArgs) error {
return api.c.SignAccepted(args)
}
func (api *API) SignRejected(args commands.RejectedArgs) error {
return api.c.SignRejected(args)
}