feat(wallet) api to retrieve last activity timestamp
Also fix the lookup source for `GetRecipients` API Updates status-desktop #11169
This commit is contained in:
parent
d3e650d5e5
commit
ea7a389075
|
@ -364,7 +364,7 @@ const (
|
||||||
)
|
)
|
||||||
OR (filterActivityTypeReceive
|
OR (filterActivityTypeReceive
|
||||||
AND (filterAllAddresses
|
AND (filterAllAddresses
|
||||||
OR (HEX(transfers.tx_to_address) IN filter_addresses))
|
OR (HEX(transfers.tx_to_address) IN filter_addresses))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
AND (filterAllAddresses
|
AND (filterAllAddresses
|
||||||
|
|
|
@ -3,6 +3,7 @@ package activity
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
eth "github.com/ethereum/go-ethereum/common"
|
eth "github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
@ -91,28 +92,28 @@ type Filter struct {
|
||||||
// TODO: consider sorting by saved address and contacts to offload the client from doing it at runtime
|
// TODO: consider sorting by saved address and contacts to offload the client from doing it at runtime
|
||||||
func GetRecipients(ctx context.Context, db *sql.DB, offset int, limit int) (addresses []eth.Address, hasMore bool, err error) {
|
func GetRecipients(ctx context.Context, db *sql.DB, offset int, limit int) (addresses []eth.Address, hasMore bool, err error) {
|
||||||
rows, err := db.QueryContext(ctx, `
|
rows, err := db.QueryContext(ctx, `
|
||||||
SELECT
|
SELECT
|
||||||
transfers.address as to_address,
|
transfers.tx_to_address as to_address,
|
||||||
transfers.timestamp AS timestamp
|
transfers.timestamp AS timestamp
|
||||||
FROM transfers
|
FROM transfers
|
||||||
WHERE transfers.multi_transaction_id = 0
|
WHERE transfers.multi_transaction_id = 0
|
||||||
|
|
||||||
UNION ALL
|
UNION ALL
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
pending_transactions.to_address AS to_address,
|
pending_transactions.to_address AS to_address,
|
||||||
pending_transactions.timestamp AS timestamp
|
pending_transactions.timestamp AS timestamp
|
||||||
FROM pending_transactions
|
FROM pending_transactions
|
||||||
WHERE pending_transactions.multi_transaction_id = 0
|
WHERE pending_transactions.multi_transaction_id = 0
|
||||||
|
|
||||||
UNION ALL
|
UNION ALL
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
multi_transactions.to_address AS to_address,
|
multi_transactions.to_address AS to_address,
|
||||||
multi_transactions.timestamp AS timestamp
|
multi_transactions.timestamp AS timestamp
|
||||||
FROM multi_transactions
|
FROM multi_transactions
|
||||||
ORDER BY timestamp DESC
|
ORDER BY timestamp DESC
|
||||||
LIMIT ? OFFSET ?`, limit, offset)
|
LIMIT ? OFFSET ?`, limit, offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
@ -137,3 +138,60 @@ func GetRecipients(ctx context.Context, db *sql.DB, offset int, limit int) (addr
|
||||||
|
|
||||||
return entries, hasMore, nil
|
return entries, hasMore, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetOldestTimestamp(ctx context.Context, db *sql.DB, addresses []eth.Address) (timestamp int64, err error) {
|
||||||
|
queryFormatString := `
|
||||||
|
WITH filter_conditions AS (SELECT ? AS filterAllAddresses),
|
||||||
|
filter_addresses(address) AS (
|
||||||
|
SELECT * FROM (VALUES %s) WHERE (SELECT filterAllAddresses FROM filter_conditions) = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
transfers.tx_from_address AS from_address,
|
||||||
|
transfers.tx_to_address AS to_address,
|
||||||
|
transfers.timestamp AS timestamp
|
||||||
|
FROM transfers, filter_conditions
|
||||||
|
WHERE transfers.multi_transaction_id = 0
|
||||||
|
AND (filterAllAddresses OR HEX(from_address) IN filter_addresses OR HEX(to_address) IN filter_addresses)
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
pending_transactions.from_address AS from_address,
|
||||||
|
pending_transactions.to_address AS to_address,
|
||||||
|
pending_transactions.timestamp AS timestamp
|
||||||
|
FROM pending_transactions, filter_conditions
|
||||||
|
WHERE pending_transactions.multi_transaction_id = 0
|
||||||
|
AND (filterAllAddresses OR HEX(from_address) IN filter_addresses OR HEX(to_address) IN filter_addresses)
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
multi_transactions.from_address AS from_address,
|
||||||
|
multi_transactions.to_address AS to_address,
|
||||||
|
multi_transactions.timestamp AS timestamp
|
||||||
|
FROM multi_transactions, filter_conditions
|
||||||
|
WHERE filterAllAddresses OR HEX(from_address) IN filter_addresses OR HEX(to_address) IN filter_addresses
|
||||||
|
ORDER BY timestamp ASC
|
||||||
|
LIMIT 1`
|
||||||
|
|
||||||
|
filterAllAddresses := len(addresses) == 0
|
||||||
|
involvedAddresses := noEntriesInTmpTableSQLValues
|
||||||
|
if !filterAllAddresses {
|
||||||
|
involvedAddresses = joinAddresses(addresses)
|
||||||
|
}
|
||||||
|
queryString := fmt.Sprintf(queryFormatString, involvedAddresses)
|
||||||
|
|
||||||
|
row := db.QueryRowContext(ctx, queryString, filterAllAddresses)
|
||||||
|
var fromAddress, toAddress sql.NullString
|
||||||
|
err = row.Scan(&fromAddress, &toAddress, ×tamp)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return timestamp, nil
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
eth "github.com/ethereum/go-ethereum/common"
|
||||||
|
|
||||||
"github.com/status-im/status-go/appdatabase"
|
"github.com/status-im/status-go/appdatabase"
|
||||||
|
"github.com/status-im/status-go/services/wallet/testutils"
|
||||||
"github.com/status-im/status-go/services/wallet/transfer"
|
"github.com/status-im/status-go/services/wallet/transfer"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -61,3 +64,65 @@ func TestGetRecipients(t *testing.T) {
|
||||||
require.Equal(t, 4, len(entries))
|
require.Equal(t, 4, len(entries))
|
||||||
require.True(t, hasMore)
|
require.True(t, hasMore)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetOldestTimestampEmptyDB(t *testing.T) {
|
||||||
|
db, close := setupTestFilterDB(t)
|
||||||
|
defer close()
|
||||||
|
|
||||||
|
timestamp, err := GetOldestTimestamp(context.Background(), db, []eth.Address{eth.HexToAddress("0x1")})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, int64(0), timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetOldestTimestamp(t *testing.T) {
|
||||||
|
db, close := setupTestFilterDB(t)
|
||||||
|
defer close()
|
||||||
|
|
||||||
|
// Add 6 extractable transactions
|
||||||
|
trs, _, _ := transfer.GenerateTestTransfers(t, db, 0, 7)
|
||||||
|
for i := range trs {
|
||||||
|
if i < 5 {
|
||||||
|
transfer.InsertTestTransfer(t, db, trs[i].To, &trs[i])
|
||||||
|
} else {
|
||||||
|
transfer.InsertTestPendingTransaction(t, db, &trs[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
multiTxs := []transfer.TestMultiTransaction{
|
||||||
|
transfer.GenerateTestBridgeMultiTransaction(trs[0], trs[1]),
|
||||||
|
transfer.GenerateTestSwapMultiTransaction(trs[2], testutils.SntSymbol, 100),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract oldest timestamp, no filter
|
||||||
|
timestamp, err := GetOldestTimestamp(context.Background(), db, []eth.Address{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, multiTxs[0].Timestamp, timestamp)
|
||||||
|
|
||||||
|
// Test to filter
|
||||||
|
timestamp, err = GetOldestTimestamp(context.Background(), db, []eth.Address{
|
||||||
|
trs[3].To,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, trs[3].Timestamp, timestamp)
|
||||||
|
|
||||||
|
// Test from filter
|
||||||
|
timestamp, err = GetOldestTimestamp(context.Background(), db, []eth.Address{
|
||||||
|
trs[4].From,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, trs[4].Timestamp, timestamp)
|
||||||
|
|
||||||
|
// Test MT
|
||||||
|
timestamp, err = GetOldestTimestamp(context.Background(), db, []eth.Address{
|
||||||
|
multiTxs[1].FromAddress, trs[4].To,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, multiTxs[1].Timestamp, timestamp)
|
||||||
|
|
||||||
|
// Test Pending
|
||||||
|
timestamp, err = GetOldestTimestamp(context.Background(), db, []eth.Address{
|
||||||
|
trs[6].To,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, trs[6].Timestamp, timestamp)
|
||||||
|
}
|
||||||
|
|
|
@ -551,3 +551,8 @@ func (api *API) GetAllRecipients(ctx context.Context, offset int, limit int) (re
|
||||||
result.Addresses, result.HasMore, err = activity.GetRecipients(ctx, api.s.db, offset, limit)
|
result.Addresses, result.HasMore, err = activity.GetRecipients(ctx, api.s.db, offset, limit)
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *API) GetOldestActivityTimestamp(ctx context.Context, addresses []common.Address) (timestamp int64, err error) {
|
||||||
|
log.Debug("wallet.api.GetOldestActivityTimestamp", "addresses.len", len(addresses))
|
||||||
|
return activity.GetOldestTimestamp(ctx, api.s.db, addresses)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue