feat: Community token received notification (#4682)

This commit is contained in:
Cuteivist 2024-02-19 14:55:38 +01:00 committed by GitHub
parent 54d0cf28c7
commit a866b8025e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 1090 additions and 763 deletions

View File

@ -7,6 +7,7 @@ import (
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/verification"
"github.com/status-im/status-go/services/wallet/thirdparty"
)
// The activity center is a place where we store incoming notifications before
@ -36,6 +37,7 @@ const (
ActivityCenterNotificationTypeSetSignerDeclined
ActivityCenterNotificationTypeShareAccounts
ActivityCenterNotificationTypeCommunityTokenReceived
ActivityCenterNotificationTypeFirstCommunityTokenReceived
)
type ActivityCenterMembershipStatus int
@ -60,6 +62,22 @@ const (
var ErrInvalidActivityCenterNotification = errors.New("invalid activity center notification")
type ActivityTokenData struct {
ChainID uint64 `json:"chainId,omitempty"`
CollectibleID thirdparty.CollectibleUniqueID `json:"collectibleId,omitempty"`
TxHash string `json:"txHash,omitempty"`
WalletAddress string `json:"walletAddress,omitempty"`
IsFirst bool `json:"isFirst,omitempty"`
// Community data
CommunityID string `json:"communityId,omitempty"`
// Token data
Amount string `json:"amount,omitempty"`
Name string `json:"name,omitempty"`
Symbol string `json:"symbol,omitempty"`
ImageURL string `json:"imageUrl,omitempty"`
TokenType int `json:"tokenType,omitempty"`
}
type ActivityCenterNotification struct {
ID types.HexBytes `json:"id"`
ChatID string `json:"chatId"`
@ -77,6 +95,7 @@ type ActivityCenterNotification struct {
Deleted bool `json:"deleted"`
Accepted bool `json:"accepted"`
ContactVerificationStatus verification.RequestStatus `json:"contactVerificationStatus"`
TokenData *ActivityTokenData `json:"tokenData"`
//Used for synchronization. Each update should increment the UpdatedAt.
//The value should represent the time when the update occurred.
UpdatedAt uint64 `json:"updatedAt"`

View File

@ -12,7 +12,7 @@ import (
)
const allFieldsForTableActivityCenterNotification = `id, timestamp, notification_type, chat_id, read, dismissed, accepted, message, author,
reply_message, community_id, membership_status, contact_verification_status, deleted, updated_at`
reply_message, community_id, membership_status, contact_verification_status, token_data, deleted, updated_at`
var emptyNotifications = make([]*ActivityCenterNotification, 0)
@ -125,6 +125,15 @@ func (db sqlitePersistence) SaveActivityCenterNotification(notification *Activit
}
}
// encode token data
var encodedTokenData []byte
if notification.TokenData != nil {
encodedTokenData, err = json.Marshal(notification.TokenData)
if err != nil {
return 0, err
}
}
result, err := tx.Exec(`
INSERT OR REPLACE
INTO activity_center_notifications (
@ -141,10 +150,11 @@ func (db sqlitePersistence) SaveActivityCenterNotification(notification *Activit
read,
accepted,
dismissed,
token_data,
deleted,
updated_at
)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
`,
notification.ID,
notification.Timestamp,
@ -159,9 +169,9 @@ func (db sqlitePersistence) SaveActivityCenterNotification(notification *Activit
notification.Read,
notification.Accepted,
notification.Dismissed,
encodedTokenData,
notification.Deleted,
notification.UpdatedAt,
notification.ID,
)
if err != nil {
return 0, err
@ -187,6 +197,7 @@ func (db sqlitePersistence) parseRowFromTableActivityCenterNotification(rows *sq
var communityID sql.NullString
var messageBytes []byte
var replyMessageBytes []byte
var tokenDataBytes []byte
var author sql.NullString
notification := &ActivityCenterNotification{}
err := rows.Scan(
@ -203,6 +214,7 @@ func (db sqlitePersistence) parseRowFromTableActivityCenterNotification(rows *sq
&communityID,
&notification.MembershipStatus,
&notification.ContactVerificationStatus,
&tokenDataBytes,
&notification.Deleted,
&notification.UpdatedAt,
)
@ -222,6 +234,13 @@ func (db sqlitePersistence) parseRowFromTableActivityCenterNotification(rows *sq
notification.Author = author.String
}
if len(tokenDataBytes) > 0 {
err = json.Unmarshal(tokenDataBytes, &notification.TokenData)
if err != nil {
return nil, err
}
}
if len(messageBytes) > 0 {
err = json.Unmarshal(messageBytes, &notification.Message)
if err != nil {
@ -251,6 +270,7 @@ func (db sqlitePersistence) unmarshalActivityCenterNotificationRow(row *sql.Row)
var lastMessageBytes []byte
var messageBytes []byte
var replyMessageBytes []byte
var tokenDataBytes []byte
var name sql.NullString
var author sql.NullString
notification := &ActivityCenterNotification{}
@ -271,6 +291,7 @@ func (db sqlitePersistence) unmarshalActivityCenterNotificationRow(row *sql.Row)
&notification.ContactVerificationStatus,
&name,
&author,
&tokenDataBytes,
&notification.UpdatedAt)
if err != nil {
@ -293,6 +314,13 @@ func (db sqlitePersistence) unmarshalActivityCenterNotificationRow(row *sql.Row)
notification.Author = author.String
}
if len(tokenDataBytes) > 0 {
err = json.Unmarshal(tokenDataBytes, &notification.TokenData)
if err != nil {
return nil, err
}
}
// Restore last message
if lastMessageBytes != nil {
lastMessage := common.NewMessage()
@ -332,6 +360,7 @@ func (db sqlitePersistence) unmarshalActivityCenterNotificationRows(rows *sql.Ro
var lastMessageBytes []byte
var messageBytes []byte
var replyMessageBytes []byte
var tokenDataBytes []byte
var name sql.NullString
var author sql.NullString
notification := &ActivityCenterNotification{}
@ -351,6 +380,7 @@ func (db sqlitePersistence) unmarshalActivityCenterNotificationRows(rows *sql.Ro
&notification.ContactVerificationStatus,
&name,
&author,
&tokenDataBytes,
&latestCursor,
&notification.UpdatedAt)
if err != nil {
@ -373,6 +403,14 @@ func (db sqlitePersistence) unmarshalActivityCenterNotificationRows(rows *sql.Ro
notification.Author = author.String
}
if len(tokenDataBytes) > 0 {
tokenData := &ActivityTokenData{}
if err = json.Unmarshal(tokenDataBytes, &tokenData); err != nil {
return "", nil, err
}
notification.TokenData = tokenData
}
// Restore last message
if lastMessageBytes != nil {
lastMessage := common.NewMessage()
@ -503,6 +541,7 @@ func (db sqlitePersistence) buildActivityCenterQuery(tx *sql.Tx, params activity
a.contact_verification_status,
c.name,
a.author,
a.token_data,
substr('0000000000000000000000000000000000000000000000000000000000000000' || a.timestamp, -64, 64) || hex(a.id) as cursor,
a.updated_at
FROM activity_center_notifications a
@ -623,6 +662,7 @@ func (db sqlitePersistence) GetActivityCenterNotificationsByID(ids []types.HexBy
a.contact_verification_status,
c.name,
a.author,
a.token_data,
substr('0000000000000000000000000000000000000000000000000000000000000000' || a.timestamp, -64, 64) || hex(a.id) as cursor,
a.updated_at
FROM activity_center_notifications a
@ -663,6 +703,7 @@ func (db sqlitePersistence) GetActivityCenterNotificationByID(id types.HexBytes)
a.contact_verification_status,
c.name,
a.author,
a.token_data,
a.updated_at
FROM activity_center_notifications a
LEFT JOIN chats c
@ -1295,6 +1336,7 @@ func (db sqlitePersistence) ActiveContactRequestNotification(contactID string) (
a.contact_verification_status,
c.name,
a.author,
a.token_data,
a.updated_at
FROM activity_center_notifications a
LEFT JOIN chats c ON c.id = a.chat_id

View File

@ -4189,6 +4189,15 @@ func extractQuotedImages(messages []*common.Message, s *server.MediaServer) []st
return quotedImages
}
func (m *Messenger) prepareTokenData(tokenData *ActivityTokenData, s *server.MediaServer) error {
if tokenData.TokenType == int(protobuf.CommunityTokenType_ERC721) {
tokenData.ImageURL = s.MakeWalletCollectibleImagesURL(tokenData.CollectibleID)
} else if tokenData.TokenType == int(protobuf.CommunityTokenType_ERC20) {
tokenData.ImageURL = s.MakeCommunityTokenImagesURL(tokenData.CommunityID, tokenData.ChainID, tokenData.Symbol)
}
return nil
}
func (m *Messenger) prepareMessage(msg *common.Message, s *server.MediaServer) error {
if msg.QuotedMessage != nil && msg.QuotedMessage.ContentType == int64(protobuf.ChatMessage_IMAGE) {
msg.QuotedMessage.ImageLocalURL = s.MakeImageURL(msg.QuotedMessage.ID)

View File

@ -70,6 +70,14 @@ func (m *Messenger) ActivityCenterNotifications(request ActivityCenterNotificati
}
}
}
if notification.TokenData != nil {
if notification.Type == ActivityCenterNotificationTypeCommunityTokenReceived || notification.Type == ActivityCenterNotificationTypeFirstCommunityTokenReceived {
err = m.prepareTokenData(notification.TokenData, m.httpServer)
if err != nil {
return nil, err
}
}
}
}
}

View File

@ -4241,7 +4241,13 @@ func (m *Messenger) PromoteSelfToControlNode(communityID types.HexBytes) (*Messe
return &response, nil
}
func (m *Messenger) CreateResponseWithACNotification(communityID string, acType ActivityCenterType, isRead bool) (*MessengerResponse, error) {
func (m *Messenger) CreateResponseWithACNotification(communityID string, acType ActivityCenterType, isRead bool, tokenDataJSON string) (*MessengerResponse, error) {
tokenData := ActivityTokenData{}
err := json.Unmarshal([]byte(tokenDataJSON), &tokenData)
if len(tokenDataJSON) > 0 && err != nil {
// Only return error when activityDataString is not empty
return nil, err
}
// Activity center notification
notification := &ActivityCenterNotification{
ID: types.FromHex(uuid.New().String()),
@ -4251,11 +4257,17 @@ func (m *Messenger) CreateResponseWithACNotification(communityID string, acType
Read: isRead,
Deleted: false,
UpdatedAt: m.GetCurrentTimeInMillis(),
TokenData: &tokenData,
}
err = m.prepareTokenData(notification.TokenData, m.httpServer)
if err != nil {
return nil, err
}
response := &MessengerResponse{}
err := m.addActivityCenterNotification(response, notification, nil)
err = m.addActivityCenterNotification(response, notification, nil)
if err != nil {
m.logger.Error("failed to save notification", zap.Error(err))
return response, err

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
ALTER TABLE activity_center_notifications ADD COLUMN token_data BLOB DEFAULT NULL;

View File

@ -1685,32 +1685,36 @@ func (api *PublicAPI) GetProfileShowcaseAccountsByAddress(address string) ([]*id
// Returns response with AC notification when owner token is received
func (api *PublicAPI) RegisterOwnerTokenReceivedNotification(communityID string) (*protocol.MessengerResponse, error) {
return api.service.messenger.CreateResponseWithACNotification(communityID, protocol.ActivityCenterNotificationTypeOwnerTokenReceived, false)
return api.service.messenger.CreateResponseWithACNotification(communityID, protocol.ActivityCenterNotificationTypeOwnerTokenReceived, false, "")
}
// Returns response with AC notification when setting signer is successful
func (api *PublicAPI) RegisterReceivedOwnershipNotification(communityID string) (*protocol.MessengerResponse, error) {
return api.service.messenger.CreateResponseWithACNotification(communityID, protocol.ActivityCenterNotificationTypeOwnershipReceived, false)
return api.service.messenger.CreateResponseWithACNotification(communityID, protocol.ActivityCenterNotificationTypeOwnershipReceived, false, "")
}
// Returns response with AC notification when community token is received
func (api *PublicAPI) RegisterReceivedCommunityTokenNotification(communityID string) (*protocol.MessengerResponse, error) {
return api.service.messenger.CreateResponseWithACNotification(communityID, protocol.ActivityCenterNotificationTypeCommunityTokenReceived, false)
func (api *PublicAPI) RegisterReceivedCommunityTokenNotification(communityID string, isFirst bool, tokenData string) (*protocol.MessengerResponse, error) {
activityType := protocol.ActivityCenterNotificationTypeCommunityTokenReceived
if isFirst {
activityType = protocol.ActivityCenterNotificationTypeFirstCommunityTokenReceived
}
return api.service.messenger.CreateResponseWithACNotification(communityID, activityType, false, tokenData)
}
// Returns response with AC notification when setting signer is failed
func (api *PublicAPI) RegisterSetSignerFailedNotification(communityID string) (*protocol.MessengerResponse, error) {
return api.service.messenger.CreateResponseWithACNotification(communityID, protocol.ActivityCenterNotificationTypeSetSignerFailed, false)
return api.service.messenger.CreateResponseWithACNotification(communityID, protocol.ActivityCenterNotificationTypeSetSignerFailed, false, "")
}
// Returns response with AC notification when setting signer is declined
func (api *PublicAPI) RegisterSetSignerDeclinedNotification(communityID string) (*protocol.MessengerResponse, error) {
return api.service.messenger.CreateResponseWithACNotification(communityID, protocol.ActivityCenterNotificationTypeSetSignerDeclined, true)
return api.service.messenger.CreateResponseWithACNotification(communityID, protocol.ActivityCenterNotificationTypeSetSignerDeclined, true, "")
}
// Returns response with AC notification when ownership is lost
func (api *PublicAPI) RegisterLostOwnershipNotification(communityID string) (*protocol.MessengerResponse, error) {
return api.service.messenger.CreateResponseWithACNotification(communityID, protocol.ActivityCenterNotificationTypeOwnershipLost, false)
return api.service.messenger.CreateResponseWithACNotification(communityID, protocol.ActivityCenterNotificationTypeOwnershipLost, false, "")
}
func (api *PublicAPI) PromoteSelfToControlMode(communityID string) error {

View File

@ -300,7 +300,6 @@ func updateAddressOwnershipTimestamp(creator sqlite.StatementCreator, ownerAddre
// Returns the list of added/removed IDs when comparing the given list of IDs with the ones in the DB.
// Call before Update for the result to be useful.
func (o *OwnershipDB) GetIDsNotInDB(
chainID w_common.ChainID,
ownerAddress common.Address,
newIDs []thirdparty.CollectibleUniqueID) ([]thirdparty.CollectibleUniqueID, error) {
ret := make([]thirdparty.CollectibleUniqueID, 0, len(newIDs))
@ -333,6 +332,31 @@ func (o *OwnershipDB) GetIDsNotInDB(
return ret, nil
}
func (o *OwnershipDB) GetIsFirstOfCollection(onwerAddress common.Address, newIDs []thirdparty.CollectibleUniqueID) (map[thirdparty.CollectibleUniqueID]bool, error) {
ret := make(map[thirdparty.CollectibleUniqueID]bool)
exists, err := o.db.Prepare(`SELECT count(*) FROM collectibles_ownership_cache
WHERE chain_id=? AND contract_address=? AND owner_address=?`)
if err != nil {
return nil, err
}
for _, id := range newIDs {
row := exists.QueryRow(
id.ContractID.ChainID,
id.ContractID.Address,
onwerAddress,
)
var count int
err = row.Scan(&count)
if err != nil {
return nil, err
}
ret[id] = count <= 1
}
return ret, nil
}
func (o *OwnershipDB) Update(chainID w_common.ChainID, ownerAddress common.Address, balances thirdparty.TokenBalancesPerContractAddress, timestamp int64) (removedIDs, updatedIDs, insertedIDs []thirdparty.CollectibleUniqueID, err error) {
err = insertTmpOwnership(o.db, chainID, ownerAddress, balances)
if err != nil {

View File

@ -415,8 +415,8 @@ func (s *Service) onOwnedCollectiblesChange(ownedCollectiblesChange OwnedCollect
switch ownedCollectiblesChange.changeType {
case OwnedCollectiblesChangeTypeAdded, OwnedCollectiblesChangeTypeUpdated:
// For recently added/updated collectibles, try to find a matching transfer
s.lookupTransferForCollectibles(ownedCollectiblesChange.ownedCollectibles)
s.notifyCommunityCollectiblesReceived(ownedCollectiblesChange.ownedCollectibles)
hashMap := s.lookupTransferForCollectibles(ownedCollectiblesChange.ownedCollectibles)
s.notifyCommunityCollectiblesReceived(ownedCollectiblesChange.ownedCollectibles, hashMap)
}
}
@ -437,7 +437,7 @@ func (s *Service) onCollectiblesTransfer(account common.Address, chainID walletC
}
}
func (s *Service) lookupTransferForCollectibles(ownedCollectibles OwnedCollectibles) {
func (s *Service) lookupTransferForCollectibles(ownedCollectibles OwnedCollectibles) map[thirdparty.CollectibleUniqueID]common.Hash {
// There are some limitations to this approach:
// - Collectibles ownership and transfers are not in sync and might represent the state at different moments.
// - We have no way of knowing if the latest collectible transfer we've detected is actually the latest one, so the timestamp we
@ -445,6 +445,9 @@ func (s *Service) lookupTransferForCollectibles(ownedCollectibles OwnedCollectib
// - There might be detected transfers that are temporarily not reflected in the collectibles ownership.
// - For ERC721 tokens we should only look for incoming transfers. For ERC1155 tokens we should look for both incoming and outgoing transfers.
// We need to get the contract standard for each collectible to know which approach to take.
result := make(map[thirdparty.CollectibleUniqueID]common.Hash)
for _, id := range ownedCollectibles.ids {
transfer, err := s.transferDB.GetLatestCollectibleTransfer(ownedCollectibles.account, id)
if err != nil {
@ -452,17 +455,24 @@ func (s *Service) lookupTransferForCollectibles(ownedCollectibles OwnedCollectib
continue
}
if transfer != nil {
result[id] = transfer.Transaction.Hash()
err = s.manager.SetCollectibleTransferID(ownedCollectibles.account, id, transfer.ID, false)
if err != nil {
log.Error("Error setting transfer ID for collectible", "error", err)
}
}
}
return result
}
func (s *Service) notifyCommunityCollectiblesReceived(ownedCollectibles OwnedCollectibles) {
func (s *Service) notifyCommunityCollectiblesReceived(ownedCollectibles OwnedCollectibles, hashMap map[thirdparty.CollectibleUniqueID]common.Hash) {
ctx := context.Background()
firstCollectibles, err := s.ownershipDB.GetIsFirstOfCollection(ownedCollectibles.account, ownedCollectibles.ids)
if err != nil {
return
}
collectiblesData, err := s.manager.FetchAssetsByCollectibleUniqueID(ctx, ownedCollectibles.ids, false)
if err != nil {
log.Error("Error fetching collectibles data", "error", err)
@ -475,7 +485,45 @@ func (s *Service) notifyCommunityCollectiblesReceived(ownedCollectibles OwnedCol
return
}
encodedMessage, err := json.Marshal(communityCollectibles)
type CollectibleGroup struct {
contractID thirdparty.ContractID
txHash string
}
groups := make(map[CollectibleGroup]Collectible)
for i, collectible := range communityCollectibles {
for key, value := range hashMap {
if key.Same(&collectible.ID) {
communityCollectibles[i].LatestTxHash = value.Hex()
break
}
}
for id, value := range firstCollectibles {
if value && id.Same(&collectible.ID) {
communityCollectibles[i].IsFirst = true
break
}
}
group := CollectibleGroup{
contractID: collectible.ID.ContractID,
txHash: collectible.LatestTxHash,
}
_, ok := groups[group]
if !ok {
collectible.ReceivedAmount = float64(0)
}
collectible.ReceivedAmount = collectible.ReceivedAmount + 1
groups[group] = collectible
}
groupedCommunityCollectibles := make([]Collectible, 0, len(groups))
for _, collectible := range groups {
groupedCommunityCollectibles = append(groupedCommunityCollectibles, collectible)
}
encodedMessage, err := json.Marshal(groupedCommunityCollectibles)
if err != nil {
return
}

View File

@ -15,6 +15,9 @@ type Collectible struct {
CollectionData *CollectionData `json:"collection_data,omitempty"`
CommunityData *CommunityData `json:"community_data,omitempty"`
Ownership []thirdparty.AccountBalance `json:"ownership,omitempty"`
IsFirst bool `json:"is_first,omitempty"`
LatestTxHash string `json:"latest_tx_hash,omitempty"`
ReceivedAmount float64 `json:"received_amount,omitempty"`
}
type CollectibleData struct {
@ -168,9 +171,11 @@ func fullCollectiblesDataToCommunityHeader(data []thirdparty.FullCollectibleData
ContractType: getContractType(c),
CollectibleData: &CollectibleData{
Name: c.CollectibleData.Name,
ImageURL: &c.CollectibleData.ImageURL,
},
CommunityData: &communityData,
Ownership: c.Ownership,
IsFirst: c.CollectibleData.IsFirst,
}
res = append(res, header)

View File

@ -138,6 +138,7 @@ type CollectibleData struct {
Traits []CollectibleTrait `json:"traits"`
BackgroundColor string `json:"background_color"`
TokenURI string `json:"token_uri"`
IsFirst bool `json:"is_first"`
}
// Community-related collectible info. Present only for collectibles minted in a community.

View File

@ -68,8 +68,9 @@ type ReceivedToken struct {
Image string `json:"image,omitempty"`
ChainID uint64 `json:"chainId"`
CommunityData *community.Data `json:"community_data,omitempty"`
Balance *big.Int `json:"balance"`
Amount float64 `json:"amount"`
TxHash common.Hash `json:"txHash"`
IsFirst bool `json:"isFirst"`
}
func (t *Token) IsNative() bool {
@ -316,20 +317,20 @@ func (tm *Manager) FindOrCreateTokenByAddress(ctx context.Context, chainID uint6
return token
}
func (tm *Manager) MarkAsPreviouslyOwnedToken(token *Token, owner common.Address) error {
func (tm *Manager) MarkAsPreviouslyOwnedToken(token *Token, owner common.Address) (bool, error) {
if token == nil {
return errors.New("token is nil")
return false, errors.New("token is nil")
}
if (owner == common.Address{}) {
return errors.New("owner is nil")
return false, errors.New("owner is nil")
}
count := 0
err := tm.db.QueryRow(`SELECT EXISTS(SELECT 1 FROM token_balances WHERE user_address = ? AND token_address = ? AND chain_id = ?)`, owner.Hex(), token.Address.Hex(), token.ChainID).Scan(&count)
if err != nil || count > 0 {
return err
return false, err
}
_, err = tm.db.Exec(`INSERT INTO token_balances(user_address,token_name,token_symbol,token_address,token_decimals,chain_id,token_decimals,raw_balance,balance) VALUES (?,?,?,?,?,?,?,?,?)`, owner.Hex(), token.Name, token.Symbol, token.Address.Hex(), token.Decimals, token.ChainID, 0, "0", "0")
return err
return true, err
}
func (tm *Manager) discoverTokenCommunityID(ctx context.Context, token *Token, address common.Address) {
@ -809,7 +810,7 @@ func (tm *Manager) GetBalancesAtByChain(parent context.Context, clients map[uint
return response, group.Error()
}
func (tm *Manager) SignalCommunityTokenReceived(address common.Address, txHash common.Hash, value *big.Int, t *Token) {
func (tm *Manager) SignalCommunityTokenReceived(address common.Address, txHash common.Hash, value *big.Int, t *Token, isFirst bool) {
if tm.walletFeed == nil || t == nil || t.CommunityData == nil {
return
}
@ -826,6 +827,8 @@ func (tm *Manager) SignalCommunityTokenReceived(address common.Address, txHash c
}
}
floatAmount, _ := new(big.Float).Quo(new(big.Float).SetInt(value), new(big.Float).SetInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(t.Decimals)), nil))).Float64()
receivedToken := ReceivedToken{
Address: t.Address,
Name: t.Name,
@ -833,8 +836,9 @@ func (tm *Manager) SignalCommunityTokenReceived(address common.Address, txHash c
Image: t.Image,
ChainID: t.ChainID,
CommunityData: t.CommunityData,
Balance: value,
Amount: floatAmount,
TxHash: txHash,
IsFirst: isFirst,
}
encodedMessage, err := json.Marshal(receivedToken)

View File

@ -208,14 +208,17 @@ func TestMarkAsPreviouslyOwnedToken(t *testing.T) {
ChainID: 1,
}
err := manager.MarkAsPreviouslyOwnedToken(nil, owner)
isFirst, err := manager.MarkAsPreviouslyOwnedToken(nil, owner)
require.Error(t, err)
require.False(t, isFirst)
err = manager.MarkAsPreviouslyOwnedToken(token, common.Address{})
isFirst, err = manager.MarkAsPreviouslyOwnedToken(token, common.Address{})
require.Error(t, err)
require.False(t, isFirst)
err = manager.MarkAsPreviouslyOwnedToken(token, owner)
isFirst, err = manager.MarkAsPreviouslyOwnedToken(token, owner)
require.NoError(t, err)
require.True(t, isFirst)
// Verify that the token balance was inserted correctly
var count int
@ -225,8 +228,9 @@ func TestMarkAsPreviouslyOwnedToken(t *testing.T) {
token.Name = "123"
err = manager.MarkAsPreviouslyOwnedToken(token, owner)
isFirst, err = manager.MarkAsPreviouslyOwnedToken(token, owner)
require.NoError(t, err)
require.False(t, isFirst)
// Not updated because already exists
err = manager.db.QueryRow(`SELECT count(*) FROM token_balances`).Scan(&count)
@ -235,11 +239,12 @@ func TestMarkAsPreviouslyOwnedToken(t *testing.T) {
token.ChainID = 2
err = manager.MarkAsPreviouslyOwnedToken(token, owner)
isFirst, err = manager.MarkAsPreviouslyOwnedToken(token, owner)
require.NoError(t, err)
// Same token on different chains counts as different token
err = manager.db.QueryRow(`SELECT count(*) FROM token_balances`).Scan(&count)
require.NoError(t, err)
require.Equal(t, 2, count)
require.True(t, isFirst)
}

View File

@ -378,11 +378,11 @@ func (c *transfersCommand) markMultiTxTokensAsPreviouslyOwned(ctx context.Contex
}
if len(multiTransaction.ToAsset) > 0 && multiTransaction.ToNetworkID > 0 {
token := c.tokenManager.GetToken(multiTransaction.ToNetworkID, multiTransaction.ToAsset)
_ = c.tokenManager.MarkAsPreviouslyOwnedToken(token, ownerAddress)
_, _ = c.tokenManager.MarkAsPreviouslyOwnedToken(token, ownerAddress)
}
if len(multiTransaction.FromAsset) > 0 && multiTransaction.FromNetworkID > 0 {
token := c.tokenManager.GetToken(multiTransaction.FromNetworkID, multiTransaction.FromAsset)
_ = c.tokenManager.MarkAsPreviouslyOwnedToken(token, ownerAddress)
_, _ = c.tokenManager.MarkAsPreviouslyOwnedToken(token, ownerAddress)
}
}
@ -439,11 +439,12 @@ func (c *transfersCommand) processUnknownErc20CommunityTransactions(ctx context.
// Find token in db or if this is a community token, find its metadata
token := c.tokenManager.FindOrCreateTokenByAddress(ctx, tx.NetworkID, *tx.Transaction.To())
if token != nil {
isFirst := false
if token.Verified || token.CommunityData != nil {
_ = c.tokenManager.MarkAsPreviouslyOwnedToken(token, tx.Address)
isFirst, _ = c.tokenManager.MarkAsPreviouslyOwnedToken(token, tx.Address)
}
if token.CommunityData != nil {
go c.tokenManager.SignalCommunityTokenReceived(tx.Address, tx.ID, tx.Transaction.Value(), token)
go c.tokenManager.SignalCommunityTokenReceived(tx.Address, tx.ID, tx.TokenValue, token, isFirst)
}
}
}