feat_: endpoint for getting address details with/without blocking added
`AddressDetails` is added, basically it is the same as `GetAddressDetails`, but does the check for address activity (if has balance) across all chains if the chainIDs list is empty. Setting `timeoutInMilliseconds` param ensures that in case of network congestion or no internet `AddressDetails` will return the response setting `hasActivity` property to `false`.
This commit is contained in:
parent
cc722359b5
commit
1418d40a63
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
@ -28,6 +29,7 @@ import (
|
|||
"github.com/status-im/status-go/services/wallet/currency"
|
||||
"github.com/status-im/status-go/services/wallet/history"
|
||||
"github.com/status-im/status-go/services/wallet/onramp"
|
||||
"github.com/status-im/status-go/services/wallet/requests"
|
||||
"github.com/status-im/status-go/services/wallet/router"
|
||||
"github.com/status-im/status-go/services/wallet/router/pathprocessor"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
|
@ -589,7 +591,66 @@ func (api *API) AddressExists(ctx context.Context, address types.Address) (bool,
|
|||
return api.s.accountsDB.AddressExists(address)
|
||||
}
|
||||
|
||||
// Returns details for the passed address (response doesn't include derivation path)
|
||||
// AddressDetails returns details for passed params (passed address, chains to check, timeout for the call to complete)
|
||||
// if chainIDs is empty, it will use all active chains
|
||||
// if timeout is zero, it will wait until the call completes
|
||||
// response doesn't include derivation path
|
||||
func (api *API) AddressDetails(ctx context.Context, params *requests.AddressDetails) (*DerivedAddress, error) {
|
||||
if err := params.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := &DerivedAddress{
|
||||
Address: common.HexToAddress(params.Address),
|
||||
}
|
||||
addressExists, err := api.s.accountsDB.AddressExists(types.Address(result.Address))
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
result.AlreadyCreated = addressExists
|
||||
|
||||
chainIDs := params.ChainIDs
|
||||
if len(chainIDs) == 0 {
|
||||
activeNetworks, err := api.s.rpcClient.NetworkManager.GetActiveNetworks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chainIDs = wcommon.NetworksToChainIDs(activeNetworks)
|
||||
}
|
||||
|
||||
clients, err := api.s.rpcClient.EthClients(chainIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if params.TimeoutInMilliseconds > 0 {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, time.Duration(params.TimeoutInMilliseconds)*time.Millisecond)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
for _, client := range clients {
|
||||
balance, err := api.s.tokenManager.GetChainBalance(ctx, client, result.Address)
|
||||
if err != nil {
|
||||
if err != nil && errors.Is(err, context.DeadlineExceeded) {
|
||||
return result, nil
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
result.HasActivity = balance.Cmp(big.NewInt(0)) != 0
|
||||
if result.HasActivity {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// @deprecated replaced by AddressDetails
|
||||
// GetAddressDetails returns details for the passed address (response doesn't include derivation path)
|
||||
func (api *API) GetAddressDetails(ctx context.Context, chainID uint64, address string) (*DerivedAddress, error) {
|
||||
result := &DerivedAddress{
|
||||
Address: common.HexToAddress(address),
|
||||
|
|
|
@ -3,15 +3,28 @@ package wallet
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
gomock "go.uber.org/mock/gomock"
|
||||
|
||||
"github.com/status-im/status-go/appdatabase"
|
||||
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/services/wallet/onramp"
|
||||
mock_onramp "github.com/status-im/status-go/services/wallet/onramp/mock"
|
||||
"github.com/status-im/status-go/services/wallet/requests"
|
||||
"github.com/status-im/status-go/services/wallet/walletconnect"
|
||||
"github.com/status-im/status-go/t/helpers"
|
||||
"github.com/status-im/status-go/walletdatabase"
|
||||
)
|
||||
|
||||
// TestAPI_GetWalletConnectActiveSessions tames coverage
|
||||
|
@ -90,3 +103,94 @@ func TestAPI_GetCryptoOnRamps(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.Equal(t, "url", url)
|
||||
}
|
||||
|
||||
func TestAPI_GetAddressDetails(t *testing.T) {
|
||||
appDB, err := helpers.SetupTestMemorySQLDB(appdatabase.DbInitializer{})
|
||||
require.NoError(t, err)
|
||||
defer appDB.Close()
|
||||
|
||||
accountsDb, err := accounts.NewDB(appDB)
|
||||
require.NoError(t, err)
|
||||
defer accountsDb.Close()
|
||||
|
||||
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
||||
require.NoError(t, err)
|
||||
defer db.Close()
|
||||
|
||||
accountFeed := &event.Feed{}
|
||||
|
||||
chainID := uint64(1)
|
||||
address := "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
|
||||
providerConfig := params.ProviderConfig{
|
||||
Enabled: true,
|
||||
Name: rpc.ProviderStatusProxy,
|
||||
User: "user1",
|
||||
Password: "pass1",
|
||||
}
|
||||
providerConfigs := []params.ProviderConfig{providerConfig}
|
||||
|
||||
// Create a new server that delays the response by 1 second
|
||||
serverWith1SecDelay := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Fprintln(w, `{"result": "0x10"}`)
|
||||
}))
|
||||
defer serverWith1SecDelay.Close()
|
||||
|
||||
networks := []params.Network{
|
||||
{
|
||||
ChainID: chainID,
|
||||
DefaultRPCURL: serverWith1SecDelay.URL,
|
||||
DefaultFallbackURL: serverWith1SecDelay.URL,
|
||||
},
|
||||
}
|
||||
c, err := rpc.NewClient(nil, chainID, params.UpstreamRPCConfig{}, networks, appDB, providerConfigs)
|
||||
require.NoError(t, err)
|
||||
|
||||
chainClient, err := c.EthClient(chainID)
|
||||
require.NoError(t, err)
|
||||
chainClient.SetWalletNotifier(func(chainID uint64, message string) {})
|
||||
c.SetWalletNotifier(func(chainID uint64, message string) {})
|
||||
|
||||
service := NewService(db, accountsDb, appDB, c, accountFeed, nil, nil, nil, ¶ms.NodeConfig{}, nil, nil, nil, nil, nil, "")
|
||||
|
||||
api := &API{
|
||||
s: service,
|
||||
}
|
||||
|
||||
// Test getting address details using `GetAddressDetails` call, that always waits for the request to finish
|
||||
details, err := api.GetAddressDetails(context.Background(), 1, address)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, details.HasActivity)
|
||||
|
||||
// empty params
|
||||
details, err = api.AddressDetails(context.Background(), &requests.AddressDetails{})
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, requests.ErrAddresInvalid)
|
||||
require.Nil(t, details)
|
||||
|
||||
// no response longer than the set timeout
|
||||
details, err = api.AddressDetails(context.Background(), &requests.AddressDetails{
|
||||
Address: address,
|
||||
TimeoutInMilliseconds: 500,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, details.HasActivity)
|
||||
|
||||
// timeout longer than the response time
|
||||
details, err = api.AddressDetails(context.Background(), &requests.AddressDetails{
|
||||
Address: address,
|
||||
TimeoutInMilliseconds: 1200,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, details.HasActivity)
|
||||
|
||||
// specific chain and timeout longer than the response time
|
||||
details, err = api.AddressDetails(context.Background(), &requests.AddressDetails{
|
||||
Address: address,
|
||||
ChainIDs: []uint64{chainID},
|
||||
TimeoutInMilliseconds: 1200,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, details.HasActivity)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package requests
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
var ErrAddresInvalid = errors.New("address-details: invalid address")
|
||||
|
||||
type AddressDetails struct {
|
||||
Address string `json:"address"`
|
||||
ChainIDs []uint64 `json:"chainIds"`
|
||||
TimeoutInMilliseconds int64 `json:"timeoutInMilliseconds"`
|
||||
}
|
||||
|
||||
func (a *AddressDetails) Validate() error {
|
||||
if !common.IsHexAddress(a.Address) {
|
||||
return ErrAddresInvalid
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue