fix: Upgrade status-go to the most recent version of release branch which contains memory fix
Fix #4990
This commit is contained in:
committed by
Michał Iskierko
parent
03d490156a
commit
66cf3d21b9
2
vendor/github.com/status-im/status-go/services/accounts/settings.go
generated
vendored
2
vendor/github.com/status-im/status-go/services/accounts/settings.go
generated
vendored
@@ -171,10 +171,12 @@ func (api *SettingsAPI) SetBio(bio string) error {
|
||||
return (*api.messenger).SetBio(bio)
|
||||
}
|
||||
|
||||
// Deprecated: use social links from ProfileShowcasePreferences
|
||||
func (api *SettingsAPI) GetSocialLinks() (identity.SocialLinks, error) {
|
||||
return api.db.GetSocialLinks()
|
||||
}
|
||||
|
||||
// Deprecated: use social links from ProfileShowcasePreferences
|
||||
func (api *SettingsAPI) AddOrReplaceSocialLinks(links identity.SocialLinks) error {
|
||||
for _, link := range links {
|
||||
if len(link.Text) == 0 {
|
||||
|
||||
26
vendor/github.com/status-im/status-go/services/chat/api.go
generated
vendored
26
vendor/github.com/status-im/status-go/services/chat/api.go
generated
vendored
@@ -77,8 +77,14 @@ type Chat struct {
|
||||
FirstMessageTimestamp uint32 `json:"firstMessageTimestamp,omitempty"`
|
||||
Highlight bool `json:"highlight,omitempty"`
|
||||
PinnedMessages *PinnedMessages `json:"pinnedMessages,omitempty"`
|
||||
CanPost bool `json:"canPost"`
|
||||
Base64Image string `json:"image,omitempty"`
|
||||
// Deprecated: CanPost is deprecated in favor of CanPostMessages/CanPostReactions/etc.
|
||||
// For now CanPost will equal to CanPostMessages.
|
||||
CanPost bool `json:"canPost"`
|
||||
CanPostMessages bool `json:"canPostMessages"`
|
||||
CanPostReactions bool `json:"canPostReactions"`
|
||||
ViewersCanPostReactions bool `json:"viewersCanPostReactions"`
|
||||
Base64Image string `json:"image,omitempty"`
|
||||
HideIfPermissionsNotMet bool `json:"hideIfPermissionsNotMet,omitempty"`
|
||||
}
|
||||
|
||||
type ChannelGroup struct {
|
||||
@@ -472,6 +478,9 @@ func (api *API) getCommunityByID(id string) (*communities.Community, error) {
|
||||
}
|
||||
|
||||
func (chat *Chat) populateCommunityFields(community *communities.Community) error {
|
||||
chat.CanPost = true
|
||||
chat.CanPostMessages = true
|
||||
chat.CanPostReactions = true
|
||||
if community == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -482,18 +491,27 @@ func (chat *Chat) populateCommunityFields(community *communities.Community) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
canPost, err := community.CanMemberIdentityPost(chat.ID)
|
||||
canPostMessages, err := community.CanMemberIdentityPost(chat.ID, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
canPostReactions, err := community.CanMemberIdentityPost(chat.ID, protobuf.ApplicationMetadataMessage_EMOJI_REACTION)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chat.CategoryID = commChat.CategoryId
|
||||
chat.HideIfPermissionsNotMet = commChat.HideIfPermissionsNotMet
|
||||
chat.Position = commChat.Position
|
||||
chat.Permissions = commChat.Permissions
|
||||
chat.Emoji = commChat.Identity.Emoji
|
||||
chat.Name = commChat.Identity.DisplayName
|
||||
chat.Description = commChat.Identity.Description
|
||||
chat.CanPost = canPost
|
||||
chat.CanPost = canPostMessages
|
||||
chat.CanPostMessages = canPostMessages
|
||||
chat.CanPostReactions = canPostReactions
|
||||
chat.ViewersCanPostReactions = commChat.ViewersCanPostReactions
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
57
vendor/github.com/status-im/status-go/services/communitytokens/api.go
generated
vendored
57
vendor/github.com/status-im/status-go/services/communitytokens/api.go
generated
vendored
@@ -330,9 +330,8 @@ func (api *API) DeployAssets(ctx context.Context, chainID uint64, deploymentPara
|
||||
}
|
||||
|
||||
// Returns gas units + 10%
|
||||
func (api *API) DeployCollectiblesEstimate(ctx context.Context) (uint64, error) {
|
||||
// TODO investigate why the code below does not return correct values
|
||||
/*ethClient, err := api.s.manager.rpcClient.EthClient(420)
|
||||
func (api *API) DeployCollectiblesEstimate(ctx context.Context, chainID uint64, fromAddress string) (uint64, error) {
|
||||
ethClient, err := api.s.manager.rpcClient.EthClient(chainID)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
return 0, err
|
||||
@@ -343,35 +342,63 @@ func (api *API) DeployCollectiblesEstimate(ctx context.Context) (uint64, error)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
data, err := collectiblesABI.Pack("", "name", "SYMBOL", big.NewInt(20), true, false, "tokenUriwhcih is very long asdkfjlsdkjflk",
|
||||
// use random parameters, they will not have impact on deployment results
|
||||
data, err := collectiblesABI.Pack("" /*constructor name is empty*/, "name", "SYMBOL", big.NewInt(20), true, false, "tokenUri",
|
||||
common.HexToAddress("0x77b48394c650520012795a1a25696d7eb542d110"), common.HexToAddress("0x77b48394c650520012795a1a25696d7eb542d110"))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
callMsg := ethereum.CallMsg{
|
||||
From: common.HexToAddress("0x77b48394c650520012795a1a25696d7eb542d110"),
|
||||
From: common.HexToAddress(fromAddress),
|
||||
To: nil,
|
||||
Value: big.NewInt(0),
|
||||
Data: data,
|
||||
Data: append(common.FromHex(collectibles.CollectiblesBin), data...),
|
||||
}
|
||||
estimate, err := ethClient.EstimateGas(ctx, callMsg)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return estimate + uint64(float32(estimate)*0.1), nil*/
|
||||
|
||||
// TODO compute fee dynamically
|
||||
// the code above returns too low fees, need to investigate
|
||||
gasAmount := uint64(2500000)
|
||||
return gasAmount + uint64(float32(gasAmount)*0.1), nil
|
||||
finalEstimation := estimate + uint64(float32(estimate)*0.1)
|
||||
log.Debug("Collectibles deployment gas estimation: ", finalEstimation)
|
||||
return finalEstimation, nil
|
||||
}
|
||||
|
||||
// Returns gas units + 10%
|
||||
func (api *API) DeployAssetsEstimate(ctx context.Context) (uint64, error) {
|
||||
// TODO compute fee dynamically
|
||||
gasAmount := uint64(1500000)
|
||||
return gasAmount + uint64(float32(gasAmount)*0.1), nil
|
||||
func (api *API) DeployAssetsEstimate(ctx context.Context, chainID uint64, fromAddress string) (uint64, error) {
|
||||
ethClient, err := api.s.manager.rpcClient.EthClient(chainID)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
return 0, err
|
||||
}
|
||||
|
||||
assetsABI, err := abi.JSON(strings.NewReader(assets.AssetsABI))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// use random parameters, they will not have impact on deployment results
|
||||
data, err := assetsABI.Pack("" /*constructor name is empty*/, "name", "SYMBOL", uint8(18), big.NewInt(20), "tokenUri",
|
||||
common.HexToAddress("0x77b48394c650520012795a1a25696d7eb542d110"), common.HexToAddress("0x77b48394c650520012795a1a25696d7eb542d110"))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
callMsg := ethereum.CallMsg{
|
||||
From: common.HexToAddress(fromAddress),
|
||||
To: nil,
|
||||
Value: big.NewInt(0),
|
||||
Data: append(common.FromHex(assets.AssetsBin), data...),
|
||||
}
|
||||
estimate, err := ethClient.EstimateGas(ctx, callMsg)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
finalEstimation := estimate + uint64(float32(estimate)*0.1)
|
||||
log.Debug("Assets deployment gas estimation: ", finalEstimation)
|
||||
return finalEstimation, nil
|
||||
}
|
||||
|
||||
func (api *API) DeployOwnerTokenEstimate(ctx context.Context, chainID uint64, fromAddress string,
|
||||
|
||||
77
vendor/github.com/status-im/status-go/services/ext/api.go
generated
vendored
77
vendor/github.com/status-im/status-go/services/ext/api.go
generated
vendored
@@ -31,6 +31,7 @@ import (
|
||||
"github.com/status-im/status-go/protocol/communities/token"
|
||||
"github.com/status-im/status-go/protocol/discord"
|
||||
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
||||
"github.com/status-im/status-go/protocol/identity"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
"github.com/status-im/status-go/protocol/pushnotificationclient"
|
||||
"github.com/status-im/status-go/protocol/requests"
|
||||
@@ -400,6 +401,11 @@ func (api *PublicAPI) JoinedCommunities(parent context.Context) ([]*communities.
|
||||
return api.service.messenger.JoinedCommunities()
|
||||
}
|
||||
|
||||
// IsDisplayNameDupeOfCommunityMember returns if any controlled or joined community has a member with provided display name
|
||||
func (api *PublicAPI) IsDisplayNameDupeOfCommunityMember(name string) (bool, error) {
|
||||
return api.service.messenger.IsDisplayNameDupeOfCommunityMember(name)
|
||||
}
|
||||
|
||||
// CommunityTags return the list of possible community tags
|
||||
func (api *PublicAPI) CommunityTags(parent context.Context) map[string]string {
|
||||
return requests.TagsEmojies
|
||||
@@ -448,6 +454,16 @@ func (api *PublicAPI) SetCommunityShard(request *requests.SetCommunityShard) (*p
|
||||
return api.service.messenger.SetCommunityShard(request)
|
||||
}
|
||||
|
||||
// Sets the community storenodes for a community
|
||||
func (api *PublicAPI) SetCommunityStorenodes(request *requests.SetCommunityStorenodes) (*protocol.MessengerResponse, error) {
|
||||
return api.service.messenger.SetCommunityStorenodes(request)
|
||||
}
|
||||
|
||||
// Gets the community storenodes for a community
|
||||
func (api *PublicAPI) GetCommunityStorenodes(id types.HexBytes) (*protocol.MessengerResponse, error) {
|
||||
return api.service.messenger.GetCommunityStorenodes(id)
|
||||
}
|
||||
|
||||
// ExportCommunity exports the private key of the community with given ID
|
||||
func (api *PublicAPI) ExportCommunity(id types.HexBytes) (types.HexBytes, error) {
|
||||
key, err := api.service.messenger.ExportCommunity(id)
|
||||
@@ -861,6 +877,10 @@ func (api *PublicAPI) DismissLatestContactRequestForContact(ctx context.Context,
|
||||
return api.service.messenger.DismissLatestContactRequestForContact(ctx, request)
|
||||
}
|
||||
|
||||
func (api *PublicAPI) GetLatestContactRequestForContact(ctx context.Context, contactID string) (*protocol.MessengerResponse, error) {
|
||||
return api.service.messenger.GetLatestContactRequestForContact(contactID)
|
||||
}
|
||||
|
||||
func (api *PublicAPI) RetractContactRequest(ctx context.Context, request *requests.RetractContactRequest) (*protocol.MessengerResponse, error) {
|
||||
return api.service.messenger.RetractContactRequest(request)
|
||||
}
|
||||
@@ -1348,11 +1368,11 @@ func (api *PublicAPI) DeleteActivityCenterNotifications(ctx context.Context, ids
|
||||
}
|
||||
|
||||
func (api *PublicAPI) RequestAllHistoricMessages(forceFetchingBackup bool) (*protocol.MessengerResponse, error) {
|
||||
return api.service.messenger.RequestAllHistoricMessages(forceFetchingBackup)
|
||||
return api.service.messenger.RequestAllHistoricMessages(forceFetchingBackup, false)
|
||||
}
|
||||
|
||||
func (api *PublicAPI) RequestAllHistoricMessagesWithRetries(forceFetchingBackup bool) (*protocol.MessengerResponse, error) {
|
||||
return api.service.messenger.RequestAllHistoricMessagesWithRetries(forceFetchingBackup)
|
||||
return api.service.messenger.RequestAllHistoricMessages(forceFetchingBackup, true)
|
||||
}
|
||||
|
||||
func (api *PublicAPI) DisconnectActiveMailserver() {
|
||||
@@ -1479,6 +1499,10 @@ func (api *PublicAPI) ToggleUseMailservers(value bool) error {
|
||||
return api.service.messenger.ToggleUseMailservers(value)
|
||||
}
|
||||
|
||||
func (api *PublicAPI) TogglePeerSyncing(request *requests.TogglePeerSyncingRequest) error {
|
||||
return api.service.messenger.TogglePeerSyncing(request)
|
||||
}
|
||||
|
||||
func (api *PublicAPI) SetPinnedMailservers(pinnedMailservers map[string]string) error {
|
||||
return api.service.messenger.SetPinnedMailservers(pinnedMailservers)
|
||||
}
|
||||
@@ -1663,53 +1687,67 @@ func (api *PublicAPI) CreateTokenGatedCommunity() (*protocol.MessengerResponse,
|
||||
}
|
||||
|
||||
// Set profile showcase preference for current user
|
||||
func (api *PublicAPI) SetProfileShowcasePreferences(preferences *protocol.ProfileShowcasePreferences) error {
|
||||
return api.service.messenger.SetProfileShowcasePreferences(preferences)
|
||||
func (api *PublicAPI) SetProfileShowcasePreferences(preferences *identity.ProfileShowcasePreferences) error {
|
||||
return api.service.messenger.SetProfileShowcasePreferences(preferences, true)
|
||||
}
|
||||
|
||||
// Get all profile showcase preferences for current user
|
||||
func (api *PublicAPI) GetProfileShowcasePreferences() (*protocol.ProfileShowcasePreferences, error) {
|
||||
func (api *PublicAPI) GetProfileShowcasePreferences() (*identity.ProfileShowcasePreferences, error) {
|
||||
return api.service.messenger.GetProfileShowcasePreferences()
|
||||
}
|
||||
|
||||
// Get profile showcase for a contact
|
||||
func (api *PublicAPI) GetProfileShowcaseForContact(contactID string) (*protocol.ProfileShowcase, error) {
|
||||
return api.service.messenger.GetProfileShowcaseForContact(contactID)
|
||||
func (api *PublicAPI) GetProfileShowcaseForContact(contactID string, validate bool) (*identity.ProfileShowcase, error) {
|
||||
return api.service.messenger.GetProfileShowcaseForContact(contactID, validate)
|
||||
}
|
||||
|
||||
// Get profile showcase accounts by address
|
||||
func (api *PublicAPI) GetProfileShowcaseAccountsByAddress(address string) ([]*protocol.ProfileShowcaseAccount, error) {
|
||||
func (api *PublicAPI) GetProfileShowcaseAccountsByAddress(address string) ([]*identity.ProfileShowcaseAccount, error) {
|
||||
return api.service.messenger.GetProfileShowcaseAccountsByAddress(address)
|
||||
}
|
||||
|
||||
// Get profile showcase max social link entries count
|
||||
func (api *PublicAPI) GetProfileShowcaseSocialLinksLimit() (int, error) {
|
||||
return api.service.messenger.GetProfileShowcaseSocialLinksLimit()
|
||||
}
|
||||
|
||||
// Get profile showcase max entries count (excluding social links)
|
||||
func (api *PublicAPI) GetProfileShowcaseEntriesLimit() (int, error) {
|
||||
return api.service.messenger.GetProfileShowcaseEntriesLimit()
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@@ -1737,6 +1775,15 @@ func (api *PublicAPI) SetCustomizationColor(ctx context.Context, request *reques
|
||||
return api.service.messenger.SetCustomizationColor(ctx, request)
|
||||
}
|
||||
|
||||
func (api *PublicAPI) GetCommunityMemberAllMessages(request *requests.CommunityMemberMessages) ([]*common.Message, error) {
|
||||
return api.service.messenger.GetCommunityMemberAllMessages(request)
|
||||
}
|
||||
|
||||
// Delete a specific community member messages or all community member messages (based on provided parameters)
|
||||
func (api *PublicAPI) DeleteCommunityMemberMessages(request *requests.DeleteCommunityMemberMessages) (*protocol.MessengerResponse, error) {
|
||||
return api.service.messenger.DeleteCommunityMemberMessages(request)
|
||||
}
|
||||
|
||||
// -----
|
||||
// HELPER
|
||||
// -----
|
||||
|
||||
120
vendor/github.com/status-im/status-go/services/ext/service.go
generated
vendored
120
vendor/github.com/status-im/status-go/services/ext/service.go
generated
vendored
@@ -39,7 +39,6 @@ import (
|
||||
"github.com/status-im/status-go/protocol"
|
||||
"github.com/status-im/status-go/protocol/anonmetrics"
|
||||
"github.com/status-im/status-go/protocol/common"
|
||||
"github.com/status-im/status-go/protocol/common/shard"
|
||||
"github.com/status-im/status-go/protocol/communities"
|
||||
"github.com/status-im/status-go/protocol/communities/token"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
@@ -53,6 +52,7 @@ import (
|
||||
"github.com/status-im/status-go/services/ext/mailservers"
|
||||
mailserversDB "github.com/status-im/status-go/services/mailservers"
|
||||
"github.com/status-im/status-go/services/wallet"
|
||||
"github.com/status-im/status-go/services/wallet/collectibles"
|
||||
w_common "github.com/status-im/status-go/services/wallet/common"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
"github.com/status-im/status-go/wakuv2"
|
||||
@@ -534,7 +534,30 @@ func (s *Service) GetCommunityID(tokenURI string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *Service) FillCollectibleMetadata(collectible *thirdparty.FullCollectibleData) error {
|
||||
func (s *Service) FillCollectiblesMetadata(communityID string, cs []*thirdparty.FullCollectibleData) (bool, error) {
|
||||
if s.messenger == nil {
|
||||
return false, fmt.Errorf("messenger not ready")
|
||||
}
|
||||
|
||||
community, err := s.fetchCommunityInfoForCollectibles(communityID, collectibles.IDsFromAssets(cs))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if community == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
for _, collectible := range cs {
|
||||
err := s.FillCollectibleMetadata(community, collectible)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s *Service) FillCollectibleMetadata(community *communities.Community, collectible *thirdparty.FullCollectibleData) error {
|
||||
if s.messenger == nil {
|
||||
return fmt.Errorf("messenger not ready")
|
||||
}
|
||||
@@ -550,18 +573,6 @@ func (s *Service) FillCollectibleMetadata(collectible *thirdparty.FullCollectibl
|
||||
return fmt.Errorf("invalid communityID")
|
||||
}
|
||||
|
||||
// FetchCommunityInfo should have been previously called once to ensure
|
||||
// that the latest version of the CommunityDescription is available in the DB
|
||||
community, err := s.fetchCommunity(communityID, false)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if community == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
tokenMetadata, err := s.fetchCommunityCollectibleMetadata(community, id.ContractID)
|
||||
|
||||
if err != nil {
|
||||
@@ -636,47 +647,84 @@ func communityToInfo(community *communities.Community) *thirdparty.CommunityInfo
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) FetchCommunityInfo(communityID string) (*thirdparty.CommunityInfo, error) {
|
||||
community, err := s.fetchCommunity(communityID, true)
|
||||
func (s *Service) fetchCommunityFromStoreNodes(communityID string) (*communities.Community, error) {
|
||||
community, err := s.messenger.FetchCommunity(&protocol.FetchCommunityRequest{
|
||||
CommunityKey: communityID,
|
||||
TryDatabase: false,
|
||||
WaitForResponse: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return communityToInfo(community), nil
|
||||
return community, nil
|
||||
}
|
||||
|
||||
func (s *Service) fetchCommunity(communityID string, fetchLatest bool) (*communities.Community, error) {
|
||||
func (s *Service) GetCommunityInfoFromDB(communityID string) (*thirdparty.CommunityInfo, error) {
|
||||
community, err := s.messenger.FindCommunityInfoFromDB(communityID)
|
||||
return communityToInfo(community), err
|
||||
}
|
||||
|
||||
// Fetch latest community from store nodes.
|
||||
func (s *Service) FetchCommunityInfo(communityID string) (*thirdparty.CommunityInfo, error) {
|
||||
if s.messenger == nil {
|
||||
return nil, fmt.Errorf("messenger not ready")
|
||||
}
|
||||
|
||||
// Try to fetch metadata from Messenger communities
|
||||
community, err := s.messenger.FindCommunityInfoFromDB(communityID)
|
||||
if err != nil && err != communities.ErrOrgNotFound {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: we need the shard information in the collectible to be able to retrieve info for
|
||||
// communities that have specific shards
|
||||
|
||||
if fetchLatest {
|
||||
// Try to fetch the latest version of the Community
|
||||
var shard *shard.Shard = nil // TODO: build this with info from token
|
||||
// NOTE: The community returned by this function will be nil if
|
||||
// the version we have in the DB is the latest available.
|
||||
_, err := s.messenger.FetchCommunity(&protocol.FetchCommunityRequest{
|
||||
CommunityKey: communityID,
|
||||
Shard: shard,
|
||||
TryDatabase: false,
|
||||
WaitForResponse: true,
|
||||
})
|
||||
// Fetch latest version from store nodes
|
||||
if community == nil || !community.IsControlNode() {
|
||||
community, err = s.fetchCommunityFromStoreNodes(communityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Get the latest successfully fetched version of the Community
|
||||
return communityToInfo(community), nil
|
||||
}
|
||||
|
||||
// Fetch latest community from store nodes only if any collectibles data is missing.
|
||||
func (s *Service) fetchCommunityInfoForCollectibles(communityID string, ids []thirdparty.CollectibleUniqueID) (*communities.Community, error) {
|
||||
community, err := s.messenger.FindCommunityInfoFromDB(communityID)
|
||||
if err != nil {
|
||||
if err != nil && err != communities.ErrOrgNotFound {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if community == nil {
|
||||
return s.fetchCommunityFromStoreNodes(communityID)
|
||||
}
|
||||
|
||||
if community.IsControlNode() {
|
||||
return community, nil
|
||||
}
|
||||
|
||||
contractIDs := func() map[string]thirdparty.ContractID {
|
||||
result := map[string]thirdparty.ContractID{}
|
||||
for _, id := range ids {
|
||||
result[id.HashKey()] = id.ContractID
|
||||
}
|
||||
return result
|
||||
}()
|
||||
|
||||
hasAllMetadata := true
|
||||
for _, contractID := range contractIDs {
|
||||
tokenMetadata, err := s.fetchCommunityCollectibleMetadata(community, contractID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tokenMetadata == nil {
|
||||
hasAllMetadata = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !hasAllMetadata {
|
||||
return s.fetchCommunityFromStoreNodes(communityID)
|
||||
}
|
||||
|
||||
return community, nil
|
||||
}
|
||||
|
||||
|
||||
9
vendor/github.com/status-im/status-go/services/mailservers/database.go
generated
vendored
9
vendor/github.com/status-im/status-go/services/mailservers/database.go
generated
vendored
@@ -146,13 +146,16 @@ func (d *Database) Add(mailserver Mailserver) error {
|
||||
}
|
||||
|
||||
func (d *Database) Mailservers() ([]Mailserver, error) {
|
||||
var result []Mailserver
|
||||
|
||||
rows, err := d.db.Query(`SELECT id, name, address, password, fleet FROM mailservers`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
return toMailservers(rows)
|
||||
}
|
||||
|
||||
func toMailservers(rows *sql.Rows) ([]Mailserver, error) {
|
||||
var result []Mailserver
|
||||
|
||||
for rows.Next() {
|
||||
var (
|
||||
@@ -198,7 +201,7 @@ func (d *Database) AddGaps(gaps []MailserverRequestGap) error {
|
||||
|
||||
for _, gap := range gaps {
|
||||
|
||||
_, err := tx.Exec(`INSERT OR REPLACE INTO mailserver_request_gaps(
|
||||
_, err = tx.Exec(`INSERT OR REPLACE INTO mailserver_request_gaps(
|
||||
id,
|
||||
chat_id,
|
||||
gap_from,
|
||||
|
||||
103
vendor/github.com/status-im/status-go/services/wallet/activity/TODO.md
generated
vendored
103
vendor/github.com/status-im/status-go/services/wallet/activity/TODO.md
generated
vendored
@@ -1,103 +0,0 @@
|
||||
# Provide dynamic activity updates
|
||||
|
||||
Task: https://github.com/status-im/status-desktop/issues/12120
|
||||
|
||||
## Intro
|
||||
|
||||
In the current approach only static paginated filtering is possible because the filtering is done in SQL
|
||||
|
||||
The updated requirements need to support dynamic updates of the current visualized filter
|
||||
|
||||
## Plan
|
||||
|
||||
- [ ] Required common (runtime/SQL) infrastructure
|
||||
- [-] Refactor into a session based filter
|
||||
- [-] Keep a mirror of identities for session
|
||||
- [-] Capture events (new downloaded and pending first)
|
||||
- [-] Have the simplest filter to handle new and updated and emit wallet event
|
||||
- [ ] Handle update filter events in UX and alter the model (add/remove)
|
||||
- [ ] Asses how the runtime filter grows in complexity/risk
|
||||
- [ ] Quick prototype of SQL only filter if still make sense
|
||||
- [ ] Refactor the async handling to fit the session based better (use channels and goroutine)
|
||||
|
||||
## How to
|
||||
|
||||
I see two ways:
|
||||
|
||||
- Keep a **runtime** (go/nim) dynamic in memory filter that is in sync with the SQL filter and use the filter to process transactions updates and propagate to the current visualized model
|
||||
- The filter will push changes to the in memory model based on the sorting and filtering criteria
|
||||
- If the filter is completely in sync withe the SQL one, then the dynamic updates to the model should have the same content as fetched from scratch from the DB
|
||||
- *Advantages*
|
||||
- Less memory and performance requirements
|
||||
- *Disadvantages*
|
||||
- Two sources of truth for the filter
|
||||
- With tests for each event this can be mitigated
|
||||
- Complexity around the multi-transaction/sub-transaction relation
|
||||
- If we miss doing equivalent changes in bot filters (SQL and runtime) the filter might not be in sync with the SQL one and have errors in update
|
||||
- **Refresh SQL filter** on every transaction (or bulk) update to DB and compare with the current visualized filter to extract differences and push as change notifications
|
||||
- This approach is more expensive in terms of memory and performance but will use only one source of truth implementation
|
||||
- This way we know for sure that the updated model is in sync with a newly fetched one
|
||||
- *Advantages*
|
||||
- Less complexity and less risk to be out of sync with the SQL filter
|
||||
- *Disadvantages*
|
||||
- More memory and performance requirements
|
||||
- The real improvement will be to do the postponed refactoring of the activity in DB
|
||||
|
||||
## Requirements
|
||||
|
||||
Expected filter states to be addressed
|
||||
|
||||
- Filter is set
|
||||
- No Filter
|
||||
- Filter is cleared
|
||||
- How about if only partially cleared?
|
||||
|
||||
Expected dynamic events
|
||||
|
||||
- **New transactions**
|
||||
- Pending
|
||||
- Downloaded (external)
|
||||
- Multi-transactions?
|
||||
- **Transaction changed state**
|
||||
- Pending to confirmed (new transaction/removed transaction)
|
||||
|
||||
Filter criteria
|
||||
|
||||
- time interval: start-end
|
||||
- activity type (send/receive/buy/swap/bridge/contract_deploy/mint)
|
||||
- status (pending/failed/confirmed/finalized)
|
||||
- addresses
|
||||
- tokens
|
||||
- multi-transaction filtering transaction
|
||||
|
||||
## Implementation
|
||||
|
||||
### SQL filter
|
||||
|
||||
For new events
|
||||
|
||||
- keep a mirror of identities on status-go side (optional session based)
|
||||
- on update events fetch identities and check against the mirror if any is new
|
||||
- for new entries send the notification with the transaction details
|
||||
- keep pending changes (not added)
|
||||
- remove entries that were processed for this session
|
||||
|
||||
For update?
|
||||
|
||||
- check if entry is in the mirror and propagate update event
|
||||
|
||||
### Mirror filter
|
||||
|
||||
For new events
|
||||
|
||||
- keep a mirror of identities
|
||||
- on update events pass them through the filter and if they pass send updates
|
||||
- the filter checks criteria and available mirror interval to dismiss from mirror
|
||||
- sub-transactions challenge
|
||||
- TODO
|
||||
- token challenges
|
||||
- TODO
|
||||
|
||||
For update?
|
||||
|
||||
- check if entry is in the mirror and propagate update event
|
||||
54
vendor/github.com/status-im/status-go/services/wallet/activity/activity.go
generated
vendored
54
vendor/github.com/status-im/status-go/services/wallet/activity/activity.go
generated
vendored
@@ -52,7 +52,7 @@ const (
|
||||
type Entry struct {
|
||||
payloadType PayloadType
|
||||
transaction *transfer.TransactionIdentity
|
||||
id transfer.MultiTransactionIDType
|
||||
id common.MultiTransactionIDType
|
||||
timestamp int64
|
||||
activityType Type
|
||||
activityStatus Status
|
||||
@@ -68,30 +68,32 @@ type Entry struct {
|
||||
chainIDIn *common.ChainID
|
||||
transferType *TransferType
|
||||
contractAddress *eth.Address
|
||||
communityID *string
|
||||
|
||||
isNew bool // isNew is used to indicate if the entry is newer than session start (changed state also)
|
||||
}
|
||||
|
||||
// Only used for JSON marshalling
|
||||
type EntryData struct {
|
||||
PayloadType PayloadType `json:"payloadType"`
|
||||
Transaction *transfer.TransactionIdentity `json:"transaction,omitempty"`
|
||||
ID *transfer.MultiTransactionIDType `json:"id,omitempty"`
|
||||
Timestamp *int64 `json:"timestamp,omitempty"`
|
||||
ActivityType *Type `json:"activityType,omitempty"`
|
||||
ActivityStatus *Status `json:"activityStatus,omitempty"`
|
||||
AmountOut *hexutil.Big `json:"amountOut,omitempty"`
|
||||
AmountIn *hexutil.Big `json:"amountIn,omitempty"`
|
||||
TokenOut *Token `json:"tokenOut,omitempty"`
|
||||
TokenIn *Token `json:"tokenIn,omitempty"`
|
||||
SymbolOut *string `json:"symbolOut,omitempty"`
|
||||
SymbolIn *string `json:"symbolIn,omitempty"`
|
||||
Sender *eth.Address `json:"sender,omitempty"`
|
||||
Recipient *eth.Address `json:"recipient,omitempty"`
|
||||
ChainIDOut *common.ChainID `json:"chainIdOut,omitempty"`
|
||||
ChainIDIn *common.ChainID `json:"chainIdIn,omitempty"`
|
||||
TransferType *TransferType `json:"transferType,omitempty"`
|
||||
ContractAddress *eth.Address `json:"contractAddress,omitempty"`
|
||||
PayloadType PayloadType `json:"payloadType"`
|
||||
Transaction *transfer.TransactionIdentity `json:"transaction,omitempty"`
|
||||
ID *common.MultiTransactionIDType `json:"id,omitempty"`
|
||||
Timestamp *int64 `json:"timestamp,omitempty"`
|
||||
ActivityType *Type `json:"activityType,omitempty"`
|
||||
ActivityStatus *Status `json:"activityStatus,omitempty"`
|
||||
AmountOut *hexutil.Big `json:"amountOut,omitempty"`
|
||||
AmountIn *hexutil.Big `json:"amountIn,omitempty"`
|
||||
TokenOut *Token `json:"tokenOut,omitempty"`
|
||||
TokenIn *Token `json:"tokenIn,omitempty"`
|
||||
SymbolOut *string `json:"symbolOut,omitempty"`
|
||||
SymbolIn *string `json:"symbolIn,omitempty"`
|
||||
Sender *eth.Address `json:"sender,omitempty"`
|
||||
Recipient *eth.Address `json:"recipient,omitempty"`
|
||||
ChainIDOut *common.ChainID `json:"chainIdOut,omitempty"`
|
||||
ChainIDIn *common.ChainID `json:"chainIdIn,omitempty"`
|
||||
TransferType *TransferType `json:"transferType,omitempty"`
|
||||
ContractAddress *eth.Address `json:"contractAddress,omitempty"`
|
||||
CommunityID *string `json:"communityId,omitempty"`
|
||||
|
||||
IsNew *bool `json:"isNew,omitempty"`
|
||||
|
||||
@@ -116,6 +118,7 @@ func (e *Entry) MarshalJSON() ([]byte, error) {
|
||||
ChainIDIn: e.chainIDIn,
|
||||
TransferType: e.transferType,
|
||||
ContractAddress: e.contractAddress,
|
||||
CommunityID: e.communityID,
|
||||
}
|
||||
|
||||
if e.payloadType == MultiTransactionPT {
|
||||
@@ -162,6 +165,7 @@ func (e *Entry) UnmarshalJSON(data []byte) error {
|
||||
e.chainIDOut = aux.ChainIDOut
|
||||
e.chainIDIn = aux.ChainIDIn
|
||||
e.transferType = aux.TransferType
|
||||
e.communityID = aux.CommunityID
|
||||
|
||||
e.isNew = aux.IsNew != nil && *aux.IsNew
|
||||
|
||||
@@ -192,7 +196,7 @@ func newActivityEntryWithTransaction(pending bool, transaction *transfer.Transac
|
||||
}
|
||||
}
|
||||
|
||||
func NewActivityEntryWithMultiTransaction(id transfer.MultiTransactionIDType, timestamp int64, activityType Type, activityStatus Status) Entry {
|
||||
func NewActivityEntryWithMultiTransaction(id common.MultiTransactionIDType, timestamp int64, activityType Type, activityStatus Status) Entry {
|
||||
return Entry{
|
||||
payloadType: MultiTransactionPT,
|
||||
id: id,
|
||||
@@ -510,14 +514,14 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
||||
dbPTrAmount := new(big.Int)
|
||||
var dbMtFromAmount, dbMtToAmount, contractType sql.NullString
|
||||
var tokenCode, fromTokenCode, toTokenCode sql.NullString
|
||||
var methodHash sql.NullString
|
||||
var methodHash, communityID sql.NullString
|
||||
var transferType *TransferType
|
||||
var communityMintEventDB sql.NullBool
|
||||
var communityMintEvent bool
|
||||
err := rows.Scan(&transferHash, &pendingHash, &chainID, &multiTxID, ×tamp, &dbMtType, &dbTrType, &fromAddress,
|
||||
&toAddressDB, &ownerAddressDB, &dbTrAmount, (*bigint.SQLBigIntBytes)(dbPTrAmount), &dbMtFromAmount, &dbMtToAmount, &aggregatedStatus, &aggregatedCount,
|
||||
&tokenAddress, &dbTokenID, &tokenCode, &fromTokenCode, &toTokenCode, &outChainIDDB, &inChainIDDB, &contractType,
|
||||
&contractAddressDB, &methodHash, &communityMintEventDB)
|
||||
&contractAddressDB, &methodHash, &communityMintEventDB, &communityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -656,7 +660,7 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
||||
*inChainID = common.ChainID(inChainIDDB.Int64)
|
||||
}
|
||||
|
||||
entry = NewActivityEntryWithMultiTransaction(transfer.MultiTransactionIDType(multiTxID.Int64),
|
||||
entry = NewActivityEntryWithMultiTransaction(common.MultiTransactionIDType(multiTxID.Int64),
|
||||
timestamp, activityType, activityStatus)
|
||||
|
||||
// Extract tokens
|
||||
@@ -676,6 +680,10 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
||||
return nil, errors.New("invalid row data")
|
||||
}
|
||||
|
||||
if communityID.Valid {
|
||||
entry.communityID = common.NewAndSet(communityID.String)
|
||||
}
|
||||
|
||||
// Complete common data
|
||||
entry.recipient = &toAddress
|
||||
entry.sender = &fromAddress
|
||||
|
||||
12
vendor/github.com/status-im/status-go/services/wallet/activity/filter.go
generated
vendored
12
vendor/github.com/status-im/status-go/services/wallet/activity/filter.go
generated
vendored
@@ -99,6 +99,18 @@ type Filter struct {
|
||||
FilterOutCollectibles bool `json:"filterOutCollectibles"`
|
||||
}
|
||||
|
||||
func (f *Filter) IsEmpty() bool {
|
||||
return f.Period.StartTimestamp == NoLimitTimestampForPeriod &&
|
||||
f.Period.EndTimestamp == NoLimitTimestampForPeriod &&
|
||||
len(f.Types) == 0 &&
|
||||
len(f.Statuses) == 0 &&
|
||||
len(f.CounterpartyAddresses) == 0 &&
|
||||
len(f.Assets) == 0 &&
|
||||
len(f.Collectibles) == 0 &&
|
||||
!f.FilterOutAssets &&
|
||||
!f.FilterOutCollectibles
|
||||
}
|
||||
|
||||
func GetRecipients(ctx context.Context, db *sql.DB, chainIDs []common.ChainID, addresses []eth.Address, offset int, limit int) (recipients []eth.Address, hasMore bool, err error) {
|
||||
filterAllAddresses := len(addresses) == 0
|
||||
involvedAddresses := noEntriesInTmpTableSQLValues
|
||||
|
||||
24
vendor/github.com/status-im/status-go/services/wallet/activity/filter.sql
generated
vendored
24
vendor/github.com/status-im/status-go/services/wallet/activity/filter.sql
generated
vendored
@@ -198,7 +198,10 @@ SELECT
|
||||
END AS agg_status,
|
||||
1 AS agg_count,
|
||||
transfers.token_address AS token_address,
|
||||
transfers.token_id AS token_id,
|
||||
CASE
|
||||
WHEN LENGTH(transfers.token_id) = 0 THEN X'00'
|
||||
ELSE transfers.token_id
|
||||
END AS tmp_token_id,
|
||||
NULL AS token_code,
|
||||
NULL AS from_token_code,
|
||||
NULL AS to_token_code,
|
||||
@@ -213,7 +216,12 @@ SELECT
|
||||
CASE
|
||||
WHEN transfers.tx_from_address = zeroAddress AND transfers.type = "erc20" THEN (SELECT 1 FROM json_each(transfers.receipt, '$.logs' ) WHERE json_extract( value, '$.topics[0]' ) = communityMintEvent)
|
||||
ELSE NULL
|
||||
END AS community_mint_event
|
||||
END AS community_mint_event,
|
||||
CASE
|
||||
WHEN transfers.type = 'erc20' THEN (SELECT community_id FROM tokens WHERE transfers.token_address = tokens.address AND transfers.network_id = tokens.network_id)
|
||||
WHEN transfers.type = 'erc721' OR transfers.type = 'erc1155' THEN (SELECT community_id FROM collectible_data_cache WHERE transfers.token_address = collectible_data_cache.contract_address AND transfers.network_id = collectible_data_cache.chain_id)
|
||||
ELSE NULL
|
||||
END AS community_id
|
||||
FROM
|
||||
transfers
|
||||
CROSS JOIN filter_conditions
|
||||
@@ -323,7 +331,7 @@ WHERE
|
||||
AND (
|
||||
(
|
||||
transfers.network_id,
|
||||
transfers.token_id,
|
||||
tmp_token_id,
|
||||
transfers.token_address
|
||||
) IN assets_erc721
|
||||
)
|
||||
@@ -373,7 +381,7 @@ SELECT
|
||||
statusPending AS agg_status,
|
||||
1 AS agg_count,
|
||||
NULL AS token_address,
|
||||
NULL AS token_id,
|
||||
NULL AS tmp_token_id,
|
||||
pending_transactions.symbol AS token_code,
|
||||
NULL AS from_token_code,
|
||||
NULL AS to_token_code,
|
||||
@@ -382,7 +390,8 @@ SELECT
|
||||
pending_transactions.type AS type,
|
||||
NULL as contract_address,
|
||||
NULL AS method_hash,
|
||||
NULL AS community_mint_event
|
||||
NULL AS community_mint_event,
|
||||
NULL AS community_id
|
||||
FROM
|
||||
pending_transactions
|
||||
CROSS JOIN filter_conditions
|
||||
@@ -465,7 +474,7 @@ SELECT
|
||||
END AS agg_status,
|
||||
COALESCE(tr_status.count, 0) + COALESCE(pending_status.count, 0) AS agg_count,
|
||||
NULL AS token_address,
|
||||
NULL AS token_id,
|
||||
NULL AS tmp_token_id,
|
||||
NULL AS token_code,
|
||||
multi_transactions.from_asset AS from_token_code,
|
||||
multi_transactions.to_asset AS to_token_code,
|
||||
@@ -474,7 +483,8 @@ SELECT
|
||||
NULL AS type,
|
||||
NULL as contract_address,
|
||||
NULL AS method_hash,
|
||||
NULL AS community_mint_event
|
||||
NULL AS community_mint_event,
|
||||
NULL AS community_id
|
||||
FROM
|
||||
multi_transactions
|
||||
CROSS JOIN filter_conditions
|
||||
|
||||
6
vendor/github.com/status-im/status-go/services/wallet/activity/service.go
generated
vendored
6
vendor/github.com/status-im/status-go/services/wallet/activity/service.go
generated
vendored
@@ -68,9 +68,9 @@ type Service struct {
|
||||
subscriptions event.Subscription
|
||||
ch chan walletevent.Event
|
||||
// sessionsRWMutex is used to protect all sessions related members
|
||||
sessionsRWMutex sync.RWMutex
|
||||
sessionsRWMutex sync.RWMutex
|
||||
debounceDuration time.Duration
|
||||
|
||||
// TODO #12120: sort out session dependencies
|
||||
pendingTracker *transactions.PendingTxTracker
|
||||
}
|
||||
|
||||
@@ -87,6 +87,8 @@ func NewService(db *sql.DB, tokenManager token.ManagerInterface, collectibles co
|
||||
scheduler: async.NewMultiClientScheduler(),
|
||||
|
||||
sessions: make(map[SessionID]*Session),
|
||||
// here to be overwritten by tests
|
||||
debounceDuration: 1 * time.Second,
|
||||
|
||||
pendingTracker: pendingTracker,
|
||||
}
|
||||
|
||||
334
vendor/github.com/status-im/status-go/services/wallet/activity/session.go
generated
vendored
334
vendor/github.com/status-im/status-go/services/wallet/activity/session.go
generated
vendored
@@ -4,8 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
"time"
|
||||
|
||||
eth "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
@@ -22,14 +21,17 @@ const nilStr = "nil"
|
||||
type EntryIdentity struct {
|
||||
payloadType PayloadType
|
||||
transaction *transfer.TransactionIdentity
|
||||
id transfer.MultiTransactionIDType
|
||||
id common.MultiTransactionIDType
|
||||
}
|
||||
|
||||
// func (e EntryIdentity) same(a EntryIdentity) bool {
|
||||
// return a.payloadType == e.payloadType && (a.transaction == e.transaction && (a.transaction == nil || (a.transaction.ChainID == e.transaction.ChainID &&
|
||||
// a.transaction.Hash == e.transaction.Hash &&
|
||||
// a.transaction.Address == e.transaction.Address))) && a.id == e.id
|
||||
// }
|
||||
func (e EntryIdentity) same(a EntryIdentity) bool {
|
||||
return a.payloadType == e.payloadType &&
|
||||
((a.transaction == nil && e.transaction == nil) ||
|
||||
(a.transaction.ChainID == e.transaction.ChainID &&
|
||||
a.transaction.Hash == e.transaction.Hash &&
|
||||
a.transaction.Address == e.transaction.Address)) &&
|
||||
a.id == e.id
|
||||
}
|
||||
|
||||
func (e EntryIdentity) key() string {
|
||||
txID := nilStr
|
||||
@@ -41,6 +43,13 @@ func (e EntryIdentity) key() string {
|
||||
|
||||
type SessionID int32
|
||||
|
||||
// Session stores state related to a filter session
|
||||
// The user happy flow is:
|
||||
// 1. StartFilterSession to get a new SessionID and client be notified by the current state
|
||||
// 2. GetMoreForFilterSession anytime to get more entries after the first page
|
||||
// 3. UpdateFilterForSession to update the filter and get the new state or clean the filter and get the newer entries
|
||||
// 4. ResetFilterSession in case client receives SessionUpdate with HasNewOnTop = true to get the latest state
|
||||
// 5. StopFilterSession to stop the session when no used (user changed from activity screens or changed addresses and chains)
|
||||
type Session struct {
|
||||
id SessionID
|
||||
|
||||
@@ -53,15 +62,22 @@ type Session struct {
|
||||
|
||||
// model is a mirror of the data model presentation has (sent by EventActivityFilteringDone)
|
||||
model []EntryIdentity
|
||||
// noFilterModel is a mirror of the data model presentation has when filter is empty
|
||||
noFilterModel map[string]EntryIdentity
|
||||
// new holds the new entries until user requests update by calling ResetFilterSession
|
||||
new []EntryIdentity
|
||||
}
|
||||
|
||||
type EntryUpdate struct {
|
||||
Pos int `json:"pos"`
|
||||
Entry *Entry `json:"entry"`
|
||||
}
|
||||
|
||||
// SessionUpdate payload for EventActivitySessionUpdated
|
||||
type SessionUpdate struct {
|
||||
HasNewEntries *bool `json:"hasNewEntries,omitempty"`
|
||||
Removed []EntryIdentity `json:"removed,omitempty"`
|
||||
Updated []Entry `json:"updated,omitempty"`
|
||||
HasNewOnTop *bool `json:"hasNewOnTop,omitempty"`
|
||||
New []*EntryUpdate `json:"new,omitempty"`
|
||||
Removed []EntryIdentity `json:"removed,omitempty"`
|
||||
}
|
||||
|
||||
type fullFilterParams struct {
|
||||
@@ -99,21 +115,44 @@ func (s *Service) internalFilter(f fullFilterParams, offset int, count int, proc
|
||||
})
|
||||
}
|
||||
|
||||
// mirrorIdentities for update use
|
||||
func mirrorIdentities(entries []Entry) []EntryIdentity {
|
||||
model := make([]EntryIdentity, 0, len(entries))
|
||||
for _, a := range entries {
|
||||
model = append(model, EntryIdentity{
|
||||
payloadType: a.payloadType,
|
||||
transaction: a.transaction,
|
||||
id: a.id,
|
||||
})
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
func (s *Service) internalFilterForSession(session *Session, firstPageCount int) {
|
||||
s.internalFilter(
|
||||
fullFilterParams{
|
||||
sessionID: session.id,
|
||||
addresses: session.addresses,
|
||||
allAddresses: session.allAddresses,
|
||||
chainIDs: session.chainIDs,
|
||||
filter: session.filter,
|
||||
},
|
||||
0,
|
||||
firstPageCount,
|
||||
func(entries []Entry) (offset int) {
|
||||
s.sessionsRWMutex.Lock()
|
||||
defer s.sessionsRWMutex.Unlock()
|
||||
|
||||
session.model = mirrorIdentities(entries)
|
||||
|
||||
return 0
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (s *Service) StartFilterSession(addresses []eth.Address, allAddresses bool, chainIDs []common.ChainID, filter Filter, firstPageCount int) SessionID {
|
||||
sessionID := s.nextSessionID()
|
||||
|
||||
// TODO #12120: sort rest of the filters
|
||||
// TODO #12120: prettyfy this
|
||||
slices.SortFunc(addresses, func(a eth.Address, b eth.Address) bool {
|
||||
return a.Hex() < b.Hex()
|
||||
})
|
||||
slices.Sort(chainIDs)
|
||||
slices.SortFunc(filter.CounterpartyAddresses, func(a eth.Address, b eth.Address) bool {
|
||||
return a.Hex() < b.Hex()
|
||||
})
|
||||
|
||||
s.sessionsRWMutex.Lock()
|
||||
subscribeToEvents := len(s.sessions) == 0
|
||||
session := &Session{
|
||||
id: sessionID,
|
||||
|
||||
@@ -124,6 +163,10 @@ func (s *Service) StartFilterSession(addresses []eth.Address, allAddresses bool,
|
||||
|
||||
model: make([]EntryIdentity, 0, firstPageCount),
|
||||
}
|
||||
|
||||
s.sessionsRWMutex.Lock()
|
||||
subscribeToEvents := len(s.sessions) == 0
|
||||
|
||||
s.sessions[sessionID] = session
|
||||
|
||||
if subscribeToEvents {
|
||||
@@ -131,36 +174,81 @@ func (s *Service) StartFilterSession(addresses []eth.Address, allAddresses bool,
|
||||
}
|
||||
s.sessionsRWMutex.Unlock()
|
||||
|
||||
s.internalFilter(
|
||||
fullFilterParams{
|
||||
sessionID: sessionID,
|
||||
addresses: addresses,
|
||||
allAddresses: allAddresses,
|
||||
chainIDs: chainIDs,
|
||||
filter: filter,
|
||||
},
|
||||
0,
|
||||
firstPageCount,
|
||||
func(entries []Entry) (offset int) {
|
||||
// Mirror identities for update use
|
||||
s.sessionsRWMutex.Lock()
|
||||
defer s.sessionsRWMutex.Unlock()
|
||||
|
||||
session.model = make([]EntryIdentity, 0, len(entries))
|
||||
for _, a := range entries {
|
||||
session.model = append(session.model, EntryIdentity{
|
||||
payloadType: a.payloadType,
|
||||
transaction: a.transaction,
|
||||
id: a.id,
|
||||
})
|
||||
}
|
||||
return 0
|
||||
},
|
||||
)
|
||||
s.internalFilterForSession(session, firstPageCount)
|
||||
|
||||
return sessionID
|
||||
}
|
||||
|
||||
// UpdateFilterForSession is to be called for updating the filter of a specific session
|
||||
// After calling this method to set a filter all the incoming changes will be reported with
|
||||
// Entry.isNew = true when filter is reset to empty
|
||||
func (s *Service) UpdateFilterForSession(id SessionID, filter Filter, firstPageCount int) error {
|
||||
s.sessionsRWMutex.RLock()
|
||||
session, found := s.sessions[id]
|
||||
if !found {
|
||||
s.sessionsRWMutex.RUnlock()
|
||||
return errors.New("session not found")
|
||||
}
|
||||
|
||||
prevFilterEmpty := session.filter.IsEmpty()
|
||||
newFilerEmpty := filter.IsEmpty()
|
||||
s.sessionsRWMutex.RUnlock()
|
||||
|
||||
s.sessionsRWMutex.Lock()
|
||||
|
||||
session.new = nil
|
||||
|
||||
session.filter = filter
|
||||
|
||||
if prevFilterEmpty && !newFilerEmpty {
|
||||
// Session is moving from empty to non-empty filter
|
||||
// Take a snapshot of the current model
|
||||
session.noFilterModel = entryIdsToMap(session.model)
|
||||
|
||||
session.model = make([]EntryIdentity, 0, firstPageCount)
|
||||
|
||||
// In this case there is nothing to flag so we request the first page
|
||||
s.internalFilterForSession(session, firstPageCount)
|
||||
} else if !prevFilterEmpty && newFilerEmpty {
|
||||
// Session is moving from non-empty to empty filter
|
||||
// In this case we need to flag all the new entries that are not in the noFilterModel
|
||||
s.internalFilter(
|
||||
fullFilterParams{
|
||||
sessionID: session.id,
|
||||
addresses: session.addresses,
|
||||
allAddresses: session.allAddresses,
|
||||
chainIDs: session.chainIDs,
|
||||
filter: session.filter,
|
||||
},
|
||||
0,
|
||||
firstPageCount,
|
||||
func(entries []Entry) (offset int) {
|
||||
s.sessionsRWMutex.Lock()
|
||||
defer s.sessionsRWMutex.Unlock()
|
||||
|
||||
// Mark new entries
|
||||
for i, a := range entries {
|
||||
_, found := session.noFilterModel[a.getIdentity().key()]
|
||||
entries[i].isNew = !found
|
||||
}
|
||||
|
||||
// Mirror identities for update use
|
||||
session.model = mirrorIdentities(entries)
|
||||
session.noFilterModel = nil
|
||||
return 0
|
||||
},
|
||||
)
|
||||
} else {
|
||||
// Else act as a normal filter update
|
||||
s.internalFilterForSession(session, firstPageCount)
|
||||
}
|
||||
s.sessionsRWMutex.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResetFilterSession is to be called when SessionUpdate.HasNewOnTop == true to
|
||||
// update client with the latest state including new on top entries
|
||||
func (s *Service) ResetFilterSession(id SessionID, firstPageCount int) error {
|
||||
session, found := s.sessions[id]
|
||||
if !found {
|
||||
@@ -189,15 +277,16 @@ func (s *Service) ResetFilterSession(id SessionID, firstPageCount int) error {
|
||||
}
|
||||
session.new = nil
|
||||
|
||||
// Mirror client identities for checking updates
|
||||
session.model = make([]EntryIdentity, 0, len(entries))
|
||||
for _, a := range entries {
|
||||
session.model = append(session.model, EntryIdentity{
|
||||
payloadType: a.payloadType,
|
||||
transaction: a.transaction,
|
||||
id: a.id,
|
||||
})
|
||||
if session.noFilterModel != nil {
|
||||
// Add reported new entries to mark them as seen
|
||||
for _, a := range newMap {
|
||||
session.noFilterModel[a.key()] = a
|
||||
}
|
||||
}
|
||||
|
||||
// Mirror client identities for checking updates
|
||||
session.model = mirrorIdentities(entries)
|
||||
|
||||
return 0
|
||||
},
|
||||
)
|
||||
@@ -248,55 +337,91 @@ func (s *Service) subscribeToEvents() {
|
||||
go s.processEvents()
|
||||
}
|
||||
|
||||
// TODO #12120: check that it exits on channel close
|
||||
// processEvents runs only if more than one session is active
|
||||
func (s *Service) processEvents() {
|
||||
eventCount := 0
|
||||
lastUpdate := time.Now().UnixMilli()
|
||||
for event := range s.ch {
|
||||
// TODO #12120: process rest of the events
|
||||
// TODO #12120: debounce for 1s
|
||||
if event.Type == transactions.EventPendingTransactionUpdate {
|
||||
for sessionID := range s.sessions {
|
||||
session := s.sessions[sessionID]
|
||||
activities, err := getActivityEntries(context.Background(), s.getDeps(), session.addresses, session.allAddresses, session.chainIDs, session.filter, 0, len(session.model))
|
||||
if err != nil {
|
||||
log.Error("Error getting activity entries", "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
s.sessionsRWMutex.RLock()
|
||||
allData := append(session.model, session.new...)
|
||||
new, _ /*removed*/ := findUpdates(allData, activities)
|
||||
s.sessionsRWMutex.RUnlock()
|
||||
|
||||
s.sessionsRWMutex.Lock()
|
||||
lastProcessed := -1
|
||||
for i, idRes := range new {
|
||||
if i-lastProcessed > 1 {
|
||||
// The events are not continuous, therefore these are not on top but mixed between existing entries
|
||||
break
|
||||
}
|
||||
lastProcessed = idRes.newPos
|
||||
// TODO #12120: make it more generic to follow the detection function
|
||||
// TODO #12120: hold the first few and only send mixed and removed
|
||||
if session.new == nil {
|
||||
session.new = make([]EntryIdentity, 0, len(new))
|
||||
}
|
||||
session.new = append(session.new, idRes.id)
|
||||
}
|
||||
|
||||
// TODO #12120: mixed
|
||||
|
||||
s.sessionsRWMutex.Unlock()
|
||||
|
||||
go notify(s.eventFeed, sessionID, len(session.new) > 0)
|
||||
}
|
||||
if event.Type == transactions.EventPendingTransactionUpdate ||
|
||||
event.Type == transactions.EventPendingTransactionStatusChanged ||
|
||||
event.Type == transfer.EventNewTransfers {
|
||||
eventCount++
|
||||
}
|
||||
// debounce events updates
|
||||
if eventCount > 0 &&
|
||||
(time.Duration(time.Now().UnixMilli()-lastUpdate)*time.Millisecond) >= s.debounceDuration {
|
||||
s.detectNew(eventCount)
|
||||
eventCount = 0
|
||||
lastUpdate = time.Now().UnixMilli()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func notify(eventFeed *event.Feed, id SessionID, hasNewEntries bool) {
|
||||
payload := SessionUpdate{}
|
||||
if hasNewEntries {
|
||||
payload.HasNewEntries = &hasNewEntries
|
||||
func (s *Service) detectNew(changeCount int) {
|
||||
for sessionID := range s.sessions {
|
||||
session := s.sessions[sessionID]
|
||||
|
||||
fetchLen := len(session.model) + changeCount
|
||||
activities, err := getActivityEntries(context.Background(), s.getDeps(), session.addresses, session.allAddresses, session.chainIDs, session.filter, 0, fetchLen)
|
||||
if err != nil {
|
||||
log.Error("Error getting activity entries", "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
s.sessionsRWMutex.RLock()
|
||||
allData := append(session.new, session.model...)
|
||||
new, _ /*removed*/ := findUpdates(allData, activities)
|
||||
s.sessionsRWMutex.RUnlock()
|
||||
|
||||
s.sessionsRWMutex.Lock()
|
||||
lastProcessed := -1
|
||||
onTop := true
|
||||
var mixed []*EntryUpdate
|
||||
for i, idRes := range new {
|
||||
// Detect on top
|
||||
if onTop {
|
||||
// mixedIdentityResult.newPos includes session.new, therefore compensate for it
|
||||
if ((idRes.newPos - len(session.new)) - lastProcessed) > 1 {
|
||||
// From now on the events are not on top and continuous but mixed between existing entries
|
||||
onTop = false
|
||||
mixed = make([]*EntryUpdate, 0, len(new)-i)
|
||||
}
|
||||
lastProcessed = idRes.newPos
|
||||
}
|
||||
|
||||
if onTop {
|
||||
if session.new == nil {
|
||||
session.new = make([]EntryIdentity, 0, len(new))
|
||||
}
|
||||
session.new = append(session.new, idRes.id)
|
||||
} else {
|
||||
modelPos := idRes.newPos - len(session.new)
|
||||
entry := activities[idRes.newPos]
|
||||
entry.isNew = true
|
||||
mixed = append(mixed, &EntryUpdate{
|
||||
Pos: modelPos,
|
||||
Entry: &entry,
|
||||
})
|
||||
// Insert in session model at modelPos index
|
||||
session.model = append(session.model[:modelPos], append([]EntryIdentity{{payloadType: entry.payloadType, transaction: entry.transaction, id: entry.id}}, session.model[modelPos:]...)...)
|
||||
}
|
||||
}
|
||||
|
||||
s.sessionsRWMutex.Unlock()
|
||||
|
||||
if len(session.new) > 0 || len(mixed) > 0 {
|
||||
go notify(s.eventFeed, sessionID, len(session.new) > 0, mixed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func notify(eventFeed *event.Feed, id SessionID, hasNewOnTop bool, mixed []*EntryUpdate) {
|
||||
payload := SessionUpdate{
|
||||
New: mixed,
|
||||
}
|
||||
|
||||
if hasNewOnTop {
|
||||
payload.HasNewOnTop = &hasNewOnTop
|
||||
}
|
||||
|
||||
sendResponseEvent(eventFeed, (*int32)(&id), EventActivitySessionUpdated, payload, nil)
|
||||
@@ -305,6 +430,8 @@ func notify(eventFeed *event.Feed, id SessionID, hasNewEntries bool) {
|
||||
// unsubscribeFromEvents should be called with sessionsRWMutex locked for writing
|
||||
func (s *Service) unsubscribeFromEvents() {
|
||||
s.subscriptions.Unsubscribe()
|
||||
close(s.ch)
|
||||
s.ch = nil
|
||||
s.subscriptions = nil
|
||||
}
|
||||
|
||||
@@ -369,6 +496,9 @@ func entriesToMap(entries []Entry) map[string]Entry {
|
||||
//
|
||||
// implementation assumes the order of each identity doesn't change from old state (identities) and new state (updated); we have either add or removed.
|
||||
func findUpdates(identities []EntryIdentity, updated []Entry) (new []mixedIdentityResult, removed []EntryIdentity) {
|
||||
if len(updated) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
idsMap := entryIdsToMap(identities)
|
||||
updatedMap := entriesToMap(updated)
|
||||
@@ -381,6 +511,10 @@ func findUpdates(identities []EntryIdentity, updated []Entry) (new []mixedIdenti
|
||||
id: id,
|
||||
})
|
||||
}
|
||||
|
||||
if len(identities) > 0 && entry.getIdentity().same(identities[len(identities)-1]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Account for new entries
|
||||
|
||||
24
vendor/github.com/status-im/status-go/services/wallet/api.go
generated
vendored
24
vendor/github.com/status-im/status-go/services/wallet/api.go
generated
vendored
@@ -316,6 +316,10 @@ func (api *API) FetchBalancesByOwnerAndContractAddress(ctx context.Context, chai
|
||||
return api.s.collectiblesManager.FetchBalancesByOwnerAndContractAddress(ctx, chainID, ownerAddress, contractAddresses)
|
||||
}
|
||||
|
||||
func (api *API) GetCollectibleOwnership(id thirdparty.CollectibleUniqueID) ([]thirdparty.AccountBalance, error) {
|
||||
return api.s.collectiblesManager.GetCollectibleOwnership(id)
|
||||
}
|
||||
|
||||
func (api *API) RefetchOwnedCollectibles() error {
|
||||
log.Debug("wallet.api.RefetchOwnedCollectibles")
|
||||
|
||||
@@ -342,6 +346,16 @@ func (api *API) GetCollectibleOwnersByContractAddress(ctx context.Context, chain
|
||||
return api.s.collectiblesManager.FetchCollectibleOwnersByContractAddress(ctx, chainID, contractAddress)
|
||||
}
|
||||
|
||||
func (api *API) SearchCollectibles(ctx context.Context, chainID wcommon.ChainID, text string, cursor string, limit int, providerID string) (*thirdparty.FullCollectibleDataContainer, error) {
|
||||
log.Debug("call to SearchCollectibles")
|
||||
return api.s.collectiblesManager.SearchCollectibles(ctx, chainID, text, cursor, limit, providerID)
|
||||
}
|
||||
|
||||
func (api *API) SearchCollections(ctx context.Context, chainID wcommon.ChainID, text string, cursor string, limit int, providerID string) (*thirdparty.CollectionDataContainer, error) {
|
||||
log.Debug("call to SearchCollections")
|
||||
return api.s.collectiblesManager.SearchCollections(ctx, chainID, text, cursor, limit, providerID)
|
||||
}
|
||||
|
||||
/*
|
||||
Collectibles API End
|
||||
*/
|
||||
@@ -567,7 +581,7 @@ func (api *API) ProceedWithTransactionsSignatures(ctx context.Context, signature
|
||||
return api.s.transactionManager.ProceedWithTransactionsSignatures(ctx, signatures)
|
||||
}
|
||||
|
||||
func (api *API) GetMultiTransactions(ctx context.Context, transactionIDs []transfer.MultiTransactionIDType) ([]*transfer.MultiTransaction, error) {
|
||||
func (api *API) GetMultiTransactions(ctx context.Context, transactionIDs []wcommon.MultiTransactionIDType) ([]*transfer.MultiTransaction, error) {
|
||||
log.Debug("wallet.api.GetMultiTransactions", "IDs.len", len(transactionIDs))
|
||||
return api.s.transactionManager.GetMultiTransactions(ctx, transactionIDs)
|
||||
}
|
||||
@@ -582,6 +596,7 @@ func (api *API) FetchAllCurrencyFormats() (currency.FormatPerSymbol, error) {
|
||||
return api.s.currency.FetchAllCurrencyFormats()
|
||||
}
|
||||
|
||||
// @deprecated replaced by session APIs; see #12120
|
||||
func (api *API) FilterActivityAsync(requestID int32, addresses []common.Address, allAddresses bool, chainIDs []wcommon.ChainID, filter activity.Filter, offset int, limit int) error {
|
||||
log.Debug("wallet.api.FilterActivityAsync", "requestID", requestID, "addr.count", len(addresses), "allAddresses", allAddresses, "chainIDs.count", len(chainIDs), "offset", offset, "limit", limit)
|
||||
|
||||
@@ -589,6 +604,7 @@ func (api *API) FilterActivityAsync(requestID int32, addresses []common.Address,
|
||||
return nil
|
||||
}
|
||||
|
||||
// @deprecated replaced by session APIs; see #12120
|
||||
func (api *API) CancelActivityFilterTask(requestID int32) error {
|
||||
log.Debug("wallet.api.CancelActivityFilterTask", "requestID", requestID)
|
||||
|
||||
@@ -602,6 +618,12 @@ func (api *API) StartActivityFilterSession(addresses []common.Address, allAddres
|
||||
return api.s.activity.StartFilterSession(addresses, allAddresses, chainIDs, filter, firstPageCount), nil
|
||||
}
|
||||
|
||||
func (api *API) UpdateActivityFilterForSession(sessionID activity.SessionID, filter activity.Filter, firstPageCount int) error {
|
||||
log.Debug("wallet.api.UpdateActivityFilterForSession", "sessionID", sessionID, "firstPageCount", firstPageCount)
|
||||
|
||||
return api.s.activity.UpdateFilterForSession(sessionID, filter, firstPageCount)
|
||||
}
|
||||
|
||||
func (api *API) ResetActivityFilterSession(id activity.SessionID, firstPageCount int) error {
|
||||
log.Debug("wallet.api.ResetActivityFilterSession", "id", id, "firstPageCount", firstPageCount)
|
||||
|
||||
|
||||
4
vendor/github.com/status-im/status-go/services/wallet/balance/balance_cache.go
generated
vendored
4
vendor/github.com/status-im/status-go/services/wallet/balance/balance_cache.go
generated
vendored
@@ -8,8 +8,6 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
||||
"github.com/status-im/status-go/rpc/chain"
|
||||
)
|
||||
|
||||
// Reader interface for reading balance at a specified address.
|
||||
@@ -17,7 +15,7 @@ type Reader interface {
|
||||
BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error)
|
||||
NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error)
|
||||
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
|
||||
FullTransactionByBlockNumberAndIndex(ctx context.Context, blockNumber *big.Int, index uint) (*chain.FullTransaction, error)
|
||||
CallBlockHashByTransaction(ctx context.Context, blockNumber *big.Int, index uint) (common.Hash, error)
|
||||
NetworkID() uint64
|
||||
}
|
||||
|
||||
|
||||
33
vendor/github.com/status-im/status-go/services/wallet/bigint/sql_big_int.go
generated
vendored
33
vendor/github.com/status-im/status-go/services/wallet/bigint/sql_big_int.go
generated
vendored
@@ -58,3 +58,36 @@ func (i *SQLBigIntBytes) Value() (driver.Value, error) {
|
||||
}
|
||||
return (*big.Int)(i).Bytes(), nil
|
||||
}
|
||||
|
||||
type NilableSQLBigInt struct {
|
||||
big.Int
|
||||
isNil bool
|
||||
}
|
||||
|
||||
func (i *NilableSQLBigInt) IsNil() bool {
|
||||
return i.isNil
|
||||
}
|
||||
|
||||
func (i *NilableSQLBigInt) SetNil() {
|
||||
i.isNil = true
|
||||
}
|
||||
|
||||
// Scan implements interface.
|
||||
func (i *NilableSQLBigInt) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
i.SetNil()
|
||||
return nil
|
||||
}
|
||||
val, ok := value.(int64)
|
||||
if !ok {
|
||||
return errors.New("not an integer")
|
||||
}
|
||||
|
||||
i.SetInt64(val)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Not implemented, used only for scanning
|
||||
func (i *NilableSQLBigInt) Value() (driver.Value, error) {
|
||||
return nil, errors.New("NilableSQLBigInt.Value is not implemented")
|
||||
}
|
||||
|
||||
1
vendor/github.com/status-im/status-go/services/wallet/bridge/bridge.go
generated
vendored
1
vendor/github.com/status-im/status-go/services/wallet/bridge/bridge.go
generated
vendored
@@ -106,4 +106,5 @@ type Bridge interface {
|
||||
Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (types.Hash, error)
|
||||
GetContractAddress(network *params.Network, token *token.Token) *common.Address
|
||||
BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Transaction, error)
|
||||
BuildTx(fromNetwork, toNetwork *params.Network, fromAddress common.Address, toAddress common.Address, token *token.Token, amountIn *big.Int, bonderFee *big.Int) (*ethTypes.Transaction, error)
|
||||
}
|
||||
|
||||
21
vendor/github.com/status-im/status-go/services/wallet/bridge/cbridge.go
generated
vendored
21
vendor/github.com/status-im/status-go/services/wallet/bridge/cbridge.go
generated
vendored
@@ -288,6 +288,27 @@ func (s *CBridge) EstimateGas(fromNetwork *params.Network, toNetwork *params.Net
|
||||
return uint64(increasedEstimation), nil
|
||||
}
|
||||
|
||||
func (s *CBridge) BuildTx(fromNetwork, toNetwork *params.Network, fromAddress common.Address, toAddress common.Address, token *token.Token, amountIn *big.Int, bonderFee *big.Int) (*ethTypes.Transaction, error) {
|
||||
toAddr := types.Address(toAddress)
|
||||
sendArgs := &TransactionBridge{
|
||||
CbridgeTx: &CBridgeTxArgs{
|
||||
SendTxArgs: transactions.SendTxArgs{
|
||||
From: types.Address(fromAddress),
|
||||
To: &toAddr,
|
||||
Value: (*hexutil.Big)(amountIn),
|
||||
Data: types.HexBytes("0x0"),
|
||||
},
|
||||
ChainID: toNetwork.ChainID,
|
||||
Symbol: token.Symbol,
|
||||
Recipient: toAddress,
|
||||
Amount: (*hexutil.Big)(amountIn),
|
||||
},
|
||||
ChainID: fromNetwork.ChainID,
|
||||
}
|
||||
|
||||
return s.BuildTransaction(sendArgs)
|
||||
}
|
||||
|
||||
func (s *CBridge) GetContractAddress(network *params.Network, token *token.Token) *common.Address {
|
||||
transferConfig, err := s.getTransferConfig(network.IsTest)
|
||||
if err != nil {
|
||||
|
||||
30
vendor/github.com/status-im/status-go/services/wallet/bridge/erc1155_transfer.go
generated
vendored
30
vendor/github.com/status-im/status-go/services/wallet/bridge/erc1155_transfer.go
generated
vendored
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
ethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/contracts/community-tokens/collectibles"
|
||||
"github.com/status-im/status-go/contracts/ierc1155"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/params"
|
||||
@@ -59,7 +58,7 @@ func (s *ERC1155TransferBridge) EstimateGas(fromNetwork *params.Network, toNetwo
|
||||
var input []byte
|
||||
value := new(big.Int)
|
||||
|
||||
abi, err := abi.JSON(strings.NewReader(collectibles.CollectiblesMetaData.ABI))
|
||||
abi, err := abi.JSON(strings.NewReader(ierc1155.Ierc1155ABI))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -102,6 +101,33 @@ func (s *ERC1155TransferBridge) EstimateGas(fromNetwork *params.Network, toNetwo
|
||||
return uint64(increasedEstimation), nil
|
||||
}
|
||||
|
||||
func (s *ERC1155TransferBridge) BuildTx(network, _ *params.Network, fromAddress common.Address, toAddress common.Address, token *token.Token, amountIn *big.Int, _ *big.Int) (*ethTypes.Transaction, error) {
|
||||
contractAddress := types.Address(token.Address)
|
||||
|
||||
// We store ERC1155 Token ID using big.Int.String() in token.Symbol
|
||||
tokenID, success := new(big.Int).SetString(token.Symbol, 10)
|
||||
if !success {
|
||||
return nil, fmt.Errorf("failed to convert ERC1155's Symbol %s to big.Int", token.Symbol)
|
||||
}
|
||||
|
||||
sendArgs := &TransactionBridge{
|
||||
ERC1155TransferTx: &ERC1155TransferTxArgs{
|
||||
SendTxArgs: transactions.SendTxArgs{
|
||||
From: types.Address(fromAddress),
|
||||
To: &contractAddress,
|
||||
Value: (*hexutil.Big)(amountIn),
|
||||
Data: types.HexBytes("0x0"),
|
||||
},
|
||||
TokenID: (*hexutil.Big)(tokenID),
|
||||
Recipient: toAddress,
|
||||
Amount: (*hexutil.Big)(amountIn),
|
||||
},
|
||||
ChainID: network.ChainID,
|
||||
}
|
||||
|
||||
return s.BuildTransaction(sendArgs)
|
||||
}
|
||||
|
||||
func (s *ERC1155TransferBridge) sendOrBuild(sendArgs *TransactionBridge, signerFn bind.SignerFn) (tx *ethTypes.Transaction, err error) {
|
||||
ethClient, err := s.rpcClient.EthClient(sendArgs.ChainID)
|
||||
if err != nil {
|
||||
|
||||
26
vendor/github.com/status-im/status-go/services/wallet/bridge/erc721_transfer.go
generated
vendored
26
vendor/github.com/status-im/status-go/services/wallet/bridge/erc721_transfer.go
generated
vendored
@@ -98,6 +98,32 @@ func (s *ERC721TransferBridge) EstimateGas(fromNetwork *params.Network, toNetwor
|
||||
return uint64(increasedEstimation), nil
|
||||
}
|
||||
|
||||
func (s *ERC721TransferBridge) BuildTx(network, _ *params.Network, fromAddress common.Address, toAddress common.Address, token *token.Token, amountIn *big.Int, _ *big.Int) (*ethTypes.Transaction, error) {
|
||||
contractAddress := types.Address(token.Address)
|
||||
|
||||
// We store ERC721 Token ID using big.Int.String() in token.Symbol
|
||||
tokenID, success := new(big.Int).SetString(token.Symbol, 10)
|
||||
if !success {
|
||||
return nil, fmt.Errorf("failed to convert ERC721's Symbol %s to big.Int", token.Symbol)
|
||||
}
|
||||
|
||||
sendArgs := &TransactionBridge{
|
||||
ERC721TransferTx: &ERC721TransferTxArgs{
|
||||
SendTxArgs: transactions.SendTxArgs{
|
||||
From: types.Address(fromAddress),
|
||||
To: &contractAddress,
|
||||
Value: (*hexutil.Big)(amountIn),
|
||||
Data: types.HexBytes("0x0"),
|
||||
},
|
||||
TokenID: (*hexutil.Big)(tokenID),
|
||||
Recipient: toAddress,
|
||||
},
|
||||
ChainID: network.ChainID,
|
||||
}
|
||||
|
||||
return s.BuildTransaction(sendArgs)
|
||||
}
|
||||
|
||||
func (s *ERC721TransferBridge) sendOrBuild(sendArgs *TransactionBridge, signerFn bind.SignerFn) (tx *ethTypes.Transaction, err error) {
|
||||
ethClient, err := s.rpcClient.EthClient(sendArgs.ChainID)
|
||||
if err != nil {
|
||||
|
||||
48
vendor/github.com/status-im/status-go/services/wallet/bridge/hop.go
generated
vendored
48
vendor/github.com/status-im/status-go/services/wallet/bridge/hop.go
generated
vendored
@@ -3,6 +3,7 @@ package bridge
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"strings"
|
||||
@@ -215,6 +216,28 @@ func (h *HopBridge) EstimateGas(fromNetwork *params.Network, toNetwork *params.N
|
||||
return uint64(increasedEstimation), nil
|
||||
}
|
||||
|
||||
func (h *HopBridge) BuildTx(fromNetwork, toNetwork *params.Network, fromAddress common.Address, toAddress common.Address, token *token.Token, amountIn *big.Int, bonderFee *big.Int) (*ethTypes.Transaction, error) {
|
||||
toAddr := types.Address(toAddress)
|
||||
sendArgs := &TransactionBridge{
|
||||
HopTx: &HopTxArgs{
|
||||
SendTxArgs: transactions.SendTxArgs{
|
||||
From: types.Address(fromAddress),
|
||||
To: &toAddr,
|
||||
Value: (*hexutil.Big)(amountIn),
|
||||
Data: types.HexBytes("0x0"),
|
||||
},
|
||||
Symbol: token.Symbol,
|
||||
Recipient: toAddress,
|
||||
Amount: (*hexutil.Big)(amountIn),
|
||||
BonderFee: (*hexutil.Big)(bonderFee),
|
||||
ChainID: toNetwork.ChainID,
|
||||
},
|
||||
ChainID: fromNetwork.ChainID,
|
||||
}
|
||||
|
||||
return h.BuildTransaction(sendArgs)
|
||||
}
|
||||
|
||||
func (h *HopBridge) GetContractAddress(network *params.Network, token *token.Token) *common.Address {
|
||||
var address common.Address
|
||||
if network.Layer == 1 {
|
||||
@@ -229,10 +252,10 @@ func (h *HopBridge) GetContractAddress(network *params.Network, token *token.Tok
|
||||
func (h *HopBridge) sendOrBuild(sendArgs *TransactionBridge, signerFn bind.SignerFn) (tx *ethTypes.Transaction, err error) {
|
||||
fromNetwork := h.contractMaker.RPCClient.NetworkManager.Find(sendArgs.ChainID)
|
||||
if fromNetwork == nil {
|
||||
return tx, err
|
||||
return tx, fmt.Errorf("ChainID not supported %d", sendArgs.ChainID)
|
||||
}
|
||||
|
||||
nonce, err := h.transactor.NextNonce(h.contractMaker.RPCClient, sendArgs.ChainID, sendArgs.HopTx.From)
|
||||
nonce, err := h.transactor.NextNonce(h.contractMaker.RPCClient, fromNetwork.ChainID, sendArgs.HopTx.From)
|
||||
if err != nil {
|
||||
return tx, err
|
||||
}
|
||||
@@ -292,22 +315,35 @@ func (h *HopBridge) swapAndSend(chainID uint64, hopArgs *HopTxArgs, signerFn bin
|
||||
return tx, err
|
||||
}
|
||||
|
||||
toNetwork := h.contractMaker.RPCClient.NetworkManager.Find(hopArgs.ChainID)
|
||||
if toNetwork == nil {
|
||||
return tx, err
|
||||
}
|
||||
|
||||
txOpts := hopArgs.ToTransactOpts(signerFn)
|
||||
if token.IsNative() {
|
||||
txOpts.Value = (*big.Int)(hopArgs.Amount)
|
||||
}
|
||||
now := time.Now()
|
||||
deadline := big.NewInt(now.Unix() + 604800)
|
||||
amountOutMin := big.NewInt(0)
|
||||
destinationDeadline := big.NewInt(now.Unix() + 604800)
|
||||
destinationAmountOutMin := big.NewInt(0)
|
||||
|
||||
if toNetwork.Layer == 1 {
|
||||
destinationDeadline = big.NewInt(0)
|
||||
}
|
||||
|
||||
tx, err = ammWrapper.SwapAndSend(
|
||||
txOpts,
|
||||
big.NewInt(int64(hopArgs.ChainID)),
|
||||
new(big.Int).SetUint64(hopArgs.ChainID),
|
||||
hopArgs.Recipient,
|
||||
hopArgs.Amount.ToInt(),
|
||||
hopArgs.BonderFee.ToInt(),
|
||||
big.NewInt(0),
|
||||
deadline,
|
||||
big.NewInt(0),
|
||||
amountOutMin,
|
||||
deadline,
|
||||
destinationAmountOutMin,
|
||||
destinationDeadline,
|
||||
)
|
||||
|
||||
return tx, err
|
||||
|
||||
16
vendor/github.com/status-im/status-go/services/wallet/bridge/transfer.go
generated
vendored
16
vendor/github.com/status-im/status-go/services/wallet/bridge/transfer.go
generated
vendored
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
ethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/contracts/ierc20"
|
||||
@@ -84,6 +85,21 @@ func (s *TransferBridge) EstimateGas(fromNetwork *params.Network, toNetwork *par
|
||||
return uint64(increasedEstimation), nil
|
||||
}
|
||||
|
||||
func (s *TransferBridge) BuildTx(network, _ *params.Network, fromAddress common.Address, toAddress common.Address, token *token.Token, amountIn *big.Int, bonderFee *big.Int) (*ethTypes.Transaction, error) {
|
||||
toAddr := types.Address(toAddress)
|
||||
sendArgs := &TransactionBridge{
|
||||
TransferTx: &transactions.SendTxArgs{
|
||||
From: types.Address(fromAddress),
|
||||
To: &toAddr,
|
||||
Value: (*hexutil.Big)(amountIn),
|
||||
Data: types.HexBytes("0x0"),
|
||||
},
|
||||
ChainID: network.ChainID,
|
||||
}
|
||||
|
||||
return s.BuildTransaction(sendArgs)
|
||||
}
|
||||
|
||||
func (s *TransferBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (types.Hash, error) {
|
||||
return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, verifiedAccount)
|
||||
}
|
||||
|
||||
@@ -203,6 +203,12 @@ func scanCollectiblesDataRow(row *sql.Row) (*thirdparty.CollectibleData, error)
|
||||
|
||||
func (o *CollectibleDataDB) GetIDsNotInDB(ids []thirdparty.CollectibleUniqueID) ([]thirdparty.CollectibleUniqueID, error) {
|
||||
ret := make([]thirdparty.CollectibleUniqueID, 0, len(ids))
|
||||
idMap := make(map[string]thirdparty.CollectibleUniqueID, len(ids))
|
||||
|
||||
// Ensure we don't have duplicates
|
||||
for _, id := range ids {
|
||||
idMap[id.HashKey()] = id
|
||||
}
|
||||
|
||||
exists, err := o.db.Prepare(`SELECT EXISTS (
|
||||
SELECT 1 FROM collectible_data_cache
|
||||
@@ -212,7 +218,7 @@ func (o *CollectibleDataDB) GetIDsNotInDB(ids []thirdparty.CollectibleUniqueID)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
for _, id := range idMap {
|
||||
row := exists.QueryRow(
|
||||
id.ContractID.ChainID,
|
||||
id.ContractID.Address,
|
||||
|
||||
@@ -180,6 +180,12 @@ func scanCollectionsDataRow(row *sql.Row) (*thirdparty.CollectionData, error) {
|
||||
|
||||
func (o *CollectionDataDB) GetIDsNotInDB(ids []thirdparty.ContractID) ([]thirdparty.ContractID, error) {
|
||||
ret := make([]thirdparty.ContractID, 0, len(ids))
|
||||
idMap := make(map[string]thirdparty.ContractID, len(ids))
|
||||
|
||||
// Ensure we don't have duplicates
|
||||
for _, id := range ids {
|
||||
idMap[id.HashKey()] = id
|
||||
}
|
||||
|
||||
exists, err := o.db.Prepare(`SELECT EXISTS (
|
||||
SELECT 1 FROM collection_data_cache
|
||||
@@ -189,7 +195,7 @@ func (o *CollectionDataDB) GetIDsNotInDB(ids []thirdparty.ContractID) ([]thirdpa
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
for _, id := range idMap {
|
||||
row := exists.QueryRow(
|
||||
id.ChainID,
|
||||
id.Address,
|
||||
|
||||
21
vendor/github.com/status-im/status-go/services/wallet/collectibles/commands.go
generated
vendored
21
vendor/github.com/status-im/status-go/services/wallet/collectibles/commands.go
generated
vendored
@@ -164,7 +164,7 @@ type loadOwnedCollectiblesCommand struct {
|
||||
ownedCollectiblesChangeCh chan<- OwnedCollectiblesChange
|
||||
|
||||
// Not to be set by the caller
|
||||
partialOwnership []thirdparty.CollectibleUniqueID
|
||||
partialOwnership []thirdparty.CollectibleIDBalance
|
||||
err error
|
||||
}
|
||||
|
||||
@@ -200,14 +200,18 @@ func (c *loadOwnedCollectiblesCommand) triggerEvent(eventType walletevent.EventT
|
||||
})
|
||||
}
|
||||
|
||||
func ownedTokensToTokenBalancesPerContractAddress(ownership []thirdparty.CollectibleUniqueID) thirdparty.TokenBalancesPerContractAddress {
|
||||
func ownedTokensToTokenBalancesPerContractAddress(ownership []thirdparty.CollectibleIDBalance) thirdparty.TokenBalancesPerContractAddress {
|
||||
ret := make(thirdparty.TokenBalancesPerContractAddress)
|
||||
for _, id := range ownership {
|
||||
balance := thirdparty.TokenBalance{
|
||||
TokenID: id.TokenID,
|
||||
Balance: &bigint.BigInt{Int: big.NewInt(1)},
|
||||
for _, idBalance := range ownership {
|
||||
balanceBigInt := idBalance.Balance
|
||||
if balanceBigInt == nil {
|
||||
balanceBigInt = &bigint.BigInt{Int: big.NewInt(1)}
|
||||
}
|
||||
ret[id.ContractID.Address] = append(ret[id.ContractID.Address], balance)
|
||||
balance := thirdparty.TokenBalance{
|
||||
TokenID: idBalance.ID.TokenID,
|
||||
Balance: balanceBigInt,
|
||||
}
|
||||
ret[idBalance.ID.ContractID.Address] = append(ret[idBalance.ID.ContractID.Address], balance)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
@@ -295,9 +299,6 @@ func (c *loadOwnedCollectiblesCommand) Run(parent context.Context) (err error) {
|
||||
// Normally, update the DB once we've finished fetching
|
||||
// If this is the first fetch, make partial updates to the client to get a better UX
|
||||
if initialFetch || finished {
|
||||
// Token balances should come from the providers. For now we assume all balances are 1, which
|
||||
// is only valid for ERC721.
|
||||
// TODO (#13025): Fetch balances from the providers.
|
||||
balances := ownedTokensToTokenBalancesPerContractAddress(c.partialOwnership)
|
||||
|
||||
updateMessage.Removed, updateMessage.Updated, updateMessage.Added, err = c.ownershipDB.Update(c.chainID, c.account, balances, start.Unix())
|
||||
|
||||
2
vendor/github.com/status-im/status-go/services/wallet/collectibles/controller.go
generated
vendored
2
vendor/github.com/status-im/status-go/services/wallet/collectibles/controller.go
generated
vendored
@@ -366,7 +366,7 @@ func (c *Controller) startSettingsWatcher() {
|
||||
}
|
||||
|
||||
settingChangeCb := func(setting settings.SettingField, value interface{}) {
|
||||
if setting.Equals(settings.TestNetworksEnabled) || setting.Equals(settings.IsSepoliaEnabled) {
|
||||
if setting.Equals(settings.TestNetworksEnabled) || setting.Equals(settings.IsGoerliEnabled) {
|
||||
c.stopPeriodicalOwnershipFetch()
|
||||
err := c.startPeriodicalOwnershipFetch()
|
||||
if err != nil {
|
||||
|
||||
2
vendor/github.com/status-im/status-go/services/wallet/collectibles/filter.go
generated
vendored
2
vendor/github.com/status-im/status-go/services/wallet/collectibles/filter.go
generated
vendored
@@ -60,7 +60,7 @@ func filterOwnedCollectibles(ctx context.Context, db *sql.DB, chainIDs []wcommon
|
||||
return nil, errors.New("no chainIDs provided")
|
||||
}
|
||||
|
||||
q := sq.Select("ownership.chain_id,ownership.contract_address,ownership.token_id")
|
||||
q := sq.Select("ownership.chain_id,ownership.contract_address,ownership.token_id").Distinct()
|
||||
q = q.From("collectibles_ownership_cache ownership").
|
||||
LeftJoin(`collectible_data_cache data ON
|
||||
ownership.chain_id = data.chain_id AND
|
||||
|
||||
595
vendor/github.com/status-im/status-go/services/wallet/collectibles/manager.go
generated
vendored
595
vendor/github.com/status-im/status-go/services/wallet/collectibles/manager.go
generated
vendored
@@ -8,15 +8,16 @@ import (
|
||||
"math/big"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/afex/hystrix-go/hystrix"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/status-im/status-go/circuitbreaker"
|
||||
"github.com/status-im/status-go/contracts/community-tokens/collectibles"
|
||||
"github.com/status-im/status-go/contracts/ierc1155"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/server"
|
||||
"github.com/status-im/status-go/services/wallet/async"
|
||||
@@ -31,8 +32,6 @@ import (
|
||||
const requestTimeout = 5 * time.Second
|
||||
const signalUpdatedCollectiblesDataPageSize = 10
|
||||
|
||||
const hystrixContractOwnershipClientName = "contractOwnershipClient"
|
||||
|
||||
const EventCollectiblesConnectionStatusChanged walletevent.EventType = "wallet-collectible-status-changed"
|
||||
|
||||
// ERC721 does not support function "TokenURI" if call
|
||||
@@ -52,12 +51,8 @@ type ManagerInterface interface {
|
||||
}
|
||||
|
||||
type Manager struct {
|
||||
rpcClient *rpc.Client
|
||||
contractOwnershipProviders []thirdparty.CollectibleContractOwnershipProvider
|
||||
accountOwnershipProviders []thirdparty.CollectibleAccountOwnershipProvider
|
||||
collectibleDataProviders []thirdparty.CollectibleDataProvider
|
||||
collectionDataProviders []thirdparty.CollectionDataProvider
|
||||
collectibleProviders []thirdparty.CollectibleProvider
|
||||
rpcClient *rpc.Client
|
||||
providers thirdparty.CollectibleProviders
|
||||
|
||||
httpClient *http.Client
|
||||
|
||||
@@ -68,27 +63,19 @@ type Manager struct {
|
||||
|
||||
mediaServer *server.MediaServer
|
||||
|
||||
statuses map[string]*connection.Status
|
||||
statusNotifier *connection.StatusNotifier
|
||||
feed *event.Feed
|
||||
statuses map[string]*connection.Status
|
||||
statusNotifier *connection.StatusNotifier
|
||||
feed *event.Feed
|
||||
circuitBreakers sync.Map
|
||||
}
|
||||
|
||||
func NewManager(
|
||||
db *sql.DB,
|
||||
rpcClient *rpc.Client,
|
||||
communityManager *community.Manager,
|
||||
contractOwnershipProviders []thirdparty.CollectibleContractOwnershipProvider,
|
||||
accountOwnershipProviders []thirdparty.CollectibleAccountOwnershipProvider,
|
||||
collectibleDataProviders []thirdparty.CollectibleDataProvider,
|
||||
collectionDataProviders []thirdparty.CollectionDataProvider,
|
||||
providers thirdparty.CollectibleProviders,
|
||||
mediaServer *server.MediaServer,
|
||||
feed *event.Feed) *Manager {
|
||||
hystrix.ConfigureCommand(hystrixContractOwnershipClientName, hystrix.CommandConfig{
|
||||
Timeout: 10000,
|
||||
MaxConcurrentRequests: 100,
|
||||
SleepWindow: 300000,
|
||||
ErrorPercentThreshold: 25,
|
||||
})
|
||||
|
||||
ownershipDB := NewOwnershipDB(db)
|
||||
|
||||
@@ -112,32 +99,9 @@ func NewManager(
|
||||
feed,
|
||||
)
|
||||
|
||||
// Get list of all providers
|
||||
collectibleProvidersMap := make(map[string]thirdparty.CollectibleProvider)
|
||||
collectibleProviders := make([]thirdparty.CollectibleProvider, 0)
|
||||
for _, provider := range contractOwnershipProviders {
|
||||
collectibleProvidersMap[provider.ID()] = provider
|
||||
}
|
||||
for _, provider := range accountOwnershipProviders {
|
||||
collectibleProvidersMap[provider.ID()] = provider
|
||||
}
|
||||
for _, provider := range collectibleDataProviders {
|
||||
collectibleProvidersMap[provider.ID()] = provider
|
||||
}
|
||||
for _, provider := range collectionDataProviders {
|
||||
collectibleProvidersMap[provider.ID()] = provider
|
||||
}
|
||||
for _, provider := range collectibleProvidersMap {
|
||||
collectibleProviders = append(collectibleProviders, provider)
|
||||
}
|
||||
|
||||
return &Manager{
|
||||
rpcClient: rpcClient,
|
||||
contractOwnershipProviders: contractOwnershipProviders,
|
||||
accountOwnershipProviders: accountOwnershipProviders,
|
||||
collectibleDataProviders: collectibleDataProviders,
|
||||
collectionDataProviders: collectionDataProviders,
|
||||
collectibleProviders: collectibleProviders,
|
||||
rpcClient: rpcClient,
|
||||
providers: providers,
|
||||
httpClient: &http.Client{
|
||||
Timeout: requestTimeout,
|
||||
},
|
||||
@@ -160,35 +124,6 @@ func mapToList[K comparable, T any](m map[K]T) []T {
|
||||
return list
|
||||
}
|
||||
|
||||
func makeContractOwnershipCall(main func() (any, error), fallback func() (any, error)) (any, error) {
|
||||
resultChan := make(chan any, 1)
|
||||
errChan := hystrix.Go(hystrixContractOwnershipClientName, func() error {
|
||||
res, err := main()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resultChan <- res
|
||||
return nil
|
||||
}, func(err error) error {
|
||||
if fallback == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := fallback()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resultChan <- res
|
||||
return nil
|
||||
})
|
||||
select {
|
||||
case result := <-resultChan:
|
||||
return result, nil
|
||||
case err := <-errChan:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Manager) doContentTypeRequest(ctx context.Context, url string) (string, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodHead, url, nil)
|
||||
if err != nil {
|
||||
@@ -253,67 +188,169 @@ func (o *Manager) FetchBalancesByOwnerAndContractAddress(ctx context.Context, ch
|
||||
func (o *Manager) FetchAllAssetsByOwnerAndContractAddress(ctx context.Context, chainID walletCommon.ChainID, owner common.Address, contractAddresses []common.Address, cursor string, limit int, providerID string) (*thirdparty.FullCollectibleDataContainer, error) {
|
||||
defer o.checkConnectionStatus(chainID)
|
||||
|
||||
anyProviderAvailable := false
|
||||
for _, provider := range o.accountOwnershipProviders {
|
||||
cmd := circuitbreaker.Command{}
|
||||
for _, provider := range o.providers.AccountOwnershipProviders {
|
||||
if !provider.IsChainSupported(chainID) {
|
||||
continue
|
||||
}
|
||||
anyProviderAvailable = true
|
||||
if providerID != thirdparty.FetchFromAnyProvider && providerID != provider.ID() {
|
||||
continue
|
||||
}
|
||||
|
||||
assetContainer, err := provider.FetchAllAssetsByOwnerAndContractAddress(ctx, chainID, owner, contractAddresses, cursor, limit)
|
||||
if err != nil {
|
||||
log.Error("FetchAllAssetsByOwnerAndContractAddress failed for", "provider", provider.ID(), "chainID", chainID, "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = o.processFullCollectibleData(ctx, assetContainer.Items, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return assetContainer, nil
|
||||
provider := provider
|
||||
f := circuitbreaker.NewFunctor(
|
||||
func() ([]interface{}, error) {
|
||||
assetContainer, err := provider.FetchAllAssetsByOwnerAndContractAddress(ctx, chainID, owner, contractAddresses, cursor, limit)
|
||||
if err != nil {
|
||||
log.Error("FetchAllAssetsByOwnerAndContractAddress failed for", "provider", provider.ID(), "chainID", chainID, "err", err)
|
||||
}
|
||||
return []interface{}{assetContainer}, err
|
||||
},
|
||||
)
|
||||
cmd.Add(f)
|
||||
}
|
||||
|
||||
if anyProviderAvailable {
|
||||
return nil, ErrAllProvidersFailedForChainID
|
||||
if cmd.IsEmpty() {
|
||||
return nil, ErrNoProvidersAvailableForChainID
|
||||
}
|
||||
return nil, ErrNoProvidersAvailableForChainID
|
||||
|
||||
cmdRes := o.getCircuitBreaker(chainID).Execute(cmd)
|
||||
if cmdRes.Error() != nil {
|
||||
log.Error("FetchAllAssetsByOwnerAndContractAddress failed for", "chainID", chainID, "err", cmdRes.Error())
|
||||
return nil, cmdRes.Error()
|
||||
}
|
||||
|
||||
assetContainer := cmdRes.Result()[0].(*thirdparty.FullCollectibleDataContainer)
|
||||
_, err := o.processFullCollectibleData(ctx, assetContainer.Items, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return assetContainer, nil
|
||||
}
|
||||
|
||||
func (o *Manager) FetchAllAssetsByOwner(ctx context.Context, chainID walletCommon.ChainID, owner common.Address, cursor string, limit int, providerID string) (*thirdparty.FullCollectibleDataContainer, error) {
|
||||
defer o.checkConnectionStatus(chainID)
|
||||
|
||||
anyProviderAvailable := false
|
||||
for _, provider := range o.accountOwnershipProviders {
|
||||
cmd := circuitbreaker.Command{}
|
||||
for _, provider := range o.providers.AccountOwnershipProviders {
|
||||
if !provider.IsChainSupported(chainID) {
|
||||
continue
|
||||
}
|
||||
anyProviderAvailable = true
|
||||
if providerID != thirdparty.FetchFromAnyProvider && providerID != provider.ID() {
|
||||
continue
|
||||
}
|
||||
|
||||
assetContainer, err := provider.FetchAllAssetsByOwner(ctx, chainID, owner, cursor, limit)
|
||||
if err != nil {
|
||||
log.Error("FetchAllAssetsByOwner failed for", "provider", provider.ID(), "chainID", chainID, "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = o.processFullCollectibleData(ctx, assetContainer.Items, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return assetContainer, nil
|
||||
provider := provider
|
||||
f := circuitbreaker.NewFunctor(
|
||||
func() ([]interface{}, error) {
|
||||
assetContainer, err := provider.FetchAllAssetsByOwner(ctx, chainID, owner, cursor, limit)
|
||||
if err != nil {
|
||||
log.Error("FetchAllAssetsByOwner failed for", "provider", provider.ID(), "chainID", chainID, "err", err)
|
||||
}
|
||||
return []interface{}{assetContainer}, err
|
||||
},
|
||||
)
|
||||
cmd.Add(f)
|
||||
}
|
||||
|
||||
if anyProviderAvailable {
|
||||
return nil, ErrAllProvidersFailedForChainID
|
||||
if cmd.IsEmpty() {
|
||||
return nil, ErrNoProvidersAvailableForChainID
|
||||
}
|
||||
|
||||
cmdRes := o.getCircuitBreaker(chainID).Execute(cmd)
|
||||
if cmdRes.Error() != nil {
|
||||
log.Error("FetchAllAssetsByOwner failed for", "chainID", chainID, "err", cmdRes.Error())
|
||||
return nil, cmdRes.Error()
|
||||
}
|
||||
|
||||
assetContainer := cmdRes.Result()[0].(*thirdparty.FullCollectibleDataContainer)
|
||||
_, err := o.processFullCollectibleData(ctx, assetContainer.Items, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return assetContainer, nil
|
||||
}
|
||||
|
||||
func (o *Manager) FetchERC1155Balances(ctx context.Context, owner common.Address, chainID walletCommon.ChainID, contractAddress common.Address, tokenIDs []*bigint.BigInt) ([]*bigint.BigInt, error) {
|
||||
if len(tokenIDs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
backend, err := o.rpcClient.EthClient(uint64(chainID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
caller, err := ierc1155.NewIerc1155Caller(contractAddress, backend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
owners := make([]common.Address, len(tokenIDs))
|
||||
ids := make([]*big.Int, len(tokenIDs))
|
||||
for i, tokenID := range tokenIDs {
|
||||
owners[i] = owner
|
||||
ids[i] = tokenID.Int
|
||||
}
|
||||
|
||||
balances, err := caller.BalanceOfBatch(&bind.CallOpts{
|
||||
Context: ctx,
|
||||
}, owners, ids)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bigIntBalances := make([]*bigint.BigInt, len(balances))
|
||||
for i, balance := range balances {
|
||||
bigIntBalances[i] = &bigint.BigInt{Int: balance}
|
||||
}
|
||||
|
||||
return bigIntBalances, err
|
||||
}
|
||||
|
||||
func (o *Manager) fillMissingBalances(ctx context.Context, owner common.Address, collectibles []*thirdparty.FullCollectibleData) {
|
||||
collectiblesByChainIDAndContractAddress := thirdparty.GroupCollectiblesByChainIDAndContractAddress(collectibles)
|
||||
|
||||
for chainID, collectiblesByContract := range collectiblesByChainIDAndContractAddress {
|
||||
for contractAddress, contractCollectibles := range collectiblesByContract {
|
||||
collectiblesToFetchPerTokenID := make(map[string]*thirdparty.FullCollectibleData)
|
||||
|
||||
for _, collectible := range contractCollectibles {
|
||||
if collectible.AccountBalance == nil {
|
||||
switch getContractType(*collectible) {
|
||||
case walletCommon.ContractTypeERC1155:
|
||||
collectiblesToFetchPerTokenID[collectible.CollectibleData.ID.TokenID.String()] = collectible
|
||||
default:
|
||||
// Any other type of collectible is non-fungible, balance is 1
|
||||
collectible.AccountBalance = &bigint.BigInt{Int: big.NewInt(1)}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(collectiblesToFetchPerTokenID) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
tokenIDs := make([]*bigint.BigInt, 0, len(collectiblesToFetchPerTokenID))
|
||||
for _, c := range collectiblesToFetchPerTokenID {
|
||||
tokenIDs = append(tokenIDs, c.CollectibleData.ID.TokenID)
|
||||
}
|
||||
|
||||
balances, err := o.FetchERC1155Balances(ctx, owner, chainID, contractAddress, tokenIDs)
|
||||
if err != nil {
|
||||
log.Error("FetchERC1155Balances failed", "chainID", chainID, "contractAddress", contractAddress, "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
for i := range balances {
|
||||
collectible := collectiblesToFetchPerTokenID[tokenIDs[i].String()]
|
||||
collectible.AccountBalance = balances[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, ErrNoProvidersAvailableForChainID
|
||||
}
|
||||
|
||||
func (o *Manager) FetchCollectibleOwnershipByOwner(ctx context.Context, chainID walletCommon.ChainID, owner common.Address, cursor string, limit int, providerID string) (*thirdparty.CollectibleOwnershipContainer, error) {
|
||||
@@ -324,7 +361,15 @@ func (o *Manager) FetchCollectibleOwnershipByOwner(ctx context.Context, chainID
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Some providers do not give us the balances for ERC1155 tokens, so we need to fetch them separately.
|
||||
collectibles := make([]*thirdparty.FullCollectibleData, 0, len(assetContainer.Items))
|
||||
for i := range assetContainer.Items {
|
||||
collectibles = append(collectibles, &assetContainer.Items[i])
|
||||
}
|
||||
o.fillMissingBalances(ctx, owner, collectibles)
|
||||
|
||||
ret := assetContainer.ToOwnershipContainer()
|
||||
|
||||
return &ret, nil
|
||||
}
|
||||
|
||||
@@ -332,50 +377,81 @@ func (o *Manager) FetchCollectibleOwnershipByOwner(ctx context.Context, chainID
|
||||
// If asyncFetch is true, empty metadata will be returned for any missing collectibles and an EventCollectiblesDataUpdated will be sent when the data is ready.
|
||||
// If asyncFetch is false, it will wait for all collectibles' metadata to be retrieved before returning.
|
||||
func (o *Manager) FetchAssetsByCollectibleUniqueID(ctx context.Context, uniqueIDs []thirdparty.CollectibleUniqueID, asyncFetch bool) ([]thirdparty.FullCollectibleData, error) {
|
||||
missingIDs, err := o.collectiblesDataDB.GetIDsNotInDB(uniqueIDs)
|
||||
err := o.FetchMissingAssetsByCollectibleUniqueID(ctx, uniqueIDs, asyncFetch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
missingIDsPerChainID := thirdparty.GroupCollectibleUIDsByChainID(missingIDs)
|
||||
return o.getCacheFullCollectibleData(uniqueIDs)
|
||||
}
|
||||
|
||||
group := async.NewGroup(ctx)
|
||||
group.Add(func(ctx context.Context) error {
|
||||
for chainID, idsToFetch := range missingIDsPerChainID {
|
||||
defer o.checkConnectionStatus(chainID)
|
||||
|
||||
for _, provider := range o.collectibleDataProviders {
|
||||
if !provider.IsChainSupported(chainID) {
|
||||
continue
|
||||
}
|
||||
|
||||
fetchedAssets, err := provider.FetchAssetsByCollectibleUniqueID(ctx, idsToFetch)
|
||||
if err != nil {
|
||||
log.Error("FetchAssetsByCollectibleUniqueID failed for", "provider", provider.ID(), "chainID", chainID, "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
updatedCollectibles, err := o.processFullCollectibleData(ctx, fetchedAssets, asyncFetch)
|
||||
if err != nil {
|
||||
log.Error("processFullCollectibleData failed for", "provider", provider.ID(), "chainID", chainID, "len(fetchedAssets)", len(fetchedAssets), "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if asyncFetch {
|
||||
o.signalUpdatedCollectiblesData(updatedCollectibles)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if !asyncFetch {
|
||||
group.Wait()
|
||||
func (o *Manager) FetchMissingAssetsByCollectibleUniqueID(ctx context.Context, uniqueIDs []thirdparty.CollectibleUniqueID, asyncFetch bool) error {
|
||||
missingIDs, err := o.collectiblesDataDB.GetIDsNotInDB(uniqueIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return o.getCacheFullCollectibleData(uniqueIDs)
|
||||
missingIDsPerChainID := thirdparty.GroupCollectibleUIDsByChainID(missingIDs)
|
||||
|
||||
// Atomic group stores the error from the first failed command and stops other commands on error
|
||||
group := async.NewAtomicGroup(ctx)
|
||||
for chainID, idsToFetch := range missingIDsPerChainID {
|
||||
group.Add(func(ctx context.Context) error {
|
||||
defer o.checkConnectionStatus(chainID)
|
||||
|
||||
fetchedAssets, err := o.fetchMissingAssetsForChainByCollectibleUniqueID(ctx, chainID, idsToFetch)
|
||||
if err != nil {
|
||||
log.Error("FetchMissingAssetsByCollectibleUniqueID failed for", "chainID", chainID, "ids", idsToFetch, "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
updatedCollectibles, err := o.processFullCollectibleData(ctx, fetchedAssets, asyncFetch)
|
||||
if err != nil {
|
||||
log.Error("processFullCollectibleData failed for", "chainID", chainID, "len(fetchedAssets)", len(fetchedAssets), "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
o.signalUpdatedCollectiblesData(updatedCollectibles)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
if asyncFetch {
|
||||
group.Wait()
|
||||
return group.Error()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Manager) fetchMissingAssetsForChainByCollectibleUniqueID(ctx context.Context, chainID walletCommon.ChainID, idsToFetch []thirdparty.CollectibleUniqueID) ([]thirdparty.FullCollectibleData, error) {
|
||||
cmd := circuitbreaker.Command{}
|
||||
for _, provider := range o.providers.CollectibleDataProviders {
|
||||
if !provider.IsChainSupported(chainID) {
|
||||
continue
|
||||
}
|
||||
|
||||
provider := provider
|
||||
cmd.Add(circuitbreaker.NewFunctor(func() ([]any, error) {
|
||||
fetchedAssets, err := provider.FetchAssetsByCollectibleUniqueID(ctx, idsToFetch)
|
||||
if err != nil {
|
||||
log.Error("fetchMissingAssetsForChainByCollectibleUniqueID failed for", "provider", provider.ID(), "chainID", chainID, "err", err)
|
||||
}
|
||||
|
||||
return []any{fetchedAssets}, err
|
||||
}))
|
||||
}
|
||||
|
||||
if cmd.IsEmpty() {
|
||||
return nil, ErrNoProvidersAvailableForChainID // lets not stop the group if no providers are available for the chain
|
||||
}
|
||||
|
||||
cmdRes := o.getCircuitBreaker(chainID).Execute(cmd)
|
||||
if cmdRes.Error() != nil {
|
||||
log.Error("fetchMissingAssetsForChainByCollectibleUniqueID failed for", "chainID", chainID, "err", cmdRes.Error())
|
||||
return nil, cmdRes.Error()
|
||||
}
|
||||
return cmdRes.Result()[0].([]thirdparty.FullCollectibleData), cmdRes.Error()
|
||||
}
|
||||
|
||||
func (o *Manager) FetchCollectionsDataByContractID(ctx context.Context, ids []thirdparty.ContractID) ([]thirdparty.CollectionData, error) {
|
||||
@@ -386,27 +462,49 @@ func (o *Manager) FetchCollectionsDataByContractID(ctx context.Context, ids []th
|
||||
|
||||
missingIDsPerChainID := thirdparty.GroupContractIDsByChainID(missingIDs)
|
||||
|
||||
// Atomic group stores the error from the first failed command and stops other commands on error
|
||||
group := async.NewAtomicGroup(ctx)
|
||||
for chainID, idsToFetch := range missingIDsPerChainID {
|
||||
defer o.checkConnectionStatus(chainID)
|
||||
group.Add(func(ctx context.Context) error {
|
||||
defer o.checkConnectionStatus(chainID)
|
||||
|
||||
for _, provider := range o.collectionDataProviders {
|
||||
if !provider.IsChainSupported(chainID) {
|
||||
continue
|
||||
cmd := circuitbreaker.Command{}
|
||||
for _, provider := range o.providers.CollectionDataProviders {
|
||||
if !provider.IsChainSupported(chainID) {
|
||||
continue
|
||||
}
|
||||
|
||||
provider := provider
|
||||
cmd.Add(circuitbreaker.NewFunctor(func() ([]any, error) {
|
||||
fetchedCollections, err := provider.FetchCollectionsDataByContractID(ctx, idsToFetch)
|
||||
return []any{fetchedCollections}, err
|
||||
}))
|
||||
}
|
||||
|
||||
fetchedCollections, err := provider.FetchCollectionsDataByContractID(ctx, idsToFetch)
|
||||
if err != nil {
|
||||
log.Error("FetchCollectionsDataByContractID failed for", "provider", provider.ID(), "chainID", chainID, "err", err)
|
||||
continue
|
||||
if cmd.IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmdRes := o.getCircuitBreaker(chainID).Execute(cmd)
|
||||
if cmdRes.Error() != nil {
|
||||
log.Error("FetchCollectionsDataByContractID failed for", "chainID", chainID, "err", cmdRes.Error())
|
||||
return cmdRes.Error()
|
||||
}
|
||||
|
||||
fetchedCollections := cmdRes.Result()[0].([]thirdparty.CollectionData)
|
||||
err = o.processCollectionData(ctx, fetchedCollections)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
group.Wait()
|
||||
|
||||
if group.Error() != nil {
|
||||
return nil, group.Error()
|
||||
}
|
||||
|
||||
data, err := o.collectionsDataDB.GetData(ids)
|
||||
@@ -417,55 +515,39 @@ func (o *Manager) FetchCollectionsDataByContractID(ctx context.Context, ids []th
|
||||
return mapToList(data), nil
|
||||
}
|
||||
|
||||
func (o *Manager) getContractOwnershipProviders(chainID walletCommon.ChainID) (mainProvider thirdparty.CollectibleContractOwnershipProvider, fallbackProvider thirdparty.CollectibleContractOwnershipProvider) {
|
||||
mainProvider = nil
|
||||
fallbackProvider = nil
|
||||
|
||||
for _, provider := range o.contractOwnershipProviders {
|
||||
if provider.IsChainSupported(chainID) {
|
||||
if mainProvider == nil {
|
||||
// First provider found
|
||||
mainProvider = provider
|
||||
continue
|
||||
}
|
||||
// Second provider found
|
||||
fallbackProvider = provider
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getCollectibleOwnersByContractAddressFunc(ctx context.Context, chainID walletCommon.ChainID, contractAddress common.Address, provider thirdparty.CollectibleContractOwnershipProvider) func() (any, error) {
|
||||
if provider == nil {
|
||||
return nil
|
||||
}
|
||||
return func() (any, error) {
|
||||
res, err := provider.FetchCollectibleOwnersByContractAddress(ctx, chainID, contractAddress)
|
||||
if err != nil {
|
||||
log.Error("FetchCollectibleOwnersByContractAddress failed for", "provider", provider.ID(), "chainID", chainID, "err", err)
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
func (o *Manager) GetCollectibleOwnership(id thirdparty.CollectibleUniqueID) ([]thirdparty.AccountBalance, error) {
|
||||
return o.ownershipDB.GetOwnership(id)
|
||||
}
|
||||
|
||||
func (o *Manager) FetchCollectibleOwnersByContractAddress(ctx context.Context, chainID walletCommon.ChainID, contractAddress common.Address) (*thirdparty.CollectibleContractOwnership, error) {
|
||||
defer o.checkConnectionStatus(chainID)
|
||||
|
||||
mainProvider, fallbackProvider := o.getContractOwnershipProviders(chainID)
|
||||
if mainProvider == nil {
|
||||
cmd := circuitbreaker.Command{}
|
||||
for _, provider := range o.providers.ContractOwnershipProviders {
|
||||
if !provider.IsChainSupported(chainID) {
|
||||
continue
|
||||
}
|
||||
|
||||
provider := provider
|
||||
cmd.Add(circuitbreaker.NewFunctor(func() ([]any, error) {
|
||||
res, err := provider.FetchCollectibleOwnersByContractAddress(ctx, chainID, contractAddress)
|
||||
if err != nil {
|
||||
log.Error("FetchCollectibleOwnersByContractAddress failed for", "provider", provider.ID(), "chainID", chainID, "err", err)
|
||||
}
|
||||
return []any{res}, err
|
||||
}))
|
||||
}
|
||||
|
||||
if cmd.IsEmpty() {
|
||||
return nil, ErrNoProvidersAvailableForChainID
|
||||
}
|
||||
|
||||
mainFn := getCollectibleOwnersByContractAddressFunc(ctx, chainID, contractAddress, mainProvider)
|
||||
fallbackFn := getCollectibleOwnersByContractAddressFunc(ctx, chainID, contractAddress, fallbackProvider)
|
||||
|
||||
owners, err := makeContractOwnershipCall(mainFn, fallbackFn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
cmdRes := o.getCircuitBreaker(chainID).Execute(cmd)
|
||||
if cmdRes.Error() != nil {
|
||||
log.Error("FetchCollectibleOwnersByContractAddress failed for", "chainID", chainID, "err", cmdRes.Error())
|
||||
return nil, cmdRes.Error()
|
||||
}
|
||||
|
||||
return owners.(*thirdparty.CollectibleContractOwnership), nil
|
||||
return cmdRes.Result()[0].(*thirdparty.CollectibleContractOwnership), cmdRes.Error()
|
||||
}
|
||||
|
||||
func (o *Manager) fetchTokenURI(ctx context.Context, id thirdparty.CollectibleUniqueID) (string, error) {
|
||||
@@ -646,25 +728,16 @@ func (o *Manager) fillCommunityID(asset *thirdparty.FullCollectibleData) error {
|
||||
}
|
||||
|
||||
func (o *Manager) fetchCommunityAssets(communityID string, communityAssets []*thirdparty.FullCollectibleData) error {
|
||||
communityInfo, err := o.communityManager.FetchCommunityInfo(communityID)
|
||||
communityFound, err := o.communityManager.FillCollectiblesMetadata(communityID, communityAssets)
|
||||
if err != nil {
|
||||
log.Error("FillCollectiblesMetadata failed", "communityID", communityID, "err", err)
|
||||
} else if !communityFound {
|
||||
log.Warn("fetchCommunityAssets community not found", "communityID", communityID)
|
||||
}
|
||||
|
||||
// If the community is found, we update the DB.
|
||||
// If the community is not found, we only insert new entries to the DB (don't replace what is already there).
|
||||
allowUpdate := false
|
||||
if err != nil {
|
||||
log.Error("fetchCommunityInfo failed", "communityID", communityID, "err", err)
|
||||
} else if communityInfo == nil {
|
||||
log.Warn("fetchCommunityAssets community not found", "communityID", communityID)
|
||||
} else {
|
||||
for _, communityAsset := range communityAssets {
|
||||
err := o.communityManager.FillCollectibleMetadata(communityAsset)
|
||||
if err != nil {
|
||||
log.Error("FillCollectibleMetadata failed", "communityID", communityID, "err", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
allowUpdate = true
|
||||
}
|
||||
allowUpdate := communityFound
|
||||
|
||||
collectiblesData := make([]thirdparty.CollectibleData, 0, len(communityAssets))
|
||||
collectionsData := make([]thirdparty.CollectionData, 0, len(communityAssets))
|
||||
@@ -827,7 +900,7 @@ func (o *Manager) ResetConnectionStatus() {
|
||||
}
|
||||
|
||||
func (o *Manager) checkConnectionStatus(chainID walletCommon.ChainID) {
|
||||
for _, provider := range o.collectibleProviders {
|
||||
for _, provider := range o.providers.GetProviderList() {
|
||||
if provider.IsChainSupported(chainID) && provider.IsConnected() {
|
||||
o.statuses[chainID.String()].SetIsConnected(true)
|
||||
return
|
||||
@@ -868,3 +941,89 @@ func (o *Manager) signalUpdatedCollectiblesData(ids []thirdparty.CollectibleUniq
|
||||
o.feed.Send(event)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Manager) getCircuitBreaker(chainID walletCommon.ChainID) *circuitbreaker.CircuitBreaker {
|
||||
cb, ok := o.circuitBreakers.Load(chainID.String())
|
||||
if !ok {
|
||||
cb = circuitbreaker.NewCircuitBreaker(circuitbreaker.Config{
|
||||
CommandName: chainID.String(),
|
||||
Timeout: 10000,
|
||||
MaxConcurrentRequests: 100,
|
||||
RequestVolumeThreshold: 25,
|
||||
SleepWindow: 300000,
|
||||
ErrorPercentThreshold: 25,
|
||||
})
|
||||
o.circuitBreakers.Store(chainID.String(), cb)
|
||||
}
|
||||
return cb.(*circuitbreaker.CircuitBreaker)
|
||||
}
|
||||
|
||||
func (o *Manager) SearchCollectibles(ctx context.Context, chainID walletCommon.ChainID, text string, cursor string, limit int, providerID string) (*thirdparty.FullCollectibleDataContainer, error) {
|
||||
defer o.checkConnectionStatus(chainID)
|
||||
|
||||
anyProviderAvailable := false
|
||||
for _, provider := range o.providers.SearchProviders {
|
||||
if !provider.IsChainSupported(chainID) {
|
||||
continue
|
||||
}
|
||||
anyProviderAvailable = true
|
||||
if providerID != thirdparty.FetchFromAnyProvider && providerID != provider.ID() {
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO (#13951): Be smarter about how we handle the user-entered string
|
||||
collections := []common.Address{}
|
||||
|
||||
container, err := provider.SearchCollectibles(ctx, chainID, collections, text, cursor, limit)
|
||||
if err != nil {
|
||||
log.Error("FetchAllAssetsByOwner failed for", "provider", provider.ID(), "chainID", chainID, "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = o.processFullCollectibleData(ctx, container.Items, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return container, nil
|
||||
}
|
||||
|
||||
if anyProviderAvailable {
|
||||
return nil, ErrAllProvidersFailedForChainID
|
||||
}
|
||||
return nil, ErrNoProvidersAvailableForChainID
|
||||
}
|
||||
|
||||
func (o *Manager) SearchCollections(ctx context.Context, chainID walletCommon.ChainID, query string, cursor string, limit int, providerID string) (*thirdparty.CollectionDataContainer, error) {
|
||||
defer o.checkConnectionStatus(chainID)
|
||||
|
||||
anyProviderAvailable := false
|
||||
for _, provider := range o.providers.SearchProviders {
|
||||
if !provider.IsChainSupported(chainID) {
|
||||
continue
|
||||
}
|
||||
anyProviderAvailable = true
|
||||
if providerID != thirdparty.FetchFromAnyProvider && providerID != provider.ID() {
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO (#13951): Be smarter about how we handle the user-entered string
|
||||
container, err := provider.SearchCollections(ctx, chainID, query, cursor, limit)
|
||||
if err != nil {
|
||||
log.Error("FetchAllAssetsByOwner failed for", "provider", provider.ID(), "chainID", chainID, "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
err = o.processCollectionData(ctx, container.Items)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return container, nil
|
||||
}
|
||||
|
||||
if anyProviderAvailable {
|
||||
return nil, ErrAllProvidersFailedForChainID
|
||||
}
|
||||
return nil, ErrNoProvidersAvailableForChainID
|
||||
}
|
||||
|
||||
34
vendor/github.com/status-im/status-go/services/wallet/collectibles/ownership_db.go
generated
vendored
34
vendor/github.com/status-im/status-go/services/wallet/collectibles/ownership_db.go
generated
vendored
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
||||
@@ -20,6 +21,7 @@ const InvalidTimestamp = int64(-1)
|
||||
|
||||
type OwnershipDB struct {
|
||||
db *sql.DB
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func NewOwnershipDB(sqlDb *sql.DB) *OwnershipDB {
|
||||
@@ -300,7 +302,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,7 +334,36 @@ 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) {
|
||||
// Ensure all steps are done atomically
|
||||
o.mu.Lock()
|
||||
defer o.mu.Unlock()
|
||||
|
||||
err = insertTmpOwnership(o.db, chainID, ownerAddress, balances)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -367,7 +397,7 @@ func (o *OwnershipDB) Update(chainID w_common.ChainID, ownerAddress common.Addre
|
||||
}
|
||||
|
||||
func (o *OwnershipDB) GetOwnedCollectibles(chainIDs []w_common.ChainID, ownerAddresses []common.Address, offset int, limit int) ([]thirdparty.CollectibleUniqueID, error) {
|
||||
query, args, err := sqlx.In(fmt.Sprintf(`SELECT %s
|
||||
query, args, err := sqlx.In(fmt.Sprintf(`SELECT DISTINCT %s
|
||||
FROM collectibles_ownership_cache
|
||||
WHERE chain_id IN (?) AND owner_address IN (?)
|
||||
LIMIT ? OFFSET ?`, selectOwnershipColumns), chainIDs, ownerAddresses, limit, offset)
|
||||
|
||||
68
vendor/github.com/status-im/status-go/services/wallet/collectibles/service.go
generated
vendored
68
vendor/github.com/status-im/status-go/services/wallet/collectibles/service.go
generated
vendored
@@ -61,6 +61,11 @@ const (
|
||||
FetchTypeFetchIfCacheOld
|
||||
)
|
||||
|
||||
type TxHashData struct {
|
||||
Hash common.Hash
|
||||
TxID common.Hash
|
||||
}
|
||||
|
||||
type FetchCriteria struct {
|
||||
FetchType FetchType `json:"fetch_type"`
|
||||
MaxCacheAgeSeconds int64 `json:"max_cache_age_seconds"`
|
||||
@@ -415,8 +420,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 +442,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]TxHashData {
|
||||
// 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 +450,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]TxHashData)
|
||||
|
||||
for _, id := range ownedCollectibles.ids {
|
||||
transfer, err := s.transferDB.GetLatestCollectibleTransfer(ownedCollectibles.account, id)
|
||||
if err != nil {
|
||||
@@ -452,17 +460,27 @@ func (s *Service) lookupTransferForCollectibles(ownedCollectibles OwnedCollectib
|
||||
continue
|
||||
}
|
||||
if transfer != nil {
|
||||
result[id] = TxHashData{
|
||||
Hash: transfer.Transaction.Hash(),
|
||||
TxID: transfer.ID,
|
||||
}
|
||||
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]TxHashData) {
|
||||
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 +493,47 @@ 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 _, collectible := range communityCollectibles {
|
||||
txHash := ""
|
||||
for key, value := range hashMap {
|
||||
if key.Same(&collectible.ID) {
|
||||
collectible.LatestTxHash = value.TxID.Hex()
|
||||
txHash = value.Hash.Hex()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for id, value := range firstCollectibles {
|
||||
if value && id.Same(&collectible.ID) {
|
||||
collectible.IsFirst = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
group := CollectibleGroup{
|
||||
contractID: collectible.ID.ContractID,
|
||||
txHash: txHash,
|
||||
}
|
||||
_, 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
|
||||
}
|
||||
|
||||
15
vendor/github.com/status-im/status-go/services/wallet/collectibles/types.go
generated
vendored
15
vendor/github.com/status-im/status-go/services/wallet/collectibles/types.go
generated
vendored
@@ -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 {
|
||||
@@ -167,10 +170,12 @@ func fullCollectiblesDataToCommunityHeader(data []thirdparty.FullCollectibleData
|
||||
ID: collectibleID,
|
||||
ContractType: getContractType(c),
|
||||
CollectibleData: &CollectibleData{
|
||||
Name: c.CollectibleData.Name,
|
||||
Name: c.CollectibleData.Name,
|
||||
ImageURL: &c.CollectibleData.ImageURL,
|
||||
},
|
||||
CommunityData: &communityData,
|
||||
Ownership: c.Ownership,
|
||||
IsFirst: c.CollectibleData.IsFirst,
|
||||
}
|
||||
|
||||
res = append(res, header)
|
||||
@@ -196,3 +201,11 @@ func communityInfoToData(communityID string, community *thirdparty.CommunityInfo
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func IDsFromAssets(assets []*thirdparty.FullCollectibleData) []thirdparty.CollectibleUniqueID {
|
||||
result := make([]thirdparty.CollectibleUniqueID, len(assets))
|
||||
for i, asset := range assets {
|
||||
result[i] = asset.CollectibleData.ID
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
6
vendor/github.com/status-im/status-go/services/wallet/common/const.go
generated
vendored
6
vendor/github.com/status-im/status-go/services/wallet/common/const.go
generated
vendored
@@ -5,6 +5,12 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type MultiTransactionIDType int64
|
||||
|
||||
const (
|
||||
NoMultiTransactionID = MultiTransactionIDType(0)
|
||||
)
|
||||
|
||||
type ChainID uint64
|
||||
|
||||
const (
|
||||
|
||||
31
vendor/github.com/status-im/status-go/services/wallet/community/manager.go
generated
vendored
31
vendor/github.com/status-im/status-go/services/wallet/community/manager.go
generated
vendored
@@ -58,16 +58,33 @@ func (cm *Manager) GetCommunityID(tokenURI string) string {
|
||||
return cm.communityInfoProvider.GetCommunityID(tokenURI)
|
||||
}
|
||||
|
||||
func (cm *Manager) FillCollectibleMetadata(c *thirdparty.FullCollectibleData) error {
|
||||
return cm.communityInfoProvider.FillCollectibleMetadata(c)
|
||||
func (cm *Manager) FillCollectiblesMetadata(communityID string, cs []*thirdparty.FullCollectibleData) (bool, error) {
|
||||
communityFound, err := cm.communityInfoProvider.FillCollectiblesMetadata(communityID, cs)
|
||||
if err != nil {
|
||||
return communityFound, err
|
||||
}
|
||||
|
||||
if communityFound {
|
||||
// Update local community data cache
|
||||
community, err := cm.communityInfoProvider.GetCommunityInfoFromDB(communityID)
|
||||
if err != nil {
|
||||
log.Error("GetCommunityInfoFromDB failed", "communityID", communityID, "err", err)
|
||||
return communityFound, err
|
||||
}
|
||||
err = cm.setCommunityInfo(communityID, community)
|
||||
if err != nil {
|
||||
log.Error("SetCommunityInfo failed", "communityID", community)
|
||||
}
|
||||
}
|
||||
return communityFound, nil
|
||||
}
|
||||
|
||||
func (cm *Manager) setCommunityInfo(id string, c *thirdparty.CommunityInfo) (err error) {
|
||||
return cm.db.SetCommunityInfo(id, c)
|
||||
}
|
||||
|
||||
func (cm *Manager) FetchCommunityInfo(communityID string) (*thirdparty.CommunityInfo, error) {
|
||||
communityInfo, err := cm.communityInfoProvider.FetchCommunityInfo(communityID)
|
||||
func (cm *Manager) fetchCommunityInfo(communityID string, fetcher func() (*thirdparty.CommunityInfo, error)) (*thirdparty.CommunityInfo, error) {
|
||||
communityInfo, err := fetcher()
|
||||
if err != nil {
|
||||
dbErr := cm.setCommunityInfo(communityID, nil)
|
||||
if dbErr != nil {
|
||||
@@ -79,6 +96,12 @@ func (cm *Manager) FetchCommunityInfo(communityID string) (*thirdparty.Community
|
||||
return communityInfo, err
|
||||
}
|
||||
|
||||
func (cm *Manager) FetchCommunityInfo(communityID string) (*thirdparty.CommunityInfo, error) {
|
||||
return cm.fetchCommunityInfo(communityID, func() (*thirdparty.CommunityInfo, error) {
|
||||
return cm.communityInfoProvider.FetchCommunityInfo(communityID)
|
||||
})
|
||||
}
|
||||
|
||||
func (cm *Manager) FetchCommunityMetadataAsync(communityID string) {
|
||||
go func() {
|
||||
communityInfo, err := cm.FetchCommunityMetadata(communityID)
|
||||
|
||||
36
vendor/github.com/status-im/status-go/services/wallet/fees.go
generated
vendored
36
vendor/github.com/status-im/status-go/services/wallet/fees.go
generated
vendored
@@ -7,8 +7,11 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||
ethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
gaspriceoracle "github.com/status-im/status-go/contracts/gas-price-oracle"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
)
|
||||
|
||||
@@ -27,6 +30,7 @@ type SuggestedFees struct {
|
||||
MaxFeePerGasLow *big.Float `json:"maxFeePerGasLow"`
|
||||
MaxFeePerGasMedium *big.Float `json:"maxFeePerGasMedium"`
|
||||
MaxFeePerGasHigh *big.Float `json:"maxFeePerGasHigh"`
|
||||
L1GasFee *big.Float `json:"l1GasFee"`
|
||||
EIP1559Enabled bool `json:"eip1559Enabled"`
|
||||
}
|
||||
|
||||
@@ -251,3 +255,35 @@ func (f *FeeManager) getFeeHistorySorted(chainID uint64) ([]*big.Int, error) {
|
||||
sort.Slice(fees, func(i, j int) bool { return fees[i].Cmp(fees[j]) < 0 })
|
||||
return fees, nil
|
||||
}
|
||||
|
||||
func (f *FeeManager) getL1Fee(ctx context.Context, chainID uint64, tx *ethTypes.Transaction) (uint64, error) {
|
||||
|
||||
ethClient, err := f.RPCClient.EthClient(chainID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
contractAddress, err := gaspriceoracle.ContractAddress(chainID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
contract, err := gaspriceoracle.NewGaspriceoracleCaller(contractAddress, ethClient)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
callOpt := &bind.CallOpts{}
|
||||
|
||||
data, err := tx.MarshalBinary()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
result, err := contract.GetL1Fee(callOpt, data)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return result.Uint64(), nil
|
||||
}
|
||||
|
||||
2
vendor/github.com/status-im/status-go/services/wallet/on_ramp.go
generated
vendored
2
vendor/github.com/status-im/status-go/services/wallet/on_ramp.go
generated
vendored
@@ -120,7 +120,7 @@ func (c *CryptoOnRampManager) getFromStaticDataSource() ([]byte, error) {
|
||||
"description": "Global crypto to fiat flow",
|
||||
"fees": "0.49%% - 2.9%%",
|
||||
"logoUrl": "%s",
|
||||
"siteUrl": "https://buy.ramp.network/?hostApiKey=zrtf9u2uqebeyzcs37fu5857tktr3eg9w5tffove&swapAsset=DAI,ETH,USDC,USDT",
|
||||
"siteUrl": "https://ramp.network/buy?hostApiKey=zrtf9u2uqebeyzcs37fu5857tktr3eg9w5tffove&swapAsset=DAI,ETH,USDC,USDT",
|
||||
"hostname": "ramp.network"
|
||||
},
|
||||
{
|
||||
|
||||
2
vendor/github.com/status-im/status-go/services/wallet/persistence.go
generated
vendored
2
vendor/github.com/status-im/status-go/services/wallet/persistence.go
generated
vendored
@@ -33,7 +33,7 @@ func (p *Persistence) SaveTokens(tokens map[common.Address][]Token) (err error)
|
||||
for address, addressTokens := range tokens {
|
||||
for _, t := range addressTokens {
|
||||
for chainID, b := range t.BalancesPerChain {
|
||||
if b.HasError || b.Balance.Cmp(big.NewFloat(0)) == 0 {
|
||||
if b.HasError {
|
||||
continue
|
||||
}
|
||||
_, err = tx.Exec(`INSERT INTO token_balances(user_address,token_name,token_symbol,token_address,token_decimals,token_description,token_url,balance,raw_balance,chain_id) VALUES (?,?,?,?,?,?,?,?,?,?)`, address.Hex(), t.Name, t.Symbol, b.Address.Hex(), t.Decimals, t.Description, t.AssetWebsiteURL, b.Balance.String(), b.RawBalance, chainID)
|
||||
|
||||
228
vendor/github.com/status-im/status-go/services/wallet/reader.go
generated
vendored
228
vendor/github.com/status-im/status-go/services/wallet/reader.go
generated
vendored
@@ -5,7 +5,6 @@ import (
|
||||
"math"
|
||||
"math/big"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -19,9 +18,8 @@ import (
|
||||
"github.com/status-im/status-go/services/wallet/community"
|
||||
"github.com/status-im/status-go/services/wallet/market"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
"github.com/status-im/status-go/services/wallet/transfer"
|
||||
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
"github.com/status-im/status-go/services/wallet/transfer"
|
||||
"github.com/status-im/status-go/services/wallet/walletevent"
|
||||
)
|
||||
|
||||
@@ -51,14 +49,14 @@ func belongsToMandatoryTokens(symbol string) bool {
|
||||
|
||||
func NewReader(rpcClient *rpc.Client, tokenManager *token.Manager, marketManager *market.Manager, communityManager *community.Manager, accountsDB *accounts.Database, persistence *Persistence, walletFeed *event.Feed) *Reader {
|
||||
return &Reader{
|
||||
rpcClient: rpcClient,
|
||||
tokenManager: tokenManager,
|
||||
marketManager: marketManager,
|
||||
communityManager: communityManager,
|
||||
accountsDB: accountsDB,
|
||||
persistence: persistence,
|
||||
walletFeed: walletFeed,
|
||||
lastWalletTokenUpdateTimestamp: atomic.Int64{},
|
||||
rpcClient: rpcClient,
|
||||
tokenManager: tokenManager,
|
||||
marketManager: marketManager,
|
||||
communityManager: communityManager,
|
||||
accountsDB: accountsDB,
|
||||
persistence: persistence,
|
||||
walletFeed: walletFeed,
|
||||
refreshBalanceCache: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +70,7 @@ type Reader struct {
|
||||
walletFeed *event.Feed
|
||||
cancel context.CancelFunc
|
||||
walletEventsWatcher *walletevent.Watcher
|
||||
lastWalletTokenUpdateTimestamp atomic.Int64
|
||||
lastWalletTokenUpdateTimestamp sync.Map
|
||||
reloadDelayTimer *time.Timer
|
||||
refreshBalanceCache bool
|
||||
rw sync.RWMutex
|
||||
@@ -91,11 +89,12 @@ type TokenMarketValues struct {
|
||||
}
|
||||
|
||||
type ChainBalance struct {
|
||||
RawBalance string `json:"rawBalance"`
|
||||
Balance *big.Float `json:"balance"`
|
||||
Address common.Address `json:"address"`
|
||||
ChainID uint64 `json:"chainId"`
|
||||
HasError bool `json:"hasError"`
|
||||
RawBalance string `json:"rawBalance"`
|
||||
Balance *big.Float `json:"balance"`
|
||||
Balance1DayAgo string `json:"balance1DayAgo"`
|
||||
Address common.Address `json:"address"`
|
||||
ChainID uint64 `json:"chainId"`
|
||||
HasError bool `json:"hasError"`
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
@@ -184,7 +183,7 @@ func (r *Reader) Stop() {
|
||||
|
||||
r.cancelDelayedWalletReload()
|
||||
|
||||
r.lastWalletTokenUpdateTimestamp.Store(0)
|
||||
r.lastWalletTokenUpdateTimestamp = sync.Map{}
|
||||
}
|
||||
|
||||
func (r *Reader) triggerWalletReload() {
|
||||
@@ -221,13 +220,18 @@ func (r *Reader) startWalletEventsWatcher() {
|
||||
return
|
||||
}
|
||||
|
||||
timecheck := r.lastWalletTokenUpdateTimestamp.Load() - activityReloadMarginSeconds
|
||||
if event.At > timecheck {
|
||||
r.triggerDelayedWalletReload()
|
||||
}
|
||||
for _, address := range event.Accounts {
|
||||
timestamp, ok := r.lastWalletTokenUpdateTimestamp.Load(address)
|
||||
timecheck := int64(0)
|
||||
if ok {
|
||||
timecheck = timestamp.(int64) - activityReloadMarginSeconds
|
||||
}
|
||||
|
||||
if transfer.IsTransferDetectionEvent(event.Type) {
|
||||
r.invalidateBalanceCache()
|
||||
if !ok || event.At > timecheck {
|
||||
r.triggerDelayedWalletReload()
|
||||
r.invalidateBalanceCache()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,11 +247,42 @@ func (r *Reader) stopWalletEventsWatcher() {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reader) isBalanceCacheValid() bool {
|
||||
func (r *Reader) tokensCachedForAddresses(addresses []common.Address) bool {
|
||||
for _, address := range addresses {
|
||||
cachedTokens, err := r.GetCachedWalletTokensWithoutMarketData()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
_, ok := cachedTokens[address]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *Reader) isCacheTimestampValidForAddress(address common.Address) bool {
|
||||
_, ok := r.lastWalletTokenUpdateTimestamp.Load(address)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r *Reader) areCacheTimestampsValid(addresses []common.Address) bool {
|
||||
for _, address := range addresses {
|
||||
if !r.isCacheTimestampValidForAddress(address) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *Reader) isBalanceCacheValid(addresses []common.Address) bool {
|
||||
r.rw.RLock()
|
||||
defer r.rw.RUnlock()
|
||||
|
||||
return !r.refreshBalanceCache
|
||||
return !r.refreshBalanceCache && r.tokensCachedForAddresses(addresses) && r.areCacheTimestampsValid(addresses)
|
||||
}
|
||||
|
||||
func (r *Reader) balanceRefreshed() {
|
||||
@@ -265,7 +300,7 @@ func (r *Reader) invalidateBalanceCache() {
|
||||
}
|
||||
|
||||
func (r *Reader) FetchOrGetCachedWalletBalances(ctx context.Context, addresses []common.Address) (map[common.Address][]Token, error) {
|
||||
if !r.isBalanceCacheValid() {
|
||||
if !r.isBalanceCacheValid(addresses) {
|
||||
balances, err := r.GetWalletTokenBalances(ctx, addresses)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -276,20 +311,6 @@ func (r *Reader) FetchOrGetCachedWalletBalances(ctx context.Context, addresses [
|
||||
}
|
||||
|
||||
tokens, err := r.getWalletTokenBalances(ctx, addresses, false)
|
||||
|
||||
addressWithoutCachedBalances := false
|
||||
for _, address := range addresses {
|
||||
if _, ok := tokens[address]; !ok {
|
||||
addressWithoutCachedBalances = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// there should be at least ETH balance
|
||||
if addressWithoutCachedBalances {
|
||||
return r.GetWalletTokenBalances(ctx, addresses)
|
||||
}
|
||||
|
||||
return tokens, err
|
||||
}
|
||||
|
||||
@@ -339,37 +360,49 @@ func (r *Reader) getWalletTokenBalances(ctx context.Context, addresses []common.
|
||||
|
||||
verifiedTokens, unverifiedTokens := splitVerifiedTokens(allTokens)
|
||||
|
||||
cachedBalancesPerChain := map[common.Address]map[common.Address]map[uint64]string{}
|
||||
updateAnyway := false
|
||||
cachedBalancesPerChain := map[common.Address]map[common.Address]map[uint64]ChainBalance{}
|
||||
if !updateBalances {
|
||||
for address, tokens := range cachedTokens {
|
||||
if _, ok := cachedBalancesPerChain[address]; !ok {
|
||||
cachedBalancesPerChain[address] = map[common.Address]map[uint64]ChainBalance{}
|
||||
cacheCheck:
|
||||
for _, address := range addresses {
|
||||
if res, ok := cachedTokens[address]; !ok || len(res) == 0 {
|
||||
updateAnyway = true
|
||||
break
|
||||
}
|
||||
|
||||
networkFound := map[uint64]bool{}
|
||||
for _, token := range cachedTokens[address] {
|
||||
for _, chain := range chainIDs {
|
||||
if _, ok := token.BalancesPerChain[chain]; ok {
|
||||
networkFound[chain] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, chain := range chainIDs {
|
||||
if !networkFound[chain] {
|
||||
updateAnyway = true
|
||||
break cacheCheck
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !updateBalances && !updateAnyway {
|
||||
for address, tokens := range cachedTokens {
|
||||
for _, token := range tokens {
|
||||
for _, balance := range token.BalancesPerChain {
|
||||
if _, ok := cachedBalancesPerChain[address]; !ok {
|
||||
cachedBalancesPerChain[address] = map[common.Address]map[uint64]string{}
|
||||
}
|
||||
if _, ok := cachedBalancesPerChain[address][balance.Address]; !ok {
|
||||
cachedBalancesPerChain[address][balance.Address] = map[uint64]ChainBalance{}
|
||||
cachedBalancesPerChain[address][balance.Address] = map[uint64]string{}
|
||||
}
|
||||
cachedBalancesPerChain[address][balance.Address][balance.ChainID] = balance
|
||||
cachedBalancesPerChain[address][balance.Address][balance.ChainID] = balance.RawBalance
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for _, address := range addresses {
|
||||
for _, tokenList := range [][]*token.Token{verifiedTokens, unverifiedTokens} {
|
||||
for _, tokens := range getTokenBySymbols(tokenList) {
|
||||
for _, token := range tokens {
|
||||
if _, ok := cachedBalancesPerChain[address][token.Address][token.ChainID]; !ok {
|
||||
updateAnyway = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var latestBalances map[uint64]map[common.Address]map[common.Address]*hexutil.Big
|
||||
@@ -385,7 +418,7 @@ func (r *Reader) getWalletTokenBalances(ctx context.Context, addresses []common.
|
||||
}
|
||||
|
||||
result := make(map[common.Address][]Token)
|
||||
communities := make(map[string]bool)
|
||||
dayAgoTimestamp := time.Now().Add(-24 * time.Hour).Unix()
|
||||
|
||||
for _, address := range addresses {
|
||||
for _, tokenList := range [][]*token.Token{verifiedTokens, unverifiedTokens} {
|
||||
@@ -395,19 +428,22 @@ func (r *Reader) getWalletTokenBalances(ctx context.Context, addresses []common.
|
||||
isVisible := false
|
||||
for _, token := range tokens {
|
||||
var balance *big.Float
|
||||
hexBalance := &hexutil.Big{}
|
||||
hexBalance := &big.Int{}
|
||||
if latestBalances != nil {
|
||||
hexBalance = latestBalances[token.ChainID][address][token.Address]
|
||||
balance = big.NewFloat(0.0)
|
||||
if hexBalance != nil {
|
||||
balance = new(big.Float).Quo(
|
||||
new(big.Float).SetInt(hexBalance.ToInt()),
|
||||
big.NewFloat(math.Pow(10, float64(decimals))),
|
||||
)
|
||||
}
|
||||
hexBalance = latestBalances[token.ChainID][address][token.Address].ToInt()
|
||||
} else {
|
||||
balance = cachedBalancesPerChain[address][token.Address][token.ChainID].Balance
|
||||
if cachedRawBalance, ok := cachedBalancesPerChain[address][token.Address][token.ChainID]; ok {
|
||||
hexBalance, _ = new(big.Int).SetString(cachedRawBalance, 10)
|
||||
}
|
||||
}
|
||||
balance = big.NewFloat(0.0)
|
||||
if hexBalance != nil {
|
||||
balance = new(big.Float).Quo(
|
||||
new(big.Float).SetInt(hexBalance),
|
||||
big.NewFloat(math.Pow(10, float64(decimals))),
|
||||
)
|
||||
}
|
||||
|
||||
hasError := false
|
||||
if client, ok := clients[token.ChainID]; ok {
|
||||
hasError = err != nil || !client.GetIsConnected()
|
||||
@@ -416,11 +452,12 @@ func (r *Reader) getWalletTokenBalances(ctx context.Context, addresses []common.
|
||||
isVisible = balance.Cmp(big.NewFloat(0.0)) > 0 || r.isCachedToken(cachedTokens, address, token.Symbol, token.ChainID)
|
||||
}
|
||||
balancesPerChain[token.ChainID] = ChainBalance{
|
||||
RawBalance: hexBalance.ToInt().String(),
|
||||
Balance: balance,
|
||||
Address: token.Address,
|
||||
ChainID: token.ChainID,
|
||||
HasError: hasError,
|
||||
RawBalance: hexBalance.String(),
|
||||
Balance: balance,
|
||||
Balance1DayAgo: "0",
|
||||
Address: token.Address,
|
||||
ChainID: token.ChainID,
|
||||
HasError: hasError,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,6 +465,17 @@ func (r *Reader) getWalletTokenBalances(ctx context.Context, addresses []common.
|
||||
continue
|
||||
}
|
||||
|
||||
for _, balance := range balancesPerChain {
|
||||
balance1DayAgo, err := r.tokenManager.GetTokenHistoricalBalance(address, balance.ChainID, symbol, dayAgoTimestamp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if balance1DayAgo != nil {
|
||||
balance.Balance1DayAgo = balance1DayAgo.String()
|
||||
balancesPerChain[balance.ChainID] = balance
|
||||
}
|
||||
}
|
||||
|
||||
walletToken := Token{
|
||||
Name: tokens[0].Name,
|
||||
Symbol: symbol,
|
||||
@@ -439,20 +487,12 @@ func (r *Reader) getWalletTokenBalances(ctx context.Context, addresses []common.
|
||||
Image: tokens[0].Image,
|
||||
}
|
||||
|
||||
if walletToken.CommunityData != nil {
|
||||
communities[walletToken.CommunityData.ID] = true
|
||||
}
|
||||
|
||||
result[address] = append(result[address], walletToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.lastWalletTokenUpdateTimestamp.Store(time.Now().Unix())
|
||||
|
||||
for communityID := range communities {
|
||||
r.communityManager.FetchCommunityMetadataAsync(communityID)
|
||||
}
|
||||
r.updateTokenUpdateTimestamp(addresses)
|
||||
|
||||
return result, r.persistence.SaveTokens(result)
|
||||
}
|
||||
@@ -618,8 +658,6 @@ func (r *Reader) GetWalletToken(ctx context.Context, addresses []common.Address)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
communities := make(map[string]bool)
|
||||
|
||||
for address, tokens := range result {
|
||||
for index, token := range tokens {
|
||||
marketValuesPerCurrency := make(map[string]TokenMarketValues)
|
||||
@@ -640,10 +678,6 @@ func (r *Reader) GetWalletToken(ctx context.Context, addresses []common.Address)
|
||||
}
|
||||
}
|
||||
|
||||
if token.CommunityData != nil {
|
||||
communities[token.CommunityData.ID] = true
|
||||
}
|
||||
|
||||
if _, ok := tokenDetails[token.Symbol]; !ok {
|
||||
continue
|
||||
}
|
||||
@@ -655,11 +689,7 @@ func (r *Reader) GetWalletToken(ctx context.Context, addresses []common.Address)
|
||||
}
|
||||
}
|
||||
|
||||
r.lastWalletTokenUpdateTimestamp.Store(time.Now().Unix())
|
||||
|
||||
for communityID := range communities {
|
||||
r.communityManager.FetchCommunityMetadataAsync(communityID)
|
||||
}
|
||||
r.updateTokenUpdateTimestamp(addresses)
|
||||
|
||||
return result, r.persistence.SaveTokens(result)
|
||||
}
|
||||
@@ -684,3 +714,9 @@ func (r *Reader) isCachedToken(cachedTokens map[common.Address][]Token, address
|
||||
func (r *Reader) GetCachedWalletTokensWithoutMarketData() (map[common.Address][]Token, error) {
|
||||
return r.persistence.GetTokens()
|
||||
}
|
||||
|
||||
func (r *Reader) updateTokenUpdateTimestamp(addresses []common.Address) {
|
||||
for _, address := range addresses {
|
||||
r.lastWalletTokenUpdateTimestamp.Store(address, time.Now().Unix())
|
||||
}
|
||||
}
|
||||
|
||||
115
vendor/github.com/status-im/status-go/services/wallet/router.go
generated
vendored
115
vendor/github.com/status-im/status-go/services/wallet/router.go
generated
vendored
@@ -16,7 +16,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/status-im/status-go/contracts"
|
||||
"github.com/status-im/status-go/contracts/ierc1155"
|
||||
gaspriceoracle "github.com/status-im/status-go/contracts/gas-price-oracle"
|
||||
"github.com/status-im/status-go/contracts/ierc20"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/params"
|
||||
@@ -136,7 +136,7 @@ func (s SendType) isAvailableFor(network *params.Network) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
if network.ChainID == 1 || network.ChainID == 5 {
|
||||
if network.ChainID == 1 || network.ChainID == 5 || network.ChainID == 11155111 {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -451,27 +451,28 @@ type Router struct {
|
||||
rpcClient *rpc.Client
|
||||
}
|
||||
|
||||
func (r *Router) requireApproval(ctx context.Context, sendType SendType, bridge bridge.Bridge, account common.Address, network *params.Network, token *token.Token, amountIn *big.Int) (bool, *big.Int, uint64, *common.Address, error) {
|
||||
if sendType == ERC721Transfer {
|
||||
return false, nil, 0, nil, nil
|
||||
func (r *Router) requireApproval(ctx context.Context, sendType SendType, bridge bridge.Bridge, account common.Address, network *params.Network, token *token.Token, amountIn *big.Int) (
|
||||
bool, *big.Int, uint64, uint64, *common.Address, error) {
|
||||
if sendType.IsCollectiblesTransfer() {
|
||||
return false, nil, 0, 0, nil, nil
|
||||
}
|
||||
|
||||
if token.IsNative() {
|
||||
return false, nil, 0, nil, nil
|
||||
return false, nil, 0, 0, nil, nil
|
||||
}
|
||||
contractMaker, err := contracts.NewContractMaker(r.rpcClient)
|
||||
if err != nil {
|
||||
return false, nil, 0, nil, err
|
||||
return false, nil, 0, 0, nil, err
|
||||
}
|
||||
|
||||
bridgeAddress := bridge.GetContractAddress(network, token)
|
||||
if bridgeAddress == nil {
|
||||
return false, nil, 0, nil, nil
|
||||
return false, nil, 0, 0, nil, nil
|
||||
}
|
||||
|
||||
contract, err := contractMaker.NewERC20(network.ChainID, token.Address)
|
||||
if err != nil {
|
||||
return false, nil, 0, nil, err
|
||||
return false, nil, 0, 0, nil, err
|
||||
}
|
||||
|
||||
allowance, err := contract.Allowance(&bind.CallOpts{
|
||||
@@ -479,26 +480,26 @@ func (r *Router) requireApproval(ctx context.Context, sendType SendType, bridge
|
||||
}, account, *bridgeAddress)
|
||||
|
||||
if err != nil {
|
||||
return false, nil, 0, nil, err
|
||||
return false, nil, 0, 0, nil, err
|
||||
}
|
||||
|
||||
if allowance.Cmp(amountIn) >= 0 {
|
||||
return false, nil, 0, nil, nil
|
||||
return false, nil, 0, 0, nil, nil
|
||||
}
|
||||
|
||||
ethClient, err := r.rpcClient.EthClient(network.ChainID)
|
||||
if err != nil {
|
||||
return false, nil, 0, nil, err
|
||||
return false, nil, 0, 0, nil, err
|
||||
}
|
||||
|
||||
erc20ABI, err := abi.JSON(strings.NewReader(ierc20.IERC20ABI))
|
||||
if err != nil {
|
||||
return false, nil, 0, nil, err
|
||||
return false, nil, 0, 0, nil, err
|
||||
}
|
||||
|
||||
data, err := erc20ABI.Pack("approve", bridgeAddress, amountIn)
|
||||
if err != nil {
|
||||
return false, nil, 0, nil, err
|
||||
return false, nil, 0, 0, nil, err
|
||||
}
|
||||
|
||||
estimate, err := ethClient.EstimateGas(context.Background(), ethereum.CallMsg{
|
||||
@@ -508,11 +509,25 @@ func (r *Router) requireApproval(ctx context.Context, sendType SendType, bridge
|
||||
Data: data,
|
||||
})
|
||||
if err != nil {
|
||||
return false, nil, 0, nil, err
|
||||
return false, nil, 0, 0, nil, err
|
||||
}
|
||||
|
||||
return true, amountIn, estimate, bridgeAddress, nil
|
||||
// fetching l1 fee
|
||||
oracleContractAddress, err := gaspriceoracle.ContractAddress(network.ChainID)
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, nil, err
|
||||
}
|
||||
|
||||
oracleContract, err := gaspriceoracle.NewGaspriceoracleCaller(oracleContractAddress, ethClient)
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, nil, err
|
||||
}
|
||||
|
||||
callOpt := &bind.CallOpts{}
|
||||
|
||||
l1Fee, _ := oracleContract.GetL1Fee(callOpt, data)
|
||||
|
||||
return true, amountIn, estimate, l1Fee.Uint64(), bridgeAddress, nil
|
||||
}
|
||||
|
||||
func (r *Router) getBalance(ctx context.Context, network *params.Network, token *token.Token, account common.Address) (*big.Int, error) {
|
||||
@@ -525,24 +540,27 @@ func (r *Router) getBalance(ctx context.Context, network *params.Network, token
|
||||
}
|
||||
|
||||
func (r *Router) getERC1155Balance(ctx context.Context, network *params.Network, token *token.Token, account common.Address) (*big.Int, error) {
|
||||
client, err := r.s.rpcClient.EthClient(network.ChainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tokenID, success := new(big.Int).SetString(token.Symbol, 10)
|
||||
if !success {
|
||||
return nil, errors.New("failed to convert token symbol to big.Int")
|
||||
}
|
||||
|
||||
caller, err := ierc1155.NewIerc1155Caller(token.Address, client)
|
||||
balances, err := r.s.collectiblesManager.FetchERC1155Balances(
|
||||
ctx,
|
||||
account,
|
||||
walletCommon.ChainID(network.ChainID),
|
||||
token.Address,
|
||||
[]*bigint.BigInt{&bigint.BigInt{Int: tokenID}},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return caller.BalanceOf(&bind.CallOpts{
|
||||
Context: ctx,
|
||||
}, account, tokenID)
|
||||
if len(balances) != 1 || balances[0] == nil {
|
||||
return nil, errors.New("invalid ERC1155 balance fetch response")
|
||||
}
|
||||
|
||||
return balances[0].Int, nil
|
||||
}
|
||||
|
||||
func (r *Router) suggestedRoutes(
|
||||
@@ -572,7 +590,6 @@ func (r *Router) suggestedRoutes(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
group = async.NewAtomicGroup(ctx)
|
||||
mu sync.Mutex
|
||||
@@ -658,7 +675,6 @@ func (r *Router) suggestedRoutes(
|
||||
if len(preferedChainIDs) > 0 && !containsNetworkChainID(dest, preferedChainIDs) {
|
||||
continue
|
||||
}
|
||||
|
||||
if containsNetworkChainID(dest, disabledToChaindIDs) {
|
||||
continue
|
||||
}
|
||||
@@ -692,31 +708,45 @@ func (r *Router) suggestedRoutes(
|
||||
gasLimit = sendType.EstimateGas(r.s, network, addrFrom, tokenID)
|
||||
}
|
||||
|
||||
approvalRequired, approvalAmountRequired, approvalGasLimit, l1ApprovalFee, approvalContractAddress, err := r.requireApproval(ctx, sendType, bridge, addrFrom, network, token, amountIn)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
tx, err := bridge.BuildTx(network, dest, addrFrom, addrTo, token, amountIn, bonderFees)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
l1GasFeeWei, _ := r.s.feesManager.getL1Fee(ctx, network.ChainID, tx)
|
||||
l1GasFeeWei += l1ApprovalFee
|
||||
gasFees.L1GasFee = weiToGwei(big.NewInt(int64(l1GasFeeWei)))
|
||||
|
||||
requiredNativeBalance := new(big.Int).Mul(gweiToWei(maxFees), big.NewInt(int64(gasLimit)))
|
||||
requiredNativeBalance.Add(requiredNativeBalance, new(big.Int).Mul(gweiToWei(maxFees), big.NewInt(int64(approvalGasLimit))))
|
||||
requiredNativeBalance.Add(requiredNativeBalance, big.NewInt(int64(l1GasFeeWei))) // add l1Fee to requiredNativeBalance, in case of L1 chain l1Fee is 0
|
||||
|
||||
if nativeBalance.Cmp(requiredNativeBalance) <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Removed the required fees from maxAMount in case of native token tx
|
||||
if token.IsNative() {
|
||||
maxAmountIn = (*hexutil.Big)(new(big.Int).Sub(maxAmountIn.ToInt(), requiredNativeBalance))
|
||||
}
|
||||
if nativeBalance.Cmp(requiredNativeBalance) <= 0 {
|
||||
continue
|
||||
}
|
||||
approvalRequired, approvalAmountRequired, approvalGasLimit, approvalContractAddress, err := r.requireApproval(ctx, sendType, bridge, addrFrom, network, token, amountIn)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
ethPrice := big.NewFloat(prices["ETH"])
|
||||
|
||||
approvalGasFees := new(big.Float).Mul(gweiToEth(maxFees), big.NewFloat((float64(approvalGasLimit))))
|
||||
|
||||
approvalGasCost := new(big.Float)
|
||||
approvalGasCost.Mul(
|
||||
approvalGasFees,
|
||||
big.NewFloat(prices["ETH"]),
|
||||
)
|
||||
approvalGasCost.Mul(approvalGasFees, ethPrice)
|
||||
|
||||
l1GasCost := new(big.Float)
|
||||
l1GasCost.Mul(gasFees.L1GasFee, ethPrice)
|
||||
|
||||
gasCost := new(big.Float)
|
||||
gasCost.Mul(
|
||||
new(big.Float).Mul(gweiToEth(maxFees), big.NewFloat((float64(gasLimit)))),
|
||||
big.NewFloat(prices["ETH"]),
|
||||
)
|
||||
gasCost.Mul(new(big.Float).Mul(gweiToEth(maxFees), big.NewFloat(float64(gasLimit))), ethPrice)
|
||||
|
||||
tokenFeesAsFloat := new(big.Float).Quo(
|
||||
new(big.Float).SetInt(tokenFees),
|
||||
@@ -728,6 +758,7 @@ func (r *Router) suggestedRoutes(
|
||||
cost := new(big.Float)
|
||||
cost.Add(tokenCost, gasCost)
|
||||
cost.Add(cost, approvalGasCost)
|
||||
cost.Add(cost, l1GasCost)
|
||||
mu.Lock()
|
||||
candidates = append(candidates, &Path{
|
||||
BridgeName: bridge.Name(),
|
||||
|
||||
27
vendor/github.com/status-im/status-go/services/wallet/saved_addresses.go
generated
vendored
27
vendor/github.com/status-im/status-go/services/wallet/saved_addresses.go
generated
vendored
@@ -2,6 +2,7 @@ package wallet
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@@ -31,6 +32,32 @@ func (s *SavedAddress) ID() string {
|
||||
return fmt.Sprintf("%s-%t", s.Address.Hex(), s.IsTest)
|
||||
}
|
||||
|
||||
func (s *SavedAddress) MarshalJSON() ([]byte, error) {
|
||||
item := struct {
|
||||
Address common.Address `json:"address"`
|
||||
MixedcaseAddress string `json:"mixedcaseAddress"`
|
||||
Name string `json:"name"`
|
||||
ChainShortNames string `json:"chainShortNames"`
|
||||
ENSName string `json:"ens"`
|
||||
ColorID multiAccCommon.CustomizationColor `json:"colorId"`
|
||||
IsTest bool `json:"isTest"`
|
||||
CreatedAt int64 `json:"createdAt"`
|
||||
Removed bool `json:"removed"`
|
||||
}{
|
||||
Address: s.Address,
|
||||
MixedcaseAddress: s.Address.Hex(),
|
||||
Name: s.Name,
|
||||
ChainShortNames: s.ChainShortNames,
|
||||
ENSName: s.ENSName,
|
||||
ColorID: s.ColorID,
|
||||
IsTest: s.IsTest,
|
||||
CreatedAt: s.CreatedAt,
|
||||
Removed: s.Removed,
|
||||
}
|
||||
|
||||
return json.Marshal(item)
|
||||
}
|
||||
|
||||
type SavedAddressesManager struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
23
vendor/github.com/status-im/status-go/services/wallet/service.go
generated
vendored
23
vendor/github.com/status-im/status-go/services/wallet/service.go
generated
vendored
@@ -121,7 +121,7 @@ func NewService(
|
||||
raribleClient := rarible.NewClient(config.WalletConfig.RaribleMainnetAPIKey, config.WalletConfig.RaribleTestnetAPIKey)
|
||||
alchemyClient := alchemy.NewClient(config.WalletConfig.AlchemyAPIKeys)
|
||||
|
||||
// Try OpenSea, Infura, Alchemy in that order
|
||||
// Collectible providers in priority order (i.e. provider N+1 will be tried only if provider N fails)
|
||||
contractOwnershipProviders := []thirdparty.CollectibleContractOwnershipProvider{
|
||||
raribleClient,
|
||||
alchemyClient,
|
||||
@@ -145,7 +145,26 @@ func NewService(
|
||||
openseaV2Client,
|
||||
}
|
||||
|
||||
collectiblesManager := collectibles.NewManager(db, rpcClient, communityManager, contractOwnershipProviders, accountOwnershipProviders, collectibleDataProviders, collectionDataProviders, mediaServer, feed)
|
||||
collectibleSearchProviders := []thirdparty.CollectibleSearchProvider{
|
||||
raribleClient,
|
||||
}
|
||||
|
||||
collectibleProviders := thirdparty.CollectibleProviders{
|
||||
ContractOwnershipProviders: contractOwnershipProviders,
|
||||
AccountOwnershipProviders: accountOwnershipProviders,
|
||||
CollectibleDataProviders: collectibleDataProviders,
|
||||
CollectionDataProviders: collectionDataProviders,
|
||||
SearchProviders: collectibleSearchProviders,
|
||||
}
|
||||
|
||||
collectiblesManager := collectibles.NewManager(
|
||||
db,
|
||||
rpcClient,
|
||||
communityManager,
|
||||
collectibleProviders,
|
||||
mediaServer,
|
||||
feed,
|
||||
)
|
||||
collectibles := collectibles.NewService(db, feed, accountsDB, accountFeed, settingsFeed, communityManager, rpcClient.NetworkManager, collectiblesManager)
|
||||
|
||||
activity := activity.NewService(db, tokenManager, collectiblesManager, feed, pendingTxManager)
|
||||
|
||||
20
vendor/github.com/status-im/status-go/services/wallet/thirdparty/alchemy/client.go
generated
vendored
20
vendor/github.com/status-im/status-go/services/wallet/thirdparty/alchemy/client.go
generated
vendored
@@ -33,8 +33,6 @@ func getBaseURL(chainID walletCommon.ChainID) (string, error) {
|
||||
return "https://eth-sepolia.g.alchemy.com", nil
|
||||
case walletCommon.OptimismMainnet:
|
||||
return "https://opt-mainnet.g.alchemy.com", nil
|
||||
case walletCommon.OptimismGoerli:
|
||||
return "https://opt-goerli.g.alchemy.com", nil
|
||||
case walletCommon.OptimismSepolia:
|
||||
return "https://opt-sepolia.g.alchemy.com", nil
|
||||
case walletCommon.ArbitrumMainnet:
|
||||
@@ -129,14 +127,13 @@ func (o *Client) doPostWithJSON(ctx context.Context, url string, payload any) (*
|
||||
}
|
||||
|
||||
func (o *Client) doWithRetries(req *http.Request) (*http.Response, error) {
|
||||
b := backoff.ExponentialBackOff{
|
||||
InitialInterval: time.Millisecond * 1000,
|
||||
RandomizationFactor: 0.1,
|
||||
Multiplier: 1.5,
|
||||
MaxInterval: time.Second * 32,
|
||||
MaxElapsedTime: time.Second * 128,
|
||||
Clock: backoff.SystemClock,
|
||||
}
|
||||
b := backoff.NewExponentialBackOff()
|
||||
b.InitialInterval = time.Millisecond * 1000
|
||||
b.RandomizationFactor = 0.1
|
||||
b.Multiplier = 1.5
|
||||
b.MaxInterval = time.Second * 32
|
||||
b.MaxElapsedTime = time.Second * 70
|
||||
|
||||
b.Reset()
|
||||
|
||||
op := func() (*http.Response, error) {
|
||||
@@ -151,12 +148,13 @@ func (o *Client) doWithRetries(req *http.Request) (*http.Response, error) {
|
||||
|
||||
err = fmt.Errorf("unsuccessful request: %d %s", resp.StatusCode, http.StatusText(resp.StatusCode))
|
||||
if resp.StatusCode == http.StatusTooManyRequests {
|
||||
log.Error("doWithRetries failed with http.StatusTooManyRequests", "provider", o.ID(), "elapsed time", b.GetElapsedTime(), "next backoff", b.NextBackOff())
|
||||
return nil, err
|
||||
}
|
||||
return nil, backoff.Permanent(err)
|
||||
}
|
||||
|
||||
return backoff.RetryWithData(op, &b)
|
||||
return backoff.RetryWithData(op, b)
|
||||
}
|
||||
|
||||
func (o *Client) FetchCollectibleOwnersByContractAddress(ctx context.Context, chainID walletCommon.ChainID, contractAddress common.Address) (*thirdparty.CollectibleContractOwnership, error) {
|
||||
|
||||
@@ -87,7 +87,7 @@ const ownedCollectiblesJSON = `{
|
||||
},
|
||||
"owners": null,
|
||||
"timeLastUpdated": "2024-01-03T19:11:04.681Z",
|
||||
"balance": "1",
|
||||
"balance": "15",
|
||||
"acquiredAt": {
|
||||
"blockTimestamp": null,
|
||||
"blockNumber": null
|
||||
|
||||
2
vendor/github.com/status-im/status-go/services/wallet/thirdparty/alchemy/types.go
generated
vendored
2
vendor/github.com/status-im/status-go/services/wallet/thirdparty/alchemy/types.go
generated
vendored
@@ -133,6 +133,7 @@ type Asset struct {
|
||||
Image Image `json:"image"`
|
||||
Raw Raw `json:"raw"`
|
||||
TokenURI string `json:"tokenUri"`
|
||||
Balance *bigint.BigInt `json:"balance,omitempty"`
|
||||
}
|
||||
|
||||
type OwnedNFTList struct {
|
||||
@@ -216,6 +217,7 @@ func (c *Asset) toCommon(id thirdparty.CollectibleUniqueID) thirdparty.FullColle
|
||||
return thirdparty.FullCollectibleData{
|
||||
CollectibleData: c.toCollectiblesData(id),
|
||||
CollectionData: &contractData,
|
||||
AccountBalance: c.Balance,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
90
vendor/github.com/status-im/status-go/services/wallet/thirdparty/coingecko/client.go
generated
vendored
90
vendor/github.com/status-im/status-go/services/wallet/thirdparty/coingecko/client.go
generated
vendored
@@ -14,45 +14,52 @@ import (
|
||||
)
|
||||
|
||||
var coinGeckoMapping = map[string]string{
|
||||
"STT": "status",
|
||||
"SNT": "status",
|
||||
"ETH": "ethereum",
|
||||
"AST": "airswap",
|
||||
"AMB": "",
|
||||
"ABT": "arcblock",
|
||||
"ATM": "",
|
||||
"BNB": "binancecoin",
|
||||
"BLT": "bloom",
|
||||
"CDT": "",
|
||||
"COMP": "compound-coin",
|
||||
"EDG": "edgeless",
|
||||
"ELF": "",
|
||||
"ENG": "enigma",
|
||||
"EOS": "eos",
|
||||
"GEN": "daostack",
|
||||
"MANA": "decentraland-wormhole",
|
||||
"LEND": "ethlend",
|
||||
"LRC": "loopring",
|
||||
"MET": "metronome",
|
||||
"POLY": "polymath",
|
||||
"PPT": "populous",
|
||||
"SAN": "santiment-network-token",
|
||||
"DNT": "district0x",
|
||||
"SPN": "sapien",
|
||||
"USDS": "stableusd",
|
||||
"STX": "stox",
|
||||
"SUB": "substratum",
|
||||
"PAY": "tenx",
|
||||
"GRT": "the-graph",
|
||||
"TNT": "tierion",
|
||||
"TRX": "tron",
|
||||
"TGT": "",
|
||||
"RARE": "superrare",
|
||||
"UNI": "uniswap",
|
||||
"USDC": "usd-coin",
|
||||
"USDP": "paxos-standard",
|
||||
"VRS": "",
|
||||
"TIME": "",
|
||||
"STT": "status",
|
||||
"SNT": "status",
|
||||
"ETH": "ethereum",
|
||||
"AST": "airswap",
|
||||
"AMB": "",
|
||||
"ABT": "arcblock",
|
||||
"ATM": "",
|
||||
"BNB": "binancecoin",
|
||||
"BLT": "bloom",
|
||||
"CDT": "",
|
||||
"COMP": "compound-coin",
|
||||
"EDG": "edgeless",
|
||||
"ELF": "",
|
||||
"ENG": "enigma",
|
||||
"EOS": "eos",
|
||||
"GEN": "daostack",
|
||||
"MANA": "decentraland-wormhole",
|
||||
"LEND": "ethlend",
|
||||
"LRC": "loopring",
|
||||
"MET": "metronome",
|
||||
"POLY": "polymath",
|
||||
"PPT": "populous",
|
||||
"SAN": "santiment-network-token",
|
||||
"DNT": "district0x",
|
||||
"SPN": "sapien",
|
||||
"USDS": "stableusd",
|
||||
"STX": "stox",
|
||||
"SUB": "substratum",
|
||||
"PAY": "tenx",
|
||||
"GRT": "the-graph",
|
||||
"TNT": "tierion",
|
||||
"TRX": "tron",
|
||||
"TGT": "",
|
||||
"RARE": "superrare",
|
||||
"UNI": "uniswap",
|
||||
"USDC": "usd-coin",
|
||||
"USDP": "paxos-standard",
|
||||
"VRS": "",
|
||||
"TIME": "",
|
||||
"USDT": "tether",
|
||||
"SHIB": "shiba-inu",
|
||||
"LINK": "chainlink",
|
||||
"MATIC": "matic-network",
|
||||
"DAI": "dai",
|
||||
"ARB": "arbitrum",
|
||||
"OP": "optimism",
|
||||
}
|
||||
|
||||
const baseURL = "https://api.coingecko.com/api/v3/"
|
||||
@@ -150,6 +157,7 @@ func (c *Client) mapSymbolsToIds(symbols []string) ([]string, error) {
|
||||
ids = append(ids, token.ID)
|
||||
}
|
||||
}
|
||||
ids = utils.RemoveDuplicates(ids)
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
@@ -182,7 +190,7 @@ func (c *Client) FetchPrices(symbols []string, currencies []string) (map[string]
|
||||
prices := make(map[string]map[string]float64)
|
||||
err = json.Unmarshal(body, &prices)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("%s - %s", err, string(body))
|
||||
}
|
||||
|
||||
result := make(map[string]map[string]float64)
|
||||
@@ -240,7 +248,7 @@ func (c *Client) FetchTokenMarketValues(symbols []string, currency string) (map[
|
||||
var marketValues []GeckoMarketValues
|
||||
err = json.Unmarshal(body, &marketValues)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("%s - %s", err, string(body))
|
||||
}
|
||||
|
||||
result := make(map[string]thirdparty.TokenMarketValues)
|
||||
|
||||
103
vendor/github.com/status-im/status-go/services/wallet/thirdparty/collectible_types.go
generated
vendored
103
vendor/github.com/status-im/status-go/services/wallet/thirdparty/collectible_types.go
generated
vendored
@@ -97,6 +97,45 @@ func GroupContractIDsByChainID(ids []ContractID) map[w_common.ChainID][]Contract
|
||||
return ret
|
||||
}
|
||||
|
||||
func GroupCollectiblesByChainID(collectibles []*FullCollectibleData) map[w_common.ChainID][]*FullCollectibleData {
|
||||
ret := make(map[w_common.ChainID][]*FullCollectibleData)
|
||||
|
||||
for i, collectible := range collectibles {
|
||||
chainID := collectible.CollectibleData.ID.ContractID.ChainID
|
||||
if _, ok := ret[chainID]; !ok {
|
||||
ret[chainID] = make([]*FullCollectibleData, 0, len(collectibles))
|
||||
}
|
||||
ret[chainID] = append(ret[chainID], collectibles[i])
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func GroupCollectiblesByContractAddress(collectibles []*FullCollectibleData) map[common.Address][]*FullCollectibleData {
|
||||
ret := make(map[common.Address][]*FullCollectibleData)
|
||||
|
||||
for i, collectible := range collectibles {
|
||||
contractAddress := collectible.CollectibleData.ID.ContractID.Address
|
||||
if _, ok := ret[contractAddress]; !ok {
|
||||
ret[contractAddress] = make([]*FullCollectibleData, 0, len(collectibles))
|
||||
}
|
||||
ret[contractAddress] = append(ret[contractAddress], collectibles[i])
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func GroupCollectiblesByChainIDAndContractAddress(collectibles []*FullCollectibleData) map[w_common.ChainID]map[common.Address][]*FullCollectibleData {
|
||||
ret := make(map[w_common.ChainID]map[common.Address][]*FullCollectibleData)
|
||||
|
||||
collectiblesByChainID := GroupCollectiblesByChainID(collectibles)
|
||||
for chainID, chainCollectibles := range collectiblesByChainID {
|
||||
ret[chainID] = GroupCollectiblesByContractAddress(chainCollectibles)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
type CollectionTrait struct {
|
||||
Min float64 `json:"min"`
|
||||
Max float64 `json:"max"`
|
||||
@@ -138,6 +177,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.
|
||||
@@ -152,7 +192,8 @@ type FullCollectibleData struct {
|
||||
CollectionData *CollectionData
|
||||
CommunityInfo *CommunityInfo
|
||||
CollectibleCommunityInfo *CollectibleCommunityInfo
|
||||
Ownership []AccountBalance
|
||||
Ownership []AccountBalance // This is a list of all the owners of the collectible
|
||||
AccountBalance *bigint.BigInt // This is the balance of the collectible for the requested account
|
||||
}
|
||||
|
||||
type CollectiblesContainer[T any] struct {
|
||||
@@ -162,29 +203,38 @@ type CollectiblesContainer[T any] struct {
|
||||
Provider string
|
||||
}
|
||||
|
||||
type CollectibleOwnershipContainer CollectiblesContainer[CollectibleUniqueID]
|
||||
type CollectibleOwnershipContainer CollectiblesContainer[CollectibleIDBalance]
|
||||
type CollectionDataContainer CollectiblesContainer[CollectionData]
|
||||
type CollectibleDataContainer CollectiblesContainer[CollectibleData]
|
||||
type FullCollectibleDataContainer CollectiblesContainer[FullCollectibleData]
|
||||
|
||||
// Tried to find a way to make this generic, but couldn't, so the code below is duplicated somewhere else
|
||||
func collectibleItemsToIDs(items []FullCollectibleData) []CollectibleUniqueID {
|
||||
ret := make([]CollectibleUniqueID, 0, len(items))
|
||||
func collectibleItemsToBalances(items []FullCollectibleData) []CollectibleIDBalance {
|
||||
ret := make([]CollectibleIDBalance, 0, len(items))
|
||||
for _, item := range items {
|
||||
ret = append(ret, item.CollectibleData.ID)
|
||||
balance := CollectibleIDBalance{
|
||||
ID: item.CollectibleData.ID,
|
||||
Balance: item.AccountBalance,
|
||||
}
|
||||
ret = append(ret, balance)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *FullCollectibleDataContainer) ToOwnershipContainer() CollectibleOwnershipContainer {
|
||||
return CollectibleOwnershipContainer{
|
||||
Items: collectibleItemsToIDs(c.Items),
|
||||
Items: collectibleItemsToBalances(c.Items),
|
||||
NextCursor: c.NextCursor,
|
||||
PreviousCursor: c.PreviousCursor,
|
||||
Provider: c.Provider,
|
||||
}
|
||||
}
|
||||
|
||||
type CollectibleIDBalance struct {
|
||||
ID CollectibleUniqueID `json:"id"`
|
||||
Balance *bigint.BigInt `json:"balance"`
|
||||
}
|
||||
|
||||
type TokenBalance struct {
|
||||
TokenID *bigint.BigInt `json:"tokenId"`
|
||||
Balance *bigint.BigInt `json:"balance"`
|
||||
@@ -228,3 +278,44 @@ type CollectionDataProvider interface {
|
||||
CollectibleProvider
|
||||
FetchCollectionsDataByContractID(ctx context.Context, ids []ContractID) ([]CollectionData, error)
|
||||
}
|
||||
|
||||
type CollectibleSearchProvider interface {
|
||||
CollectibleProvider
|
||||
SearchCollections(ctx context.Context, chainID w_common.ChainID, text string, cursor string, limit int) (*CollectionDataContainer, error)
|
||||
SearchCollectibles(ctx context.Context, chainID w_common.ChainID, collections []common.Address, text string, cursor string, limit int) (*FullCollectibleDataContainer, error)
|
||||
}
|
||||
|
||||
type CollectibleProviders struct {
|
||||
ContractOwnershipProviders []CollectibleContractOwnershipProvider
|
||||
AccountOwnershipProviders []CollectibleAccountOwnershipProvider
|
||||
CollectibleDataProviders []CollectibleDataProvider
|
||||
CollectionDataProviders []CollectionDataProvider
|
||||
SearchProviders []CollectibleSearchProvider
|
||||
}
|
||||
|
||||
func (p *CollectibleProviders) GetProviderList() []CollectibleProvider {
|
||||
ret := make([]CollectibleProvider, 0)
|
||||
|
||||
uniqueProviders := make(map[string]CollectibleProvider)
|
||||
for _, provider := range p.ContractOwnershipProviders {
|
||||
uniqueProviders[provider.ID()] = provider
|
||||
}
|
||||
for _, provider := range p.AccountOwnershipProviders {
|
||||
uniqueProviders[provider.ID()] = provider
|
||||
}
|
||||
for _, provider := range p.CollectibleDataProviders {
|
||||
uniqueProviders[provider.ID()] = provider
|
||||
}
|
||||
for _, provider := range p.CollectionDataProviders {
|
||||
uniqueProviders[provider.ID()] = provider
|
||||
}
|
||||
for _, provider := range p.SearchProviders {
|
||||
uniqueProviders[provider.ID()] = provider
|
||||
}
|
||||
|
||||
for _, provider := range uniqueProviders {
|
||||
ret = append(ret, provider)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
3
vendor/github.com/status-im/status-go/services/wallet/thirdparty/community_types.go
generated
vendored
3
vendor/github.com/status-im/status-go/services/wallet/thirdparty/community_types.go
generated
vendored
@@ -9,9 +9,10 @@ type CommunityInfo struct {
|
||||
}
|
||||
|
||||
type CommunityInfoProvider interface {
|
||||
GetCommunityInfoFromDB(communityID string) (*CommunityInfo, error)
|
||||
FetchCommunityInfo(communityID string) (*CommunityInfo, error)
|
||||
|
||||
// Collectible-related methods
|
||||
GetCommunityID(tokenURI string) string
|
||||
FillCollectibleMetadata(collectible *FullCollectibleData) error
|
||||
FillCollectiblesMetadata(communityID string, cs []*FullCollectibleData) (bool, error)
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func (c *Client) FetchPrices(symbols []string, currencies []string) (map[string]
|
||||
prices := make(map[string]map[string]float64)
|
||||
err = json.Unmarshal(body, &prices)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("%s - %s", err, string(body))
|
||||
}
|
||||
|
||||
for _, symbol := range smbls {
|
||||
@@ -132,8 +132,12 @@ func (c *Client) FetchTokenMarketValues(symbols []string, currency string) (map[
|
||||
|
||||
container := MarketValuesContainer{}
|
||||
err = json.Unmarshal(body, &container)
|
||||
|
||||
if len(container.Raw) == 0 {
|
||||
return nil, fmt.Errorf("no data found - %s", string(body))
|
||||
}
|
||||
if err != nil {
|
||||
return item, err
|
||||
return nil, fmt.Errorf("%s - %s", err, string(body))
|
||||
}
|
||||
|
||||
for _, symbol := range smbls {
|
||||
|
||||
@@ -157,7 +157,9 @@ func (o *ClientV2) fetchAssets(ctx context.Context, chainID walletCommon.ChainID
|
||||
|
||||
body, err := o.client.doGetRequest(ctx, url, o.apiKey)
|
||||
if err != nil {
|
||||
o.connectionStatus.SetIsConnected(false)
|
||||
if ctx.Err() == nil {
|
||||
o.connectionStatus.SetIsConnected(false)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
o.connectionStatus.SetIsConnected(true)
|
||||
@@ -274,7 +276,9 @@ func (o *ClientV2) fetchCollectionDataBySlug(ctx context.Context, chainID wallet
|
||||
|
||||
body, err := o.client.doGetRequest(ctx, url, o.apiKey)
|
||||
if err != nil {
|
||||
o.connectionStatus.SetIsConnected(false)
|
||||
if ctx.Err() == nil {
|
||||
o.connectionStatus.SetIsConnected(false)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
o.connectionStatus.SetIsConnected(true)
|
||||
|
||||
215
vendor/github.com/status-im/status-go/services/wallet/thirdparty/rarible/client.go
generated
vendored
215
vendor/github.com/status-im/status-go/services/wallet/thirdparty/rarible/client.go
generated
vendored
@@ -24,6 +24,8 @@ import (
|
||||
const ownedNFTLimit = 100
|
||||
const collectionOwnershipLimit = 50
|
||||
const nftMetadataBatchLimit = 50
|
||||
const searchCollectiblesLimit = 1000
|
||||
const searchCollectionsLimit = 1000
|
||||
|
||||
func (o *Client) ID() string {
|
||||
return RaribleID
|
||||
@@ -42,7 +44,7 @@ func getBaseURL(chainID walletCommon.ChainID) (string, error) {
|
||||
switch uint64(chainID) {
|
||||
case walletCommon.EthereumMainnet, walletCommon.ArbitrumMainnet:
|
||||
return "https://api.rarible.org", nil
|
||||
case walletCommon.EthereumGoerli, walletCommon.ArbitrumSepolia:
|
||||
case walletCommon.ArbitrumSepolia:
|
||||
return "https://testnet-api.rarible.org", nil
|
||||
}
|
||||
|
||||
@@ -143,14 +145,13 @@ func (o *Client) doPostWithJSON(ctx context.Context, url string, payload any, ap
|
||||
}
|
||||
|
||||
func (o *Client) doWithRetries(req *http.Request, apiKey string) (*http.Response, error) {
|
||||
b := backoff.ExponentialBackOff{
|
||||
InitialInterval: time.Millisecond * 1000,
|
||||
RandomizationFactor: 0.1,
|
||||
Multiplier: 1.5,
|
||||
MaxInterval: time.Second * 32,
|
||||
MaxElapsedTime: time.Second * 128,
|
||||
Clock: backoff.SystemClock,
|
||||
}
|
||||
b := backoff.NewExponentialBackOff()
|
||||
b.InitialInterval = time.Millisecond * 1000
|
||||
b.RandomizationFactor = 0.1
|
||||
b.Multiplier = 1.5
|
||||
b.MaxInterval = time.Second * 32
|
||||
b.MaxElapsedTime = time.Second * 70
|
||||
|
||||
b.Reset()
|
||||
|
||||
req.Header.Set("X-API-KEY", apiKey)
|
||||
@@ -167,12 +168,13 @@ func (o *Client) doWithRetries(req *http.Request, apiKey string) (*http.Response
|
||||
|
||||
err = fmt.Errorf("unsuccessful request: %d %s", resp.StatusCode, http.StatusText(resp.StatusCode))
|
||||
if resp.StatusCode == http.StatusTooManyRequests {
|
||||
log.Error("doWithRetries failed with http.StatusTooManyRequests", "provider", o.ID(), "elapsed time", b.GetElapsedTime(), "next backoff", b.NextBackOff())
|
||||
return nil, err
|
||||
}
|
||||
return nil, backoff.Permanent(err)
|
||||
}
|
||||
|
||||
return backoff.RetryWithData(op, &b)
|
||||
return backoff.RetryWithData(op, b)
|
||||
}
|
||||
|
||||
func (o *Client) FetchCollectibleOwnersByContractAddress(ctx context.Context, chainID walletCommon.ChainID, contractAddress common.Address) (*thirdparty.CollectibleContractOwnership, error) {
|
||||
@@ -197,7 +199,9 @@ func (o *Client) FetchCollectibleOwnersByContractAddress(ctx context.Context, ch
|
||||
|
||||
resp, err := o.doQuery(ctx, url, o.getAPIKey(chainID))
|
||||
if err != nil {
|
||||
o.connectionStatus.SetIsConnected(false)
|
||||
if ctx.Err() == nil {
|
||||
o.connectionStatus.SetIsConnected(false)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
o.connectionStatus.SetIsConnected(true)
|
||||
@@ -258,7 +262,9 @@ func (o *Client) FetchAllAssetsByOwner(ctx context.Context, chainID walletCommon
|
||||
|
||||
resp, err := o.doQuery(ctx, url, o.getAPIKey(chainID))
|
||||
if err != nil {
|
||||
o.connectionStatus.SetIsConnected(false)
|
||||
if ctx.Err() == nil {
|
||||
o.connectionStatus.SetIsConnected(false)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
o.connectionStatus.SetIsConnected(true)
|
||||
@@ -398,7 +404,9 @@ func (o *Client) FetchCollectionsDataByContractID(ctx context.Context, contractI
|
||||
|
||||
resp, err := o.doQuery(ctx, url, o.getAPIKey(contractID.ChainID))
|
||||
if err != nil {
|
||||
o.connectionStatus.SetIsConnected(false)
|
||||
if ctx.Err() == nil {
|
||||
o.connectionStatus.SetIsConnected(false)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
o.connectionStatus.SetIsConnected(true)
|
||||
@@ -426,3 +434,184 @@ func (o *Client) FetchCollectionsDataByContractID(ctx context.Context, contractI
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (o *Client) searchCollectibles(ctx context.Context, chainID walletCommon.ChainID, collections []common.Address, fullText CollectibleFilterFullText, sort CollectibleFilterContainerSort, cursor string, limit int) (*thirdparty.FullCollectibleDataContainer, error) {
|
||||
baseURL, err := getItemBaseURL(chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/search", baseURL)
|
||||
|
||||
ret := &thirdparty.FullCollectibleDataContainer{
|
||||
Provider: o.ID(),
|
||||
Items: make([]thirdparty.FullCollectibleData, 0),
|
||||
PreviousCursor: cursor,
|
||||
NextCursor: "",
|
||||
}
|
||||
|
||||
if fullText.Text == "" {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
tmpLimit := searchCollectiblesLimit
|
||||
if limit > thirdparty.FetchNoLimit && limit < tmpLimit {
|
||||
tmpLimit = limit
|
||||
}
|
||||
|
||||
blockchainString := chainIDToChainString(chainID)
|
||||
|
||||
filterContainer := CollectibleFilterContainer{
|
||||
Cursor: cursor,
|
||||
Limit: tmpLimit,
|
||||
Filter: CollectibleFilter{
|
||||
Blockchains: []string{blockchainString},
|
||||
Deleted: false,
|
||||
FullText: fullText,
|
||||
},
|
||||
Sort: sort,
|
||||
}
|
||||
|
||||
for _, collection := range collections {
|
||||
filterContainer.Filter.Collections = append(filterContainer.Filter.Collections, fmt.Sprintf("%s:%s", blockchainString, collection.String()))
|
||||
}
|
||||
|
||||
for {
|
||||
resp, err := o.doPostWithJSON(ctx, url, filterContainer, o.getAPIKey(chainID))
|
||||
if err != nil {
|
||||
if ctx.Err() == nil {
|
||||
o.connectionStatus.SetIsConnected(false)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
o.connectionStatus.SetIsConnected(true)
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if Json is not returned there must be an error
|
||||
if !json.Valid(body) {
|
||||
return nil, fmt.Errorf("invalid json: %s", string(body))
|
||||
}
|
||||
|
||||
var collectibles CollectiblesContainer
|
||||
err = json.Unmarshal(body, &collectibles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret.Items = append(ret.Items, raribleToCollectiblesData(collectibles.Collectibles, chainID.IsMainnet())...)
|
||||
ret.NextCursor = collectibles.Continuation
|
||||
|
||||
if len(ret.NextCursor) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
filterContainer.Cursor = ret.NextCursor
|
||||
|
||||
if limit != thirdparty.FetchNoLimit && len(ret.Items) >= limit {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (o *Client) searchCollections(ctx context.Context, chainID walletCommon.ChainID, text string, cursor string, limit int) (*thirdparty.CollectionDataContainer, error) {
|
||||
baseURL, err := getCollectionBaseURL(chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/search", baseURL)
|
||||
|
||||
ret := &thirdparty.CollectionDataContainer{
|
||||
Provider: o.ID(),
|
||||
Items: make([]thirdparty.CollectionData, 0),
|
||||
PreviousCursor: cursor,
|
||||
NextCursor: "",
|
||||
}
|
||||
|
||||
if text == "" {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
tmpLimit := searchCollectionsLimit
|
||||
if limit > thirdparty.FetchNoLimit && limit < tmpLimit {
|
||||
tmpLimit = limit
|
||||
}
|
||||
|
||||
filterContainer := CollectionFilterContainer{
|
||||
Cursor: cursor,
|
||||
Limit: tmpLimit,
|
||||
Filter: CollectionFilter{
|
||||
Blockchains: []string{chainIDToChainString(chainID)},
|
||||
Text: text,
|
||||
},
|
||||
}
|
||||
|
||||
for {
|
||||
resp, err := o.doPostWithJSON(ctx, url, filterContainer, o.getAPIKey(chainID))
|
||||
if err != nil {
|
||||
if ctx.Err() == nil {
|
||||
o.connectionStatus.SetIsConnected(false)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
o.connectionStatus.SetIsConnected(true)
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if Json is not returned there must be an error
|
||||
if !json.Valid(body) {
|
||||
return nil, fmt.Errorf("invalid json: %s", string(body))
|
||||
}
|
||||
|
||||
var collections CollectionsContainer
|
||||
err = json.Unmarshal(body, &collections)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret.Items = append(ret.Items, raribleToCollectionsData(collections.Collections, chainID.IsMainnet())...)
|
||||
ret.NextCursor = collections.Continuation
|
||||
|
||||
if len(ret.NextCursor) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
filterContainer.Cursor = ret.NextCursor
|
||||
|
||||
if limit != thirdparty.FetchNoLimit && len(ret.Items) >= limit {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (o *Client) SearchCollections(ctx context.Context, chainID walletCommon.ChainID, text string, cursor string, limit int) (*thirdparty.CollectionDataContainer, error) {
|
||||
return o.searchCollections(ctx, chainID, text, cursor, limit)
|
||||
}
|
||||
|
||||
func (o *Client) SearchCollectibles(ctx context.Context, chainID walletCommon.ChainID, collections []common.Address, text string, cursor string, limit int) (*thirdparty.FullCollectibleDataContainer, error) {
|
||||
fullText := CollectibleFilterFullText{
|
||||
Text: text,
|
||||
Fields: []string{
|
||||
CollectibleFilterFullTextFieldName,
|
||||
},
|
||||
}
|
||||
|
||||
sort := CollectibleFilterContainerSortRelevance
|
||||
|
||||
return o.searchCollectibles(ctx, chainID, collections, fullText, sort, cursor, limit)
|
||||
}
|
||||
|
||||
63
vendor/github.com/status-im/status-go/services/wallet/thirdparty/rarible/types.go
generated
vendored
63
vendor/github.com/status-im/status-go/services/wallet/thirdparty/rarible/types.go
generated
vendored
@@ -110,6 +110,51 @@ type BatchTokenIDs struct {
|
||||
IDs []string `json:"ids"`
|
||||
}
|
||||
|
||||
type CollectibleFilterFullTextField = string
|
||||
|
||||
const (
|
||||
CollectibleFilterFullTextFieldName = "NAME"
|
||||
CollectibleFilterFullTextFieldDescription = "DESCRIPTION"
|
||||
)
|
||||
|
||||
type CollectibleFilterFullText struct {
|
||||
Text string `json:"text"`
|
||||
Fields []CollectibleFilterFullTextField `json:"fields"`
|
||||
}
|
||||
|
||||
type CollectibleFilter struct {
|
||||
Blockchains []string `json:"blockchains"`
|
||||
Collections []string `json:"collections,omitempty"`
|
||||
Deleted bool `json:"deleted"`
|
||||
FullText CollectibleFilterFullText `json:"fullText"`
|
||||
}
|
||||
|
||||
type CollectibleFilterContainerSort = string
|
||||
|
||||
const (
|
||||
CollectibleFilterContainerSortRelevance = "RELEVANCE"
|
||||
CollectibleFilterContainerSortLatest = "LATEST"
|
||||
CollectibleFilterContainerSortEarliest = "EARLIEST"
|
||||
)
|
||||
|
||||
type CollectibleFilterContainer struct {
|
||||
Limit int `json:"size"`
|
||||
Cursor string `json:"continuation"`
|
||||
Filter CollectibleFilter `json:"filter"`
|
||||
Sort CollectibleFilterContainerSort `json:"sort"`
|
||||
}
|
||||
|
||||
type CollectionFilter struct {
|
||||
Blockchains []string `json:"blockchains"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
type CollectionFilterContainer struct {
|
||||
Limit int `json:"size"`
|
||||
Cursor string `json:"continuation"`
|
||||
Filter CollectionFilter `json:"filter"`
|
||||
}
|
||||
|
||||
type CollectiblesContainer struct {
|
||||
Continuation string `json:"continuation"`
|
||||
Collectibles []Collectible `json:"items"`
|
||||
@@ -157,6 +202,11 @@ func (st *AttributeValue) UnmarshalJSON(b []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type CollectionsContainer struct {
|
||||
Continuation string `json:"continuation"`
|
||||
Collections []Collection `json:"collections"`
|
||||
}
|
||||
|
||||
type Collection struct {
|
||||
ID string `json:"id"`
|
||||
Blockchain string `json:"blockchain"`
|
||||
@@ -247,6 +297,19 @@ func raribleToCollectiblesData(l []Collectible, isMainnet bool) []thirdparty.Ful
|
||||
return ret
|
||||
}
|
||||
|
||||
func raribleToCollectionsData(l []Collection, isMainnet bool) []thirdparty.CollectionData {
|
||||
ret := make([]thirdparty.CollectionData, 0, len(l))
|
||||
for _, c := range l {
|
||||
id, err := raribleContractIDToUniqueID(c.ID, isMainnet)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
item := c.toCommon(id)
|
||||
ret = append(ret, item)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *Collection) toCommon(id thirdparty.ContractID) thirdparty.CollectionData {
|
||||
ret := thirdparty.CollectionData{
|
||||
ID: id,
|
||||
|
||||
12
vendor/github.com/status-im/status-go/services/wallet/thirdparty/utils/symbols.go
generated
vendored
12
vendor/github.com/status-im/status-go/services/wallet/thirdparty/utils/symbols.go
generated
vendored
@@ -13,6 +13,18 @@ func RenameSymbols(symbols []string) (renames []string) {
|
||||
return
|
||||
}
|
||||
|
||||
func RemoveDuplicates(strings []string) []string {
|
||||
uniqueStrings := make(map[string]bool)
|
||||
var uniqueSlice []string
|
||||
for _, str := range strings {
|
||||
if !uniqueStrings[str] {
|
||||
uniqueStrings[str] = true
|
||||
uniqueSlice = append(uniqueSlice, str)
|
||||
}
|
||||
}
|
||||
return uniqueSlice
|
||||
}
|
||||
|
||||
func GetRealSymbol(symbol string) string {
|
||||
if val, ok := renameMapping[strings.ToUpper(symbol)]; ok {
|
||||
return val
|
||||
|
||||
51
vendor/github.com/status-im/status-go/services/wallet/token/token.go
generated
vendored
51
vendor/github.com/status-im/status-go/services/wallet/token/token.go
generated
vendored
@@ -30,6 +30,7 @@ import (
|
||||
"github.com/status-im/status-go/services/communitytokens"
|
||||
"github.com/status-im/status-go/services/utils"
|
||||
"github.com/status-im/status-go/services/wallet/async"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
"github.com/status-im/status-go/services/wallet/community"
|
||||
"github.com/status-im/status-go/services/wallet/walletevent"
|
||||
)
|
||||
@@ -62,14 +63,10 @@ type Token struct {
|
||||
}
|
||||
|
||||
type ReceivedToken struct {
|
||||
Address common.Address `json:"address"`
|
||||
Name string `json:"name"`
|
||||
Symbol string `json:"symbol"`
|
||||
Image string `json:"image,omitempty"`
|
||||
ChainID uint64 `json:"chainId"`
|
||||
CommunityData *community.Data `json:"community_data,omitempty"`
|
||||
Balance *big.Int `json:"balance"`
|
||||
TxHash common.Hash `json:"txHash"`
|
||||
Token
|
||||
Amount float64 `json:"amount"`
|
||||
TxHash common.Hash `json:"txHash"`
|
||||
IsFirst bool `json:"isFirst"`
|
||||
}
|
||||
|
||||
func (t *Token) IsNative() bool {
|
||||
@@ -316,20 +313,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 +806,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,15 +823,14 @@ 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()
|
||||
t.Image = tm.mediaServer.MakeCommunityTokenImagesURL(t.CommunityData.ID, t.ChainID, t.Symbol)
|
||||
|
||||
receivedToken := ReceivedToken{
|
||||
Address: t.Address,
|
||||
Name: t.Name,
|
||||
Symbol: t.Symbol,
|
||||
Image: t.Image,
|
||||
ChainID: t.ChainID,
|
||||
CommunityData: t.CommunityData,
|
||||
Balance: value,
|
||||
TxHash: txHash,
|
||||
Token: *t,
|
||||
Amount: floatAmount,
|
||||
TxHash: txHash,
|
||||
IsFirst: isFirst,
|
||||
}
|
||||
|
||||
encodedMessage, err := json.Marshal(receivedToken)
|
||||
@@ -869,3 +865,14 @@ func (tm *Manager) fillCommunityData(token *Token) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tm *Manager) GetTokenHistoricalBalance(account common.Address, chainID uint64, symbol string, timestamp int64) (*big.Int, error) {
|
||||
var balance big.Int
|
||||
err := tm.db.QueryRow("SELECT balance FROM balance_history WHERE currency = ? AND chain_id = ? AND address = ? AND timestamp < ? order by timestamp DESC LIMIT 1", symbol, chainID, account, timestamp).Scan((*bigint.SQLBigIntBytes)(&balance))
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &balance, nil
|
||||
}
|
||||
|
||||
29
vendor/github.com/status-im/status-go/services/wallet/token/tokenstore.go
generated
vendored
29
vendor/github.com/status-im/status-go/services/wallet/token/tokenstore.go
generated
vendored
@@ -15,6 +15,7 @@ type store interface {
|
||||
var tokenPeg = map[string]string{
|
||||
"aUSDC": "USD",
|
||||
"DAI": "USD",
|
||||
"EUROC": "EUR",
|
||||
"SAI": "USD",
|
||||
"sUSD": "USD",
|
||||
"PAXG": "XAU",
|
||||
@@ -776,6 +777,14 @@ func newDefaultStore() *DefaultStore {
|
||||
ChainID: 1,
|
||||
TokenListID: "status",
|
||||
},
|
||||
&Token{
|
||||
Address: common.HexToAddress("0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c"),
|
||||
Name: "Euro Coin",
|
||||
Symbol: "EUROC",
|
||||
Decimals: 6,
|
||||
ChainID: 1,
|
||||
TokenListID: "status",
|
||||
},
|
||||
&Token{
|
||||
Address: common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"),
|
||||
Name: "USD Coin",
|
||||
@@ -1729,7 +1738,23 @@ func newDefaultStore() *DefaultStore {
|
||||
TokenListID: "status",
|
||||
},
|
||||
&Token{
|
||||
Address: common.HexToAddress("0x44a739916D41eC0226d98F83BE5364B69078DA41"),
|
||||
Address: common.HexToAddress("0x08210F9170F89Ab7658F0B5E3fF39b0E03C594D4"),
|
||||
Name: "Euro Coin",
|
||||
Symbol: "EUROC",
|
||||
Decimals: 6,
|
||||
ChainID: 11155111,
|
||||
TokenListID: "status",
|
||||
},
|
||||
&Token{
|
||||
Address: common.HexToAddress("0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238"),
|
||||
Name: "USD Coin",
|
||||
Symbol: "USDC",
|
||||
Decimals: 6,
|
||||
ChainID: 11155111,
|
||||
TokenListID: "status",
|
||||
},
|
||||
&Token{
|
||||
Address: common.HexToAddress("0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d"),
|
||||
Name: "USD Coin",
|
||||
Symbol: "USDC",
|
||||
Decimals: 6,
|
||||
@@ -1737,7 +1762,7 @@ func newDefaultStore() *DefaultStore {
|
||||
TokenListID: "status",
|
||||
},
|
||||
&Token{
|
||||
Address: common.HexToAddress("0x44a739916D41eC0226d98F83BE5364B69078DA41"),
|
||||
Address: common.HexToAddress("0x5fd84259d66Cd46123540766Be93DFE6D43130D7"),
|
||||
Name: "USD Coin",
|
||||
Symbol: "USDC",
|
||||
Decimals: 6,
|
||||
|
||||
@@ -10,7 +10,8 @@ import (
|
||||
)
|
||||
|
||||
type BlockRangeDAOer interface {
|
||||
getBlockRange(chainID uint64, address common.Address) (blockRange *ethTokensBlockRanges, err error)
|
||||
getBlockRange(chainID uint64, address common.Address) (blockRange *ethTokensBlockRanges, exists bool, err error)
|
||||
getBlockRanges(chainID uint64, addresses []common.Address) (blockRanges map[common.Address]*ethTokensBlockRanges, err error)
|
||||
upsertRange(chainID uint64, account common.Address, newBlockRange *ethTokensBlockRanges) (err error)
|
||||
updateTokenRange(chainID uint64, account common.Address, newBlockRange *BlockRange) (err error)
|
||||
upsertEthRange(chainID uint64, account common.Address, newBlockRange *BlockRange) (err error)
|
||||
@@ -27,7 +28,7 @@ type BlockRange struct {
|
||||
}
|
||||
|
||||
func NewBlockRange() *BlockRange {
|
||||
return &BlockRange{Start: &big.Int{}, FirstKnown: &big.Int{}, LastKnown: &big.Int{}}
|
||||
return &BlockRange{Start: nil, FirstKnown: nil, LastKnown: nil}
|
||||
}
|
||||
|
||||
type ethTokensBlockRanges struct {
|
||||
@@ -40,8 +41,48 @@ func newEthTokensBlockRanges() *ethTokensBlockRanges {
|
||||
return ðTokensBlockRanges{eth: NewBlockRange(), tokens: NewBlockRange()}
|
||||
}
|
||||
|
||||
func (b *BlockRangeSequentialDAO) getBlockRange(chainID uint64, address common.Address) (blockRange *ethTokensBlockRanges, err error) {
|
||||
query := `SELECT blk_start, blk_first, blk_last, token_blk_start, token_blk_first, token_blk_last, balance_check_hash FROM blocks_ranges_sequential
|
||||
func scanRanges(rows *sql.Rows) (map[common.Address]*ethTokensBlockRanges, error) {
|
||||
blockRanges := make(map[common.Address]*ethTokensBlockRanges)
|
||||
for rows.Next() {
|
||||
efk := &bigint.NilableSQLBigInt{}
|
||||
elk := &bigint.NilableSQLBigInt{}
|
||||
es := &bigint.NilableSQLBigInt{}
|
||||
tfk := &bigint.NilableSQLBigInt{}
|
||||
tlk := &bigint.NilableSQLBigInt{}
|
||||
ts := &bigint.NilableSQLBigInt{}
|
||||
addressB := []byte{}
|
||||
blockRange := newEthTokensBlockRanges()
|
||||
err := rows.Scan(&addressB, es, efk, elk, ts, tfk, tlk, &blockRange.balanceCheckHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
address := common.BytesToAddress(addressB)
|
||||
blockRanges[address] = blockRange
|
||||
|
||||
if !es.IsNil() {
|
||||
blockRanges[address].eth.Start = big.NewInt(es.Int64())
|
||||
}
|
||||
if !efk.IsNil() {
|
||||
blockRanges[address].eth.FirstKnown = big.NewInt(efk.Int64())
|
||||
}
|
||||
if !elk.IsNil() {
|
||||
blockRanges[address].eth.LastKnown = big.NewInt(elk.Int64())
|
||||
}
|
||||
if !ts.IsNil() {
|
||||
blockRanges[address].tokens.Start = big.NewInt(ts.Int64())
|
||||
}
|
||||
if !tfk.IsNil() {
|
||||
blockRanges[address].tokens.FirstKnown = big.NewInt(tfk.Int64())
|
||||
}
|
||||
if !tlk.IsNil() {
|
||||
blockRanges[address].tokens.LastKnown = big.NewInt(tlk.Int64())
|
||||
}
|
||||
}
|
||||
return blockRanges, nil
|
||||
}
|
||||
|
||||
func (b *BlockRangeSequentialDAO) getBlockRange(chainID uint64, address common.Address) (blockRange *ethTokensBlockRanges, exists bool, err error) {
|
||||
query := `SELECT address, blk_start, blk_first, blk_last, token_blk_start, token_blk_first, token_blk_last, balance_check_hash FROM blocks_ranges_sequential
|
||||
WHERE address = ?
|
||||
AND network_id = ?`
|
||||
|
||||
@@ -51,25 +92,45 @@ func (b *BlockRangeSequentialDAO) getBlockRange(chainID uint64, address common.A
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
blockRange = ðTokensBlockRanges{}
|
||||
if rows.Next() {
|
||||
blockRange = newEthTokensBlockRanges()
|
||||
err = rows.Scan((*bigint.SQLBigInt)(blockRange.eth.Start),
|
||||
(*bigint.SQLBigInt)(blockRange.eth.FirstKnown),
|
||||
(*bigint.SQLBigInt)(blockRange.eth.LastKnown),
|
||||
(*bigint.SQLBigInt)(blockRange.tokens.Start),
|
||||
(*bigint.SQLBigInt)(blockRange.tokens.FirstKnown),
|
||||
(*bigint.SQLBigInt)(blockRange.tokens.LastKnown),
|
||||
&blockRange.balanceCheckHash,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return blockRange, nil
|
||||
ranges, err := scanRanges(rows)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
return blockRange, nil
|
||||
blockRange, exists = ranges[address]
|
||||
if !exists {
|
||||
blockRange = newEthTokensBlockRanges()
|
||||
}
|
||||
|
||||
return blockRange, exists, nil
|
||||
}
|
||||
|
||||
func (b *BlockRangeSequentialDAO) getBlockRanges(chainID uint64, addresses []common.Address) (blockRanges map[common.Address]*ethTokensBlockRanges, err error) {
|
||||
blockRanges = make(map[common.Address]*ethTokensBlockRanges)
|
||||
addressesPlaceholder := ""
|
||||
for i := 0; i < len(addresses); i++ {
|
||||
addressesPlaceholder += "?"
|
||||
if i < len(addresses)-1 {
|
||||
addressesPlaceholder += ","
|
||||
}
|
||||
}
|
||||
|
||||
query := "SELECT address, blk_start, blk_first, blk_last, token_blk_start, token_blk_first, token_blk_last, balance_check_hash FROM blocks_ranges_sequential WHERE address IN (" +
|
||||
addressesPlaceholder + ") AND network_id = ?"
|
||||
|
||||
params := []interface{}{}
|
||||
for _, address := range addresses {
|
||||
params = append(params, address)
|
||||
}
|
||||
params = append(params, chainID)
|
||||
|
||||
rows, err := b.db.Query(query, params...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
return scanRanges(rows)
|
||||
}
|
||||
|
||||
func (b *BlockRangeSequentialDAO) deleteRange(account common.Address) error {
|
||||
@@ -85,7 +146,7 @@ func (b *BlockRangeSequentialDAO) deleteRange(account common.Address) error {
|
||||
}
|
||||
|
||||
func (b *BlockRangeSequentialDAO) upsertRange(chainID uint64, account common.Address, newBlockRange *ethTokensBlockRanges) (err error) {
|
||||
ethTokensBlockRange, err := b.getBlockRange(chainID, account)
|
||||
ethTokensBlockRange, exists, err := b.getBlockRange(chainID, account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -93,18 +154,38 @@ func (b *BlockRangeSequentialDAO) upsertRange(chainID uint64, account common.Add
|
||||
ethBlockRange := prepareUpdatedBlockRange(ethTokensBlockRange.eth, newBlockRange.eth)
|
||||
tokensBlockRange := prepareUpdatedBlockRange(ethTokensBlockRange.tokens, newBlockRange.tokens)
|
||||
|
||||
log.Debug("update eth and tokens blocks range", "account", account, "chainID", chainID,
|
||||
"eth.start", ethBlockRange.Start, "eth.first", ethBlockRange.FirstKnown, "eth.last", ethBlockRange.LastKnown,
|
||||
"tokens.start", tokensBlockRange.Start, "tokens.first", ethBlockRange.FirstKnown, "eth.last", ethBlockRange.LastKnown, "hash", newBlockRange.balanceCheckHash)
|
||||
log.Debug("upsert eth and tokens blocks range",
|
||||
"account", account, "chainID", chainID,
|
||||
"eth.start", ethBlockRange.Start,
|
||||
"eth.first", ethBlockRange.FirstKnown,
|
||||
"eth.last", ethBlockRange.LastKnown,
|
||||
"tokens.first", tokensBlockRange.FirstKnown,
|
||||
"tokens.last", tokensBlockRange.LastKnown,
|
||||
"hash", newBlockRange.balanceCheckHash)
|
||||
|
||||
var query *sql.Stmt
|
||||
|
||||
if exists {
|
||||
query, err = b.db.Prepare(`UPDATE blocks_ranges_sequential SET
|
||||
blk_start = ?,
|
||||
blk_first = ?,
|
||||
blk_last = ?,
|
||||
token_blk_start = ?,
|
||||
token_blk_first = ?,
|
||||
token_blk_last = ?,
|
||||
balance_check_hash = ?
|
||||
WHERE network_id = ? AND address = ?`)
|
||||
|
||||
} else {
|
||||
query, err = b.db.Prepare(`INSERT INTO blocks_ranges_sequential
|
||||
(blk_start, blk_first, blk_last, token_blk_start, token_blk_first, token_blk_last, balance_check_hash, network_id, address) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
||||
}
|
||||
|
||||
upsert, err := b.db.Prepare(`REPLACE INTO blocks_ranges_sequential
|
||||
(network_id, address, blk_start, blk_first, blk_last, token_blk_start, token_blk_first, token_blk_last, balance_check_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = upsert.Exec(chainID, account, (*bigint.SQLBigInt)(ethBlockRange.Start), (*bigint.SQLBigInt)(ethBlockRange.FirstKnown), (*bigint.SQLBigInt)(ethBlockRange.LastKnown),
|
||||
(*bigint.SQLBigInt)(tokensBlockRange.Start), (*bigint.SQLBigInt)(tokensBlockRange.FirstKnown), (*bigint.SQLBigInt)(tokensBlockRange.LastKnown), newBlockRange.balanceCheckHash)
|
||||
_, err = query.Exec((*bigint.SQLBigInt)(ethBlockRange.Start), (*bigint.SQLBigInt)(ethBlockRange.FirstKnown), (*bigint.SQLBigInt)(ethBlockRange.LastKnown),
|
||||
(*bigint.SQLBigInt)(tokensBlockRange.Start), (*bigint.SQLBigInt)(tokensBlockRange.FirstKnown), (*bigint.SQLBigInt)(tokensBlockRange.LastKnown), newBlockRange.balanceCheckHash, chainID, account)
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -112,28 +193,36 @@ func (b *BlockRangeSequentialDAO) upsertRange(chainID uint64, account common.Add
|
||||
func (b *BlockRangeSequentialDAO) upsertEthRange(chainID uint64, account common.Address,
|
||||
newBlockRange *BlockRange) (err error) {
|
||||
|
||||
ethTokensBlockRange, err := b.getBlockRange(chainID, account)
|
||||
ethTokensBlockRange, exists, err := b.getBlockRange(chainID, account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blockRange := prepareUpdatedBlockRange(ethTokensBlockRange.eth, newBlockRange)
|
||||
|
||||
log.Debug("update eth blocks range", "account", account, "chainID", chainID,
|
||||
"start", blockRange.Start, "first", blockRange.FirstKnown, "last", blockRange.LastKnown, "old hash", ethTokensBlockRange.balanceCheckHash)
|
||||
log.Debug("upsert eth blocks range", "account", account, "chainID", chainID,
|
||||
"start", blockRange.Start,
|
||||
"first", blockRange.FirstKnown,
|
||||
"last", blockRange.LastKnown,
|
||||
"old hash", ethTokensBlockRange.balanceCheckHash)
|
||||
|
||||
var query *sql.Stmt
|
||||
|
||||
if exists {
|
||||
query, err = b.db.Prepare(`UPDATE blocks_ranges_sequential SET
|
||||
blk_start = ?,
|
||||
blk_first = ?,
|
||||
blk_last = ?
|
||||
WHERE network_id = ? AND address = ?`)
|
||||
} else {
|
||||
query, err = b.db.Prepare(`INSERT INTO blocks_ranges_sequential
|
||||
(blk_start, blk_first, blk_last, network_id, address) VALUES (?, ?, ?, ?, ?)`)
|
||||
}
|
||||
|
||||
upsert, err := b.db.Prepare(`REPLACE INTO blocks_ranges_sequential
|
||||
(network_id, address, blk_start, blk_first, blk_last, token_blk_start, token_blk_first, token_blk_last, balance_check_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ethTokensBlockRange.tokens == nil {
|
||||
ethTokensBlockRange.tokens = NewBlockRange()
|
||||
}
|
||||
|
||||
_, err = upsert.Exec(chainID, account, (*bigint.SQLBigInt)(blockRange.Start), (*bigint.SQLBigInt)(blockRange.FirstKnown), (*bigint.SQLBigInt)(blockRange.LastKnown),
|
||||
(*bigint.SQLBigInt)(ethTokensBlockRange.tokens.Start), (*bigint.SQLBigInt)(ethTokensBlockRange.tokens.FirstKnown), (*bigint.SQLBigInt)(ethTokensBlockRange.tokens.LastKnown), ethTokensBlockRange.balanceCheckHash)
|
||||
_, err = query.Exec((*bigint.SQLBigInt)(blockRange.Start), (*bigint.SQLBigInt)(blockRange.FirstKnown), (*bigint.SQLBigInt)(blockRange.LastKnown), chainID, account)
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -141,15 +230,16 @@ func (b *BlockRangeSequentialDAO) upsertEthRange(chainID uint64, account common.
|
||||
func (b *BlockRangeSequentialDAO) updateTokenRange(chainID uint64, account common.Address,
|
||||
newBlockRange *BlockRange) (err error) {
|
||||
|
||||
ethTokensBlockRange, err := b.getBlockRange(chainID, account)
|
||||
ethTokensBlockRange, _, err := b.getBlockRange(chainID, account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blockRange := prepareUpdatedBlockRange(ethTokensBlockRange.tokens, newBlockRange)
|
||||
|
||||
log.Debug("update tokens blocks range", "account", account, "chainID", chainID,
|
||||
"start", blockRange.Start, "first", blockRange.FirstKnown, "last", blockRange.LastKnown, "old hash", ethTokensBlockRange.balanceCheckHash)
|
||||
log.Debug("update tokens blocks range",
|
||||
"first", blockRange.FirstKnown,
|
||||
"last", blockRange.LastKnown)
|
||||
|
||||
update, err := b.db.Prepare(`UPDATE blocks_ranges_sequential SET token_blk_start = ?, token_blk_first = ?, token_blk_last = ? WHERE network_id = ? AND address = ?`)
|
||||
if err != nil {
|
||||
@@ -163,31 +253,26 @@ func (b *BlockRangeSequentialDAO) updateTokenRange(chainID uint64, account commo
|
||||
}
|
||||
|
||||
func prepareUpdatedBlockRange(blockRange, newBlockRange *BlockRange) *BlockRange {
|
||||
// Update existing range
|
||||
if blockRange != nil {
|
||||
if newBlockRange != nil {
|
||||
// Ovewrite start block if there was not any or if new one is older, because it can be precised only
|
||||
// to a greater value, because no history can be before some block that is considered
|
||||
// as a start of history, but due to concurrent block range checks, a newer greater block
|
||||
// can be found that matches criteria of a start block (nonce is zero, balances are equal)
|
||||
if newBlockRange.Start != nil && (blockRange.Start == nil || blockRange.Start.Cmp(newBlockRange.Start) < 0) {
|
||||
blockRange.Start = newBlockRange.Start
|
||||
}
|
||||
|
||||
// Overwrite first known block if there was not any or if new one is older
|
||||
if (blockRange.FirstKnown == nil && newBlockRange.FirstKnown != nil) ||
|
||||
(blockRange.FirstKnown != nil && newBlockRange.FirstKnown != nil && blockRange.FirstKnown.Cmp(newBlockRange.FirstKnown) > 0) {
|
||||
blockRange.FirstKnown = newBlockRange.FirstKnown
|
||||
}
|
||||
|
||||
// Overwrite last known block if there was not any or if new one is newer
|
||||
if (blockRange.LastKnown == nil && newBlockRange.LastKnown != nil) ||
|
||||
(blockRange.LastKnown != nil && newBlockRange.LastKnown != nil && blockRange.LastKnown.Cmp(newBlockRange.LastKnown) < 0) {
|
||||
blockRange.LastKnown = newBlockRange.LastKnown
|
||||
}
|
||||
if newBlockRange != nil {
|
||||
// Ovewrite start block if there was not any or if new one is older, because it can be precised only
|
||||
// to a greater value, because no history can be before some block that is considered
|
||||
// as a start of history, but due to concurrent block range checks, a newer greater block
|
||||
// can be found that matches criteria of a start block (nonce is zero, balances are equal)
|
||||
if newBlockRange.Start != nil && (blockRange.Start == nil || blockRange.Start.Cmp(newBlockRange.Start) < 0) {
|
||||
blockRange.Start = newBlockRange.Start
|
||||
}
|
||||
|
||||
// Overwrite first known block if there was not any or if new one is older
|
||||
if (blockRange.FirstKnown == nil && newBlockRange.FirstKnown != nil) ||
|
||||
(blockRange.FirstKnown != nil && newBlockRange.FirstKnown != nil && blockRange.FirstKnown.Cmp(newBlockRange.FirstKnown) > 0) {
|
||||
blockRange.FirstKnown = newBlockRange.FirstKnown
|
||||
}
|
||||
|
||||
// Overwrite last known block if there was not any or if new one is newer
|
||||
if (blockRange.LastKnown == nil && newBlockRange.LastKnown != nil) ||
|
||||
(blockRange.LastKnown != nil && newBlockRange.LastKnown != nil && blockRange.LastKnown.Cmp(newBlockRange.LastKnown) < 0) {
|
||||
blockRange.LastKnown = newBlockRange.LastKnown
|
||||
}
|
||||
} else {
|
||||
blockRange = newBlockRange
|
||||
}
|
||||
|
||||
return blockRange
|
||||
|
||||
38
vendor/github.com/status-im/status-go/services/wallet/transfer/commands.go
generated
vendored
38
vendor/github.com/status-im/status-go/services/wallet/transfer/commands.go
generated
vendored
@@ -322,6 +322,20 @@ func (c *transfersCommand) saveAndConfirmPending(allTransfers []Transfer, blockN
|
||||
return resErr
|
||||
}
|
||||
|
||||
func externalTransactionOrError(err error, mTID int64) bool {
|
||||
if err == sql.ErrNoRows {
|
||||
// External transaction downloaded, ignore it
|
||||
return true
|
||||
} else if err != nil {
|
||||
log.Warn("GetOwnedMultiTransactionID", "error", err)
|
||||
return true
|
||||
} else if mTID <= 0 {
|
||||
// Existing external transaction, ignore it
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *transfersCommand) confirmPendingTransactions(tx *sql.Tx, allTransfers []Transfer) (notifyFunctions []func()) {
|
||||
notifyFunctions = make([]func(), 0)
|
||||
|
||||
@@ -335,16 +349,11 @@ func (c *transfersCommand) confirmPendingTransactions(tx *sql.Tx, allTransfers [
|
||||
continue
|
||||
} else {
|
||||
// Outside transaction, already confirmed by another duplicate or not yet downloaded
|
||||
existingMTID, err := GetOwnedMultiTransactionID(tx, chainID, tr.ID, tr.Address)
|
||||
if err == sql.ErrNoRows || existingMTID == 0 {
|
||||
// Outside transaction, ignore it
|
||||
continue
|
||||
} else if err != nil {
|
||||
log.Warn("GetOwnedMultiTransactionID", "error", err)
|
||||
existingMTID, err := GetOwnedMultiTransactionID(tx, chainID, txHash, tr.Address)
|
||||
if externalTransactionOrError(err, existingMTID) {
|
||||
continue
|
||||
}
|
||||
mTID = w_common.NewAndSet(existingMTID)
|
||||
|
||||
}
|
||||
} else if err != nil {
|
||||
log.Warn("GetOwnedPendingStatus", "error", err)
|
||||
@@ -352,7 +361,7 @@ func (c *transfersCommand) confirmPendingTransactions(tx *sql.Tx, allTransfers [
|
||||
}
|
||||
|
||||
if mTID != nil {
|
||||
allTransfers[i].MultiTransactionID = MultiTransactionIDType(*mTID)
|
||||
allTransfers[i].MultiTransactionID = w_common.MultiTransactionIDType(*mTID)
|
||||
}
|
||||
if txType != nil && *txType == transactions.WalletTransfer {
|
||||
notify, err := c.pendingTxManager.DeleteBySQLTx(tx, chainID, txHash)
|
||||
@@ -366,7 +375,7 @@ func (c *transfersCommand) confirmPendingTransactions(tx *sql.Tx, allTransfers [
|
||||
}
|
||||
|
||||
// Mark all subTxs of a given Tx with the same multiTxID
|
||||
func setMultiTxID(tx Transaction, multiTxID MultiTransactionIDType) {
|
||||
func setMultiTxID(tx Transaction, multiTxID w_common.MultiTransactionIDType) {
|
||||
for _, subTx := range tx {
|
||||
subTx.MultiTransactionID = multiTxID
|
||||
}
|
||||
@@ -378,11 +387,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,7 +431,7 @@ func (c *transfersCommand) checkAndProcessBridgeMultiTx(ctx context.Context, tx
|
||||
}
|
||||
|
||||
if multiTransaction != nil {
|
||||
setMultiTxID(tx, MultiTransactionIDType(multiTransaction.ID))
|
||||
setMultiTxID(tx, multiTransaction.ID)
|
||||
c.markMultiTxTokensAsPreviouslyOwned(ctx, multiTransaction, subTx.Address)
|
||||
return true, nil
|
||||
}
|
||||
@@ -439,11 +448,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
99
vendor/github.com/status-im/status-go/services/wallet/transfer/commands_sequential.go
generated
vendored
99
vendor/github.com/status-im/status-go/services/wallet/transfer/commands_sequential.go
generated
vendored
@@ -67,7 +67,7 @@ func (c *findNewBlocksCommand) detectTransfers(parent context.Context, accounts
|
||||
tokenAddresses = append(tokenAddresses, token.Address)
|
||||
}
|
||||
}
|
||||
log.Info("findNewBlocksCommand detectTransfers", "cnt", len(tokenAddresses), "addresses", tokenAddresses)
|
||||
log.Debug("findNewBlocksCommand detectTransfers", "cnt", len(tokenAddresses))
|
||||
|
||||
ctx, cancel := context.WithTimeout(parent, requestTimeout)
|
||||
defer cancel()
|
||||
@@ -79,22 +79,12 @@ func (c *findNewBlocksCommand) detectTransfers(parent context.Context, accounts
|
||||
|
||||
addressesToCheck := []common.Address{}
|
||||
for idx, account := range accounts {
|
||||
blockRange, err := c.blockRangeDAO.getBlockRange(c.chainClient.NetworkID(), account)
|
||||
blockRange, _, err := c.blockRangeDAO.getBlockRange(c.chainClient.NetworkID(), account)
|
||||
if err != nil {
|
||||
log.Error("findNewBlocksCommand can't block range", "error", err, "account", account, "chain", c.chainClient.NetworkID())
|
||||
log.Error("findNewBlocksCommand can't get block range", "error", err, "account", account, "chain", c.chainClient.NetworkID())
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if blockRange.eth == nil {
|
||||
blockRange.eth = NewBlockRange()
|
||||
blockRange.tokens = NewBlockRange()
|
||||
}
|
||||
if blockRange.eth.FirstKnown == nil {
|
||||
blockRange.eth.FirstKnown = blockNum
|
||||
}
|
||||
if blockRange.eth.LastKnown == nil {
|
||||
blockRange.eth.LastKnown = blockNum
|
||||
}
|
||||
checkHash := common.BytesToHash(hashes[idx][:])
|
||||
log.Debug("findNewBlocksCommand comparing hashes", "account", account, "network", c.chainClient.NetworkID(), "old hash", blockRange.balanceCheckHash, "new hash", checkHash.String())
|
||||
if checkHash.String() != blockRange.balanceCheckHash {
|
||||
@@ -118,7 +108,7 @@ func (c *findNewBlocksCommand) detectNonceChange(parent context.Context, to *big
|
||||
for _, account := range accounts {
|
||||
var oldNonce *int64
|
||||
|
||||
blockRange, err := c.blockRangeDAO.getBlockRange(c.chainClient.NetworkID(), account)
|
||||
blockRange, _, err := c.blockRangeDAO.getBlockRange(c.chainClient.NetworkID(), account)
|
||||
if err != nil {
|
||||
log.Error("findNewBlocksCommand can't get block range", "error", err, "account", account, "chain", c.chainClient.NetworkID())
|
||||
return nil, err
|
||||
@@ -126,12 +116,16 @@ func (c *findNewBlocksCommand) detectNonceChange(parent context.Context, to *big
|
||||
|
||||
lastNonceInfo, ok := c.lastNonces[account]
|
||||
if !ok || lastNonceInfo.blockNumber.Cmp(blockRange.eth.LastKnown) != 0 {
|
||||
log.Info("Fetching old nonce", "at", blockRange.eth.LastKnown, "acc", account)
|
||||
|
||||
oldNonce, err = c.balanceCacher.NonceAt(parent, c.chainClient, account, blockRange.eth.LastKnown)
|
||||
if err != nil {
|
||||
log.Error("findNewBlocksCommand can't get nonce", "error", err, "account", account, "chain", c.chainClient.NetworkID())
|
||||
return nil, err
|
||||
log.Debug("Fetching old nonce", "at", blockRange.eth.LastKnown, "acc", account)
|
||||
if blockRange.eth.LastKnown == nil {
|
||||
blockRange.eth.LastKnown = big.NewInt(0)
|
||||
oldNonce = new(int64) // At 0 block nonce is 0
|
||||
} else {
|
||||
oldNonce, err = c.balanceCacher.NonceAt(parent, c.chainClient, account, blockRange.eth.LastKnown)
|
||||
if err != nil {
|
||||
log.Error("findNewBlocksCommand can't get nonce", "error", err, "account", account, "chain", c.chainClient.NetworkID())
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
oldNonce = lastNonceInfo.nonce
|
||||
@@ -143,7 +137,7 @@ func (c *findNewBlocksCommand) detectNonceChange(parent context.Context, to *big
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Info("Comparing nonces", "oldNonce", *oldNonce, "newNonce", *newNonce, "to", to, "acc", account)
|
||||
log.Debug("Comparing nonces", "oldNonce", *oldNonce, "newNonce", *newNonce, "to", to, "acc", account)
|
||||
|
||||
if *newNonce != *oldNonce {
|
||||
addressesWithChange[account] = blockRange.eth.LastKnown
|
||||
@@ -207,7 +201,7 @@ func (c *findNewBlocksCommand) Run(parent context.Context) error {
|
||||
c.blockChainState.SetLastBlockNumber(c.chainClient.NetworkID(), headNum.Uint64())
|
||||
|
||||
if len(accountsWithDetectedChanges) != 0 {
|
||||
log.Debug("findNewBlocksCommand detected accounts with changes, proceeding", "accounts", accountsWithDetectedChanges)
|
||||
log.Debug("findNewBlocksCommand detected accounts with changes, proceeding", "accounts", accountsWithDetectedChanges, "from", c.fromBlockNumber)
|
||||
err = c.findAndSaveEthBlocks(parent, c.fromBlockNumber, headNum, accountsToCheck)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -241,10 +235,15 @@ func (c *findNewBlocksCommand) Run(parent context.Context) error {
|
||||
}
|
||||
|
||||
if len(accountsWithDetectedChanges) != 0 || c.iteration%c.logsCheckIntervalIterations == 0 {
|
||||
err = c.findAndSaveTokenBlocks(parent, c.fromBlockNumber, headNum)
|
||||
from := c.fromBlockNumber
|
||||
if c.logsCheckLastKnownBlock != nil {
|
||||
from = c.logsCheckLastKnownBlock
|
||||
}
|
||||
err = c.findAndSaveTokenBlocks(parent, from, headNum)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.logsCheckLastKnownBlock = headNum
|
||||
}
|
||||
c.fromBlockNumber = headNum
|
||||
c.iteration++
|
||||
@@ -335,11 +334,11 @@ func (c *findNewBlocksCommand) findAndSaveTokenBlocks(parent context.Context, fr
|
||||
return c.markTokenBlockRangeChecked(c.accounts, fromNum, headNum)
|
||||
}
|
||||
|
||||
func (c *findNewBlocksCommand) markTokenBlockRangeChecked(accounts []common.Address, from, to *big.Int) error {
|
||||
func (c *findBlocksCommand) markTokenBlockRangeChecked(accounts []common.Address, from, to *big.Int) error {
|
||||
log.Debug("markTokenBlockRangeChecked", "chain", c.chainClient.NetworkID(), "from", from.Uint64(), "to", to.Uint64())
|
||||
|
||||
for _, account := range accounts {
|
||||
err := c.blockRangeDAO.updateTokenRange(c.chainClient.NetworkID(), account, &BlockRange{LastKnown: to})
|
||||
err := c.blockRangeDAO.updateTokenRange(c.chainClient.NetworkID(), account, &BlockRange{FirstKnown: from, LastKnown: to})
|
||||
if err != nil {
|
||||
log.Error("findNewBlocksCommand upsertTokenRange", "error", err)
|
||||
return err
|
||||
@@ -435,6 +434,7 @@ type findBlocksCommand struct {
|
||||
transactionManager *TransactionManager
|
||||
tokenManager *token.Manager
|
||||
fromBlockNumber *big.Int
|
||||
logsCheckLastKnownBlock *big.Int
|
||||
toBlockNumber *big.Int
|
||||
blocksLoadedCh chan<- []*DBHeader
|
||||
defaultNodeBlockChunkSize int
|
||||
@@ -529,7 +529,7 @@ func (c *findBlocksCommand) ERC20ScanByBalance(parent context.Context, account c
|
||||
}
|
||||
|
||||
func (c *findBlocksCommand) checkERC20Tail(parent context.Context, account common.Address) ([]*DBHeader, error) {
|
||||
log.Info("checkERC20Tail", "account", account, "to block", c.startBlockNumber, "from", c.resFromBlock.Number)
|
||||
log.Debug("checkERC20Tail", "account", account, "to block", c.startBlockNumber, "from", c.resFromBlock.Number)
|
||||
tokens, err := c.tokenManager.GetTokens(c.chainClient.NetworkID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -650,6 +650,10 @@ func (c *findBlocksCommand) Run(parent context.Context) (err error) {
|
||||
}
|
||||
|
||||
if c.reachedETHHistoryStart {
|
||||
err = c.markTokenBlockRangeChecked([]common.Address{account}, big.NewInt(0), to)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
log.Debug("findBlocksCommand reached first ETH transfer and checked erc20 tail", "chain", c.chainClient.NetworkID(), "account", account)
|
||||
break
|
||||
}
|
||||
@@ -659,6 +663,11 @@ func (c *findBlocksCommand) Run(parent context.Context) (err error) {
|
||||
break
|
||||
}
|
||||
|
||||
err = c.markTokenBlockRangeChecked([]common.Address{account}, c.resFromBlock.Number, to)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
// if we have found first ETH block and we have not reached the start of ETH history yet
|
||||
if c.startBlockNumber != nil && c.fromBlockNumber.Cmp(from) == -1 {
|
||||
log.Debug("ERC20 tail should be checked", "initial from", c.fromBlockNumber, "actual from", from, "first ETH block", c.startBlockNumber)
|
||||
@@ -746,7 +755,7 @@ func (c *findBlocksCommand) checkRange(parent context.Context, from *big.Int, to
|
||||
func loadBlockRangeInfo(chainID uint64, account common.Address, blockDAO BlockRangeDAOer) (
|
||||
*ethTokensBlockRanges, error) {
|
||||
|
||||
blockRange, err := blockDAO.getBlockRange(chainID, account)
|
||||
blockRange, _, err := blockDAO.getBlockRange(chainID, account)
|
||||
if err != nil {
|
||||
log.Error("failed to load block ranges from database", "chain", chainID, "account", account,
|
||||
"error", err)
|
||||
@@ -759,8 +768,9 @@ func loadBlockRangeInfo(chainID uint64, account common.Address, blockDAO BlockRa
|
||||
// Returns if all blocks are loaded, which means that start block (beginning of account history)
|
||||
// has been found and all block headers saved to the DB
|
||||
func areAllHistoryBlocksLoaded(blockInfo *BlockRange) bool {
|
||||
if blockInfo != nil && blockInfo.FirstKnown != nil && blockInfo.Start != nil &&
|
||||
blockInfo.Start.Cmp(blockInfo.FirstKnown) >= 0 {
|
||||
if blockInfo != nil && blockInfo.FirstKnown != nil &&
|
||||
((blockInfo.Start != nil && blockInfo.Start.Cmp(blockInfo.FirstKnown) >= 0) ||
|
||||
blockInfo.FirstKnown.Cmp(zero) == 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -770,7 +780,7 @@ func areAllHistoryBlocksLoaded(blockInfo *BlockRange) bool {
|
||||
func areAllHistoryBlocksLoadedForAddress(blockRangeDAO BlockRangeDAOer, chainID uint64,
|
||||
address common.Address) (bool, error) {
|
||||
|
||||
blockRange, err := blockRangeDAO.getBlockRange(chainID, address)
|
||||
blockRange, _, err := blockRangeDAO.getBlockRange(chainID, address)
|
||||
if err != nil {
|
||||
log.Error("findBlocksCommand getBlockRange", "error", err)
|
||||
return false, err
|
||||
@@ -964,12 +974,33 @@ func (c *loadBlocksAndTransfersCommand) Run(parent context.Context) (err error)
|
||||
finiteGroup.Wait()
|
||||
}()
|
||||
|
||||
fromNum := big.NewInt(0)
|
||||
headNum, err := getHeadBlockNumber(ctx, c.chainClient)
|
||||
blockRanges, err := c.blockRangeDAO.getBlockRanges(c.chainClient.NetworkID(), c.accounts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
firstScan := false
|
||||
var headNum *big.Int
|
||||
for _, address := range c.accounts {
|
||||
blockRange, ok := blockRanges[address]
|
||||
if !ok || blockRange.tokens.LastKnown == nil {
|
||||
firstScan = true
|
||||
break
|
||||
}
|
||||
|
||||
if headNum == nil || blockRange.tokens.LastKnown.Cmp(headNum) < 0 {
|
||||
headNum = blockRange.tokens.LastKnown
|
||||
}
|
||||
}
|
||||
|
||||
fromNum := big.NewInt(0)
|
||||
if firstScan {
|
||||
headNum, err = getHeadBlockNumber(ctx, c.chainClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// It will start loadTransfersCommand which will run until all transfers from DB are loaded or any one failed to load
|
||||
err = c.startFetchingTransfersForLoadedBlocks(finiteGroup)
|
||||
if err != nil {
|
||||
@@ -1046,14 +1077,13 @@ func (c *loadBlocksAndTransfersCommand) fetchHistoryBlocksForAccount(group *asyn
|
||||
}
|
||||
|
||||
ranges := [][]*big.Int{}
|
||||
|
||||
// There are 2 history intervals:
|
||||
// 1) from 0 to FirstKnown
|
||||
// 2) from LastKnown to `toNum`` (head)
|
||||
// If we blockRange is nil, we need to load all blocks from `fromNum` to `toNum`
|
||||
// As current implementation checks ETH first then tokens, tokens ranges maybe behind ETH ranges in
|
||||
// cases when block searching was interrupted, so we use tokens ranges
|
||||
if blockRange != nil && blockRange.tokens != nil {
|
||||
if blockRange.tokens.LastKnown != nil || blockRange.tokens.FirstKnown != nil {
|
||||
if blockRange.tokens.LastKnown != nil && toNum.Cmp(blockRange.tokens.LastKnown) > 0 {
|
||||
ranges = append(ranges, []*big.Int{blockRange.tokens.LastKnown, toNum})
|
||||
}
|
||||
@@ -1083,6 +1113,7 @@ func (c *loadBlocksAndTransfersCommand) fetchHistoryBlocksForAccount(group *asyn
|
||||
}
|
||||
|
||||
for _, rangeItem := range ranges {
|
||||
log.Debug("range item", "r", rangeItem, "n", c.chainClient.NetworkID(), "a", account)
|
||||
fbc := &findBlocksCommand{
|
||||
accounts: []common.Address{account},
|
||||
db: c.db,
|
||||
|
||||
4
vendor/github.com/status-im/status-go/services/wallet/transfer/concurrent.go
generated
vendored
4
vendor/github.com/status-im/status-go/services/wallet/transfer/concurrent.go
generated
vendored
@@ -183,11 +183,11 @@ func checkRangesWithStartBlock(parent context.Context, client balance.Reader, ca
|
||||
return err
|
||||
}
|
||||
// Obtain block hash from first transaction
|
||||
firstTransaction, err := client.FullTransactionByBlockNumberAndIndex(ctx, to, 0)
|
||||
blockHash, err := client.CallBlockHashByTransaction(ctx, to, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.PushHeader(toDBHeader(header, *firstTransaction.BlockHash, account))
|
||||
c.PushHeader(toDBHeader(header, blockHash, account))
|
||||
return nil
|
||||
}
|
||||
mid := new(big.Int).Add(from, to)
|
||||
|
||||
2
vendor/github.com/status-im/status-go/services/wallet/transfer/controller.go
generated
vendored
2
vendor/github.com/status-im/status-go/services/wallet/transfer/controller.go
generated
vendored
@@ -150,8 +150,8 @@ func (c *Controller) startAccountWatcher(chainIDs []uint64) {
|
||||
c.accWatcher = accountsevent.NewWatcher(c.accountsDB, c.accountFeed, func(changedAddresses []common.Address, eventType accountsevent.EventType, currentAddresses []common.Address) {
|
||||
c.onAccountsChanged(changedAddresses, eventType, currentAddresses, chainIDs)
|
||||
})
|
||||
c.accWatcher.Start()
|
||||
}
|
||||
c.accWatcher.Start()
|
||||
}
|
||||
|
||||
func (c *Controller) onAccountsChanged(changedAddresses []common.Address, eventType accountsevent.EventType, currentAddresses []common.Address, chainIDs []uint64) {
|
||||
|
||||
42
vendor/github.com/status-im/status-go/services/wallet/transfer/database.go
generated
vendored
42
vendor/github.com/status-im/status-go/services/wallet/transfer/database.go
generated
vendored
@@ -444,7 +444,7 @@ type transferDBFields struct {
|
||||
log *types.Log
|
||||
transferType w_common.Type
|
||||
baseGasFees string
|
||||
multiTransactionID MultiTransactionIDType
|
||||
multiTransactionID w_common.MultiTransactionIDType
|
||||
receiptStatus *uint64
|
||||
receiptType *uint8
|
||||
txHash *common.Hash
|
||||
@@ -492,7 +492,9 @@ func updateOrInsertTransfersDBFields(creator statementCreator, transfers []trans
|
||||
log.Error("can't save transfer", "b-hash", t.blockHash, "b-n", t.blockNumber, "a", t.address, "h", t.id)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, t := range transfers {
|
||||
err = removeGasOnlyEthTransfer(creator, t)
|
||||
if err != nil {
|
||||
log.Error("can't remove gas only eth transfer", "b-hash", t.blockHash, "b-n", t.blockNumber, "a", t.address, "h", t.id, "err", err)
|
||||
@@ -503,18 +505,40 @@ func updateOrInsertTransfersDBFields(creator statementCreator, transfers []trans
|
||||
}
|
||||
|
||||
func removeGasOnlyEthTransfer(creator statementCreator, t transferDBFields) error {
|
||||
if t.transferType != w_common.EthTransfer {
|
||||
query, err := creator.Prepare(`DELETE FROM transfers WHERE tx_hash = ? AND address = ? AND network_id = ?
|
||||
AND account_nonce = ? AND type = 'eth' AND amount_padded128hex = '00000000000000000000000000000000'`)
|
||||
if t.transferType == w_common.EthTransfer {
|
||||
countQuery, err := creator.Prepare(`SELECT COUNT(*) FROM transfers WHERE tx_hash = ?`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer countQuery.Close()
|
||||
|
||||
var count int
|
||||
err = countQuery.QueryRow(t.txHash).Scan(&count)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = query.Exec(t.txHash, t.address, t.chainID, t.txNonce)
|
||||
if err != nil {
|
||||
return err
|
||||
// If there's only one (or none), return without deleting
|
||||
if count <= 1 {
|
||||
log.Debug("Only one or no transfer found with the same tx_hash, skipping deletion.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
query, err := creator.Prepare(`DELETE FROM transfers WHERE tx_hash = ? AND address = ? AND network_id = ? AND account_nonce = ? AND type = 'eth' AND amount_padded128hex = '00000000000000000000000000000000'`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer query.Close()
|
||||
|
||||
res, err := query.Exec(t.txHash, t.address, t.chainID, t.txNonce)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("removeGasOnlyEthTransfer row deleted ", count)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -538,8 +562,8 @@ func markBlocksAsLoaded(chainID uint64, creator statementCreator, address common
|
||||
}
|
||||
|
||||
// GetOwnedMultiTransactionID returns sql.ErrNoRows if no transaction is found for the given identity
|
||||
func GetOwnedMultiTransactionID(tx *sql.Tx, chainID w_common.ChainID, id common.Hash, address common.Address) (mTID int64, err error) {
|
||||
row := tx.QueryRow(`SELECT COALESCE(multi_transaction_id, 0) FROM transfers WHERE network_id = ? AND hash = ? AND address = ?`, chainID, id, address)
|
||||
func GetOwnedMultiTransactionID(tx *sql.Tx, chainID w_common.ChainID, hash common.Hash, address common.Address) (mTID int64, err error) {
|
||||
row := tx.QueryRow(`SELECT COALESCE(multi_transaction_id, 0) FROM transfers WHERE network_id = ? AND tx_hash = ? AND address = ?`, chainID, hash, address)
|
||||
err = row.Scan(&mTID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
||||
10
vendor/github.com/status-im/status-go/services/wallet/transfer/downloader.go
generated
vendored
10
vendor/github.com/status-im/status-go/services/wallet/transfer/downloader.go
generated
vendored
@@ -63,7 +63,7 @@ type Transfer struct {
|
||||
TokenValue *big.Int `json:"tokenValue"`
|
||||
BaseGasFees string
|
||||
// Internal field that is used to track multi-transaction transfers.
|
||||
MultiTransactionID MultiTransactionIDType `json:"multi_transaction_id"`
|
||||
MultiTransactionID w_common.MultiTransactionIDType `json:"multi_transaction_id"`
|
||||
}
|
||||
|
||||
// ETHDownloader downloads regular eth transfers and tokens transfers.
|
||||
@@ -109,7 +109,7 @@ func getTransferByHash(ctx context.Context, client chain.ClientInterface, signer
|
||||
return nil, err
|
||||
}
|
||||
|
||||
baseGasFee, err := client.GetBaseFeeFromBlock(big.NewInt(int64(transactionLog.BlockNumber)))
|
||||
baseGasFee, err := client.GetBaseFeeFromBlock(ctx, big.NewInt(int64(transactionLog.BlockNumber)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -288,7 +288,7 @@ func (d *ETHDownloader) getTransfersInBlock(ctx context.Context, blk *types.Bloc
|
||||
Receipt: receipt,
|
||||
Log: nil,
|
||||
BaseGasFees: blk.BaseFee().String(),
|
||||
MultiTransactionID: NoMultiTransactionID})
|
||||
MultiTransactionID: w_common.NoMultiTransactionID})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -416,7 +416,7 @@ func (d *ETHDownloader) subTransactionsFromPreloaded(preloadedTx *PreloadedTrans
|
||||
Transaction: tx,
|
||||
Receipt: receipt,
|
||||
Timestamp: blk.Time(),
|
||||
MultiTransactionID: NoMultiTransactionID,
|
||||
MultiTransactionID: w_common.NoMultiTransactionID,
|
||||
}
|
||||
|
||||
rst = append(rst, transfer)
|
||||
@@ -451,7 +451,7 @@ func (d *ETHDownloader) subTransactionsFromTransactionData(address, from common.
|
||||
Transaction: tx,
|
||||
Receipt: receipt,
|
||||
Timestamp: blk.Time(),
|
||||
MultiTransactionID: NoMultiTransactionID,
|
||||
MultiTransactionID: w_common.NoMultiTransactionID,
|
||||
}
|
||||
|
||||
rst = append(rst, transfer)
|
||||
|
||||
15
vendor/github.com/status-im/status-go/services/wallet/transfer/testutils.go
generated
vendored
15
vendor/github.com/status-im/status-go/services/wallet/transfer/testutils.go
generated
vendored
@@ -27,7 +27,7 @@ type TestTransaction struct {
|
||||
Success bool
|
||||
Nonce uint64
|
||||
Contract eth_common.Address
|
||||
MultiTransactionID MultiTransactionIDType
|
||||
MultiTransactionID common.MultiTransactionIDType
|
||||
}
|
||||
|
||||
type TestTransfer struct {
|
||||
@@ -38,7 +38,7 @@ type TestTransfer struct {
|
||||
}
|
||||
|
||||
type TestMultiTransaction struct {
|
||||
MultiTransactionID MultiTransactionIDType
|
||||
MultiTransactionID common.MultiTransactionIDType
|
||||
MultiTransactionType MultiTransactionType
|
||||
FromAddress eth_common.Address
|
||||
ToAddress eth_common.Address
|
||||
@@ -78,7 +78,7 @@ func generateTestTransaction(seed int) TestTransaction {
|
||||
Nonce: uint64(seed),
|
||||
// In practice this is last20Bytes(Keccak256(RLP(From, nonce)))
|
||||
Contract: eth_common.HexToAddress(fmt.Sprintf("0x4%d", seed)),
|
||||
MultiTransactionID: NoMultiTransactionID,
|
||||
MultiTransactionID: common.NoMultiTransactionID,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,6 +161,11 @@ var TestCollectibles = []TestCollectible{
|
||||
TokenID: big.NewInt(1),
|
||||
ChainID: 1,
|
||||
},
|
||||
TestCollectible{ // TokenID (big.Int) value 0 might be problematic if not handled properly
|
||||
TokenAddress: eth_common.HexToAddress("0x97a04fda4d97c6e3547d66b572e29f4a4ff4ABCD"),
|
||||
TokenID: big.NewInt(0),
|
||||
ChainID: 420,
|
||||
},
|
||||
TestCollectible{
|
||||
TokenAddress: eth_common.HexToAddress("0x1dea7a3e04849840c0eb15fd26a55f6c40c4a69b"),
|
||||
TokenID: big.NewInt(11),
|
||||
@@ -362,7 +367,7 @@ func InsertTestPendingTransaction(tb testing.TB, db *sql.DB, tr *TestTransfer) {
|
||||
require.NoError(tb, err)
|
||||
}
|
||||
|
||||
func InsertTestMultiTransaction(tb testing.TB, db *sql.DB, tr *TestMultiTransaction) MultiTransactionIDType {
|
||||
func InsertTestMultiTransaction(tb testing.TB, db *sql.DB, tr *TestMultiTransaction) common.MultiTransactionIDType {
|
||||
fromTokenType := tr.FromToken
|
||||
if tr.FromToken == "" {
|
||||
fromTokenType = testutils.EthSymbol
|
||||
@@ -381,7 +386,7 @@ func InsertTestMultiTransaction(tb testing.TB, db *sql.DB, tr *TestMultiTransact
|
||||
require.NoError(tb, err)
|
||||
rowID, err := result.LastInsertId()
|
||||
require.NoError(tb, err)
|
||||
tr.MultiTransactionID = MultiTransactionIDType(rowID)
|
||||
tr.MultiTransactionID = common.MultiTransactionIDType(rowID)
|
||||
return tr.MultiTransactionID
|
||||
}
|
||||
|
||||
|
||||
49
vendor/github.com/status-im/status-go/services/wallet/transfer/transaction_manager.go
generated
vendored
49
vendor/github.com/status-im/status-go/services/wallet/transfer/transaction_manager.go
generated
vendored
@@ -21,11 +21,7 @@ import (
|
||||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
||||
type MultiTransactionIDType int64
|
||||
|
||||
const (
|
||||
NoMultiTransactionID = MultiTransactionIDType(0)
|
||||
|
||||
// EventMTTransactionUpdate is emitted when a multi-transaction is updated (added or deleted)
|
||||
EventMTTransactionUpdate walletevent.EventType = "multi-transaction-update"
|
||||
)
|
||||
@@ -38,6 +34,7 @@ type SignatureDetails struct {
|
||||
|
||||
type TransactionDescription struct {
|
||||
chainID uint64
|
||||
from common.Address
|
||||
builtTx *ethTypes.Transaction
|
||||
signature []byte
|
||||
}
|
||||
@@ -89,19 +86,19 @@ const (
|
||||
)
|
||||
|
||||
type MultiTransaction struct {
|
||||
ID uint `json:"id"`
|
||||
Timestamp uint64 `json:"timestamp"`
|
||||
FromNetworkID uint64 `json:"fromNetworkID"`
|
||||
ToNetworkID uint64 `json:"toNetworkID"`
|
||||
FromTxHash common.Hash `json:"fromTxHash"`
|
||||
ToTxHash common.Hash `json:"toTxHash"`
|
||||
FromAddress common.Address `json:"fromAddress"`
|
||||
ToAddress common.Address `json:"toAddress"`
|
||||
FromAsset string `json:"fromAsset"`
|
||||
ToAsset string `json:"toAsset"`
|
||||
FromAmount *hexutil.Big `json:"fromAmount"`
|
||||
ToAmount *hexutil.Big `json:"toAmount"`
|
||||
Type MultiTransactionType `json:"type"`
|
||||
ID wallet_common.MultiTransactionIDType `json:"id"`
|
||||
Timestamp uint64 `json:"timestamp"`
|
||||
FromNetworkID uint64 `json:"fromNetworkID"`
|
||||
ToNetworkID uint64 `json:"toNetworkID"`
|
||||
FromTxHash common.Hash `json:"fromTxHash"`
|
||||
ToTxHash common.Hash `json:"toTxHash"`
|
||||
FromAddress common.Address `json:"fromAddress"`
|
||||
ToAddress common.Address `json:"toAddress"`
|
||||
FromAsset string `json:"fromAsset"`
|
||||
ToAsset string `json:"toAsset"`
|
||||
FromAmount *hexutil.Big `json:"fromAmount"`
|
||||
ToAmount *hexutil.Big `json:"toAmount"`
|
||||
Type MultiTransactionType `json:"type"`
|
||||
CrossTxID string
|
||||
}
|
||||
|
||||
@@ -226,21 +223,5 @@ func (tm *TransactionManager) BuildRawTransaction(chainID uint64, sendArgs trans
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) SendTransactionWithSignature(chainID uint64, txType transactions.PendingTrxType, sendArgs transactions.SendTxArgs, signature []byte) (hash types.Hash, err error) {
|
||||
hash, err = tm.transactor.BuildTransactionAndSendWithSignature(chainID, sendArgs, signature)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
}
|
||||
|
||||
err = tm.pendingTracker.TrackPendingTransaction(
|
||||
wallet_common.ChainID(chainID),
|
||||
common.Hash(hash),
|
||||
common.Address(sendArgs.From),
|
||||
txType,
|
||||
transactions.AutoDelete,
|
||||
)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
}
|
||||
|
||||
return hash, nil
|
||||
return tm.transactor.BuildTransactionAndSendWithSignature(chainID, sendArgs, signature)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
"github.com/status-im/status-go/services/wallet/bridge"
|
||||
wallet_common "github.com/status-im/status-go/services/wallet/common"
|
||||
"github.com/status-im/status-go/services/wallet/walletevent"
|
||||
@@ -90,11 +89,11 @@ func getMultiTransactionTimestamp(multiTransaction *MultiTransaction) uint64 {
|
||||
}
|
||||
|
||||
// insertMultiTransaction inserts a multi transaction into the database and updates multi-transaction ID and timestamp
|
||||
func insertMultiTransaction(db *sql.DB, multiTransaction *MultiTransaction) (MultiTransactionIDType, error) {
|
||||
func insertMultiTransaction(db *sql.DB, multiTransaction *MultiTransaction) (wallet_common.MultiTransactionIDType, error) {
|
||||
insert, err := db.Prepare(fmt.Sprintf(`INSERT INTO multi_transactions (%s)
|
||||
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, multiTransactionColumns))
|
||||
if err != nil {
|
||||
return NoMultiTransactionID, err
|
||||
return wallet_common.NoMultiTransactionID, err
|
||||
}
|
||||
timestamp := getMultiTransactionTimestamp(multiTransaction)
|
||||
result, err := insert.Exec(
|
||||
@@ -113,22 +112,22 @@ func insertMultiTransaction(db *sql.DB, multiTransaction *MultiTransaction) (Mul
|
||||
timestamp,
|
||||
)
|
||||
if err != nil {
|
||||
return NoMultiTransactionID, err
|
||||
return wallet_common.NoMultiTransactionID, err
|
||||
}
|
||||
defer insert.Close()
|
||||
multiTransactionID, err := result.LastInsertId()
|
||||
|
||||
multiTransaction.Timestamp = timestamp
|
||||
multiTransaction.ID = uint(multiTransactionID)
|
||||
multiTransaction.ID = wallet_common.MultiTransactionIDType(multiTransactionID)
|
||||
|
||||
return MultiTransactionIDType(multiTransactionID), err
|
||||
return wallet_common.MultiTransactionIDType(multiTransactionID), err
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) InsertMultiTransaction(multiTransaction *MultiTransaction) (MultiTransactionIDType, error) {
|
||||
func (tm *TransactionManager) InsertMultiTransaction(multiTransaction *MultiTransaction) (wallet_common.MultiTransactionIDType, error) {
|
||||
return tm.insertMultiTransactionAndNotify(tm.db, multiTransaction, nil)
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) insertMultiTransactionAndNotify(db *sql.DB, multiTransaction *MultiTransaction, chainIDs []uint64) (MultiTransactionIDType, error) {
|
||||
func (tm *TransactionManager) insertMultiTransactionAndNotify(db *sql.DB, multiTransaction *MultiTransaction, chainIDs []uint64) (wallet_common.MultiTransactionIDType, error) {
|
||||
id, err := insertMultiTransaction(db, multiTransaction)
|
||||
if err != nil {
|
||||
publishMultiTransactionUpdatedEvent(db, multiTransaction, tm.eventFeed, chainIDs)
|
||||
@@ -156,7 +155,7 @@ func publishMultiTransactionUpdatedEvent(db *sql.DB, multiTransaction *MultiTran
|
||||
}
|
||||
|
||||
func updateMultiTransaction(db *sql.DB, multiTransaction *MultiTransaction) error {
|
||||
if MultiTransactionIDType(multiTransaction.ID) == NoMultiTransactionID {
|
||||
if multiTransaction.ID == wallet_common.NoMultiTransactionID {
|
||||
return fmt.Errorf("no multitransaction ID")
|
||||
}
|
||||
|
||||
@@ -211,7 +210,7 @@ func (tm *TransactionManager) CreateMultiTransactionFromCommand(ctx context.Cont
|
||||
return nil, err
|
||||
}
|
||||
|
||||
multiTransaction.ID = uint(multiTransactionID)
|
||||
multiTransaction.ID = multiTransactionID
|
||||
if password == "" {
|
||||
acc, err := tm.accountsDB.GetAccountByAddress(types.Address(multiTransaction.FromAddress))
|
||||
if err != nil {
|
||||
@@ -244,11 +243,6 @@ func (tm *TransactionManager) CreateMultiTransactionFromCommand(ctx context.Cont
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = tm.storePendingTransactions(multiTransaction, hashes, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &MultiTransactionCommandResult{
|
||||
ID: int64(multiTransactionID),
|
||||
Hashes: hashes,
|
||||
@@ -292,64 +286,26 @@ func (tm *TransactionManager) ProceedWithTransactionsSignatures(ctx context.Cont
|
||||
// send transactions
|
||||
hashes := make(map[uint64][]types.Hash)
|
||||
for _, desc := range tm.transactionsForKeycardSingning {
|
||||
hash, err := tm.transactor.AddSignatureToTransactionAndSend(desc.chainID, desc.builtTx, desc.signature)
|
||||
hash, err := tm.transactor.AddSignatureToTransactionAndSend(
|
||||
desc.chainID,
|
||||
desc.from,
|
||||
tm.multiTransactionForKeycardSigning.FromAsset,
|
||||
tm.multiTransactionForKeycardSigning.ID,
|
||||
desc.builtTx,
|
||||
desc.signature,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hashes[desc.chainID] = append(hashes[desc.chainID], hash)
|
||||
}
|
||||
|
||||
err := tm.storePendingTransactions(tm.multiTransactionForKeycardSigning, hashes, tm.transactionsBridgeData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &MultiTransactionCommandResult{
|
||||
ID: int64(tm.multiTransactionForKeycardSigning.ID),
|
||||
Hashes: hashes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) storePendingTransactions(multiTransaction *MultiTransaction,
|
||||
hashes map[uint64][]types.Hash, data []*bridge.TransactionBridge) error {
|
||||
|
||||
txs := createPendingTransactions(hashes, data, multiTransaction)
|
||||
for _, tx := range txs {
|
||||
err := tm.pendingTracker.StoreAndTrackPendingTx(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createPendingTransactions(hashes map[uint64][]types.Hash, data []*bridge.TransactionBridge,
|
||||
multiTransaction *MultiTransaction) []*transactions.PendingTransaction {
|
||||
|
||||
txs := make([]*transactions.PendingTransaction, 0)
|
||||
for _, tx := range data {
|
||||
for _, hash := range hashes[tx.ChainID] {
|
||||
pendingTransaction := &transactions.PendingTransaction{
|
||||
Hash: common.Hash(hash),
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
Value: bigint.BigInt{Int: multiTransaction.FromAmount.ToInt()},
|
||||
From: common.Address(tx.From()),
|
||||
To: common.Address(tx.To()),
|
||||
Data: tx.Data().String(),
|
||||
Type: transactions.WalletTransfer,
|
||||
ChainID: wallet_common.ChainID(tx.ChainID),
|
||||
MultiTransactionID: int64(multiTransaction.ID),
|
||||
Symbol: multiTransaction.FromAsset,
|
||||
AutoDelete: new(bool),
|
||||
}
|
||||
// Transaction downloader will delete pending transaction as soon as it is confirmed
|
||||
*pendingTransaction.AutoDelete = false
|
||||
txs = append(txs, pendingTransaction)
|
||||
}
|
||||
}
|
||||
return txs
|
||||
}
|
||||
|
||||
func multiTransactionFromCommand(command *MultiTransactionCommand) *MultiTransaction {
|
||||
|
||||
log.Info("Creating multi transaction", "command", command)
|
||||
@@ -380,6 +336,7 @@ func (tm *TransactionManager) buildTransactions(bridges map[string]bridge.Bridge
|
||||
txHash := signer.Hash(builtTx)
|
||||
|
||||
tm.transactionsForKeycardSingning[txHash] = &TransactionDescription{
|
||||
from: common.Address(bridgeTx.From()),
|
||||
chainID: bridgeTx.ChainID,
|
||||
builtTx: builtTx,
|
||||
}
|
||||
@@ -403,6 +360,27 @@ func (tm *TransactionManager) sendTransactions(multiTransaction *MultiTransactio
|
||||
|
||||
hashes := make(map[uint64][]types.Hash)
|
||||
for _, tx := range data {
|
||||
if tx.TransferTx != nil {
|
||||
tx.TransferTx.MultiTransactionID = multiTransaction.ID
|
||||
tx.TransferTx.Symbol = multiTransaction.FromAsset
|
||||
}
|
||||
if tx.HopTx != nil {
|
||||
tx.HopTx.MultiTransactionID = multiTransaction.ID
|
||||
tx.HopTx.Symbol = multiTransaction.FromAsset
|
||||
}
|
||||
if tx.CbridgeTx != nil {
|
||||
tx.CbridgeTx.MultiTransactionID = multiTransaction.ID
|
||||
tx.CbridgeTx.Symbol = multiTransaction.FromAsset
|
||||
}
|
||||
if tx.ERC721TransferTx != nil {
|
||||
tx.ERC721TransferTx.MultiTransactionID = multiTransaction.ID
|
||||
tx.ERC721TransferTx.Symbol = multiTransaction.FromAsset
|
||||
}
|
||||
if tx.ERC1155TransferTx != nil {
|
||||
tx.ERC1155TransferTx.MultiTransactionID = multiTransaction.ID
|
||||
tx.ERC1155TransferTx.Symbol = multiTransaction.FromAsset
|
||||
}
|
||||
|
||||
hash, err := bridges[tx.BridgeName].Send(tx, selectedAccount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -412,7 +390,7 @@ func (tm *TransactionManager) sendTransactions(multiTransaction *MultiTransactio
|
||||
return hashes, nil
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) GetMultiTransactions(ctx context.Context, ids []MultiTransactionIDType) ([]*MultiTransaction, error) {
|
||||
func (tm *TransactionManager) GetMultiTransactions(ctx context.Context, ids []wallet_common.MultiTransactionIDType) ([]*MultiTransaction, error) {
|
||||
placeholders := make([]string, len(ids))
|
||||
args := make([]interface{}, len(ids))
|
||||
for i, v := range ids {
|
||||
|
||||
14
vendor/github.com/status-im/status-go/services/wallet/walletevent/events.go
generated
vendored
14
vendor/github.com/status-im/status-go/services/wallet/walletevent/events.go
generated
vendored
@@ -1,6 +1,7 @@
|
||||
package walletevent
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
@@ -31,3 +32,16 @@ type Event struct {
|
||||
// For Internal events only, not serialized
|
||||
EventParams interface{}
|
||||
}
|
||||
|
||||
func GetPayload[T any](e Event) (*T, error) {
|
||||
var payload T
|
||||
err := json.Unmarshal([]byte(e.Message), &payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &payload, nil
|
||||
}
|
||||
|
||||
func ExtractPayload[T any](e Event, payload *T) error {
|
||||
return json.Unmarshal([]byte(e.Message), payload)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user