github.com/status-im/status-go@v1.1.0/protocol/persistence_profile_showcase.go (about)

     1  package protocol
     2  
     3  import (
     4  	"context"
     5  	"database/sql"
     6  	"errors"
     7  
     8  	"github.com/status-im/status-go/protocol/identity"
     9  )
    10  
    11  // Profile showcase preferences
    12  const upsertProfileShowcasePreferencesQuery = "UPDATE profile_showcase_preferences SET clock=? WHERE NOT EXISTS (SELECT 1 FROM profile_showcase_preferences WHERE clock >= ?)"
    13  const selectProfileShowcasePreferencesQuery = "SELECT clock FROM profile_showcase_preferences"
    14  
    15  const upsertProfileShowcaseCommunityPreferenceQuery = "INSERT OR REPLACE INTO profile_showcase_communities_preferences(community_id, visibility, sort_order) VALUES (?, ?, ?)" // #nosec G101
    16  const selectProfileShowcaseCommunityPreferenceQuery = "SELECT community_id, visibility, sort_order FROM profile_showcase_communities_preferences"                              // #nosec G101
    17  const selectSpecifiedShowcaseCommunityPreferenceQuery = "SELECT community_id, visibility, sort_order FROM profile_showcase_communities_preferences WHERE community_id = ?"     // #nosec G101
    18  const deleteProfileShowcaseCommunityPreferenceQuery = "DELETE FROM profile_showcase_communities_preferences WHERE community_id = ?"                                            // #nosec G101
    19  const clearProfileShowcaseCommunitiyPreferencesQuery = "DELETE FROM profile_showcase_communities_preferences"                                                                  // #nosec G101
    20  
    21  const upsertProfileShowcaseAccountPreferenceQuery = "INSERT OR REPLACE INTO profile_showcase_accounts_preferences(address, visibility, sort_order) VALUES (?, ?, ?)" // #nosec G101
    22  const selectProfileShowcaseAccountPreferenceQuery = "SELECT address, visibility, sort_order FROM profile_showcase_accounts_preferences"                              // #nosec G101
    23  const selectSpecifiedShowcaseAccountPreferenceQuery = "SELECT address, visibility, sort_order FROM profile_showcase_accounts_preferences WHERE address = ?"          // #nosec G101
    24  const deleteProfileShowcaseAccountPreferenceQuery = "DELETE FROM profile_showcase_accounts_preferences WHERE address = ?"                                            // #nosec G101
    25  const clearProfileShowcaseAccountPreferencesQuery = "DELETE FROM profile_showcase_accounts_preferences"                                                              // #nosec G101
    26  
    27  const upsertProfileShowcaseCollectiblePreferenceQuery = "INSERT OR REPLACE INTO profile_showcase_collectibles_preferences(contract_address, chain_id, token_id, visibility, sort_order) VALUES (?, ?, ?, ?, ?)" // #nosec G101
    28  const selectProfileShowcaseCollectiblePreferenceQuery = "SELECT contract_address, chain_id, token_id, visibility, sort_order FROM profile_showcase_collectibles_preferences"                                    // #nosec G101
    29  const clearProfileShowcaseCollectiblePreferencesQuery = "DELETE FROM profile_showcase_collectibles_preferences"                                                                                                 // #nosec G101
    30  
    31  const upsertProfileShowcaseVerifiedTokenPreferenceQuery = "INSERT OR REPLACE INTO profile_showcase_verified_tokens_preferences(symbol, visibility, sort_order) VALUES (?, ?, ?)" // #nosec G101
    32  const selectProfileShowcaseVerifiedTokenPreferenceQuery = "SELECT symbol, visibility, sort_order FROM profile_showcase_verified_tokens_preferences"                              // #nosec G101
    33  const clearProfileShowcaseVerifiedTokenPreferencesQuery = "DELETE FROM profile_showcase_verified_tokens_preferences"                                                             // #nosec G101
    34  
    35  const upsertProfileShowcaseUnverifiedTokenPreferenceQuery = "INSERT OR REPLACE INTO profile_showcase_unverified_tokens_preferences(contract_address, chain_id, visibility, sort_order) VALUES (?, ?, ?, ?)" // #nosec G101
    36  const selectProfileShowcaseUnverifiedTokenPreferenceQuery = "SELECT contract_address, chain_id, visibility, sort_order FROM profile_showcase_unverified_tokens_preferences"                                 // #nosec G101
    37  const clearProfileShowcaseUnverifiedTokenPreferencesQuery = "DELETE FROM profile_showcase_unverified_tokens_preferences"                                                                                    // #nosec G101
    38  
    39  const upsertProfileShowcaseSocialLinkPreferenceQuery = "INSERT OR REPLACE INTO profile_showcase_social_links_preferences(url, text, visibility, sort_order) VALUES (?, ?, ?, ?)" // #nosec G101
    40  const selectProfileShowcaseSocialLinkPreferenceQuery = "SELECT url, text, visibility, sort_order FROM profile_showcase_social_links_preferences"                                 // #nosec G101
    41  const clearProfileShowcaseSocialLinkPreferencesQuery = "DELETE FROM profile_showcase_social_links_preferences"                                                                   // #nosec G101
    42  
    43  // Profile showcase for a contact
    44  const upsertContactProfileShowcaseCommunityQuery = "INSERT OR REPLACE INTO profile_showcase_communities_contacts(contact_id, community_id, sort_order, grant) VALUES (?, ?, ?, ?)" // #nosec G101
    45  const selectContactProfileShowcaseCommunityQuery = "SELECT community_id, sort_order, grant FROM profile_showcase_communities_contacts WHERE contact_id = ?"                        // #nosec G101
    46  const removeContactProfileShowcaseCommunityQuery = "DELETE FROM profile_showcase_communities_contacts WHERE contact_id = ?"                                                        // #nosec G101
    47  
    48  const upsertContactProfileShowcaseAccountQuery = "INSERT OR REPLACE INTO profile_showcase_accounts_contacts(contact_id, address, name, color_id, emoji, sort_order) VALUES (?, ?, ?, ?, ?, ?)" // #nosec G101
    49  const selectContactProfileShowcaseAccountQuery = "SELECT * FROM profile_showcase_accounts_contacts WHERE contact_id = ?"                                                                       // #nosec G101
    50  const removeContactProfileShowcaseAccountQuery = "DELETE FROM profile_showcase_accounts_contacts WHERE contact_id = ?"                                                                         // #nosec G101
    51  
    52  const upsertContactProfileShowcaseCollectibleQuery = "INSERT OR REPLACE INTO profile_showcase_collectibles_contacts(contact_id, contract_address, chain_id, token_id, sort_order) VALUES (?, ?, ?, ?, ?)" // #nosec G101
    53  const selectContactProfileShowcaseCollectibleQuery = "SELECT contract_address, chain_id, token_id, sort_order FROM profile_showcase_collectibles_contacts WHERE contact_id = ?"                           // #nosec G101
    54  const removeContactProfileShowcaseCollectibleQuery = "DELETE FROM profile_showcase_collectibles_contacts WHERE contact_id = ?"                                                                            // #nosec G101
    55  
    56  const upsertContactProfileShowcaseVerifiedTokenQuery = "INSERT OR REPLACE INTO profile_showcase_verified_tokens_contacts(contact_id, symbol, sort_order) VALUES (?, ?, ?)" // #nosec G101
    57  const selectContactProfileShowcaseVerifiedTokenQuery = "SELECT symbol, sort_order FROM profile_showcase_verified_tokens_contacts WHERE contact_id = ?"                     // #nosec G101
    58  const removeContactProfileShowcaseVerifiedTokenQuery = "DELETE FROM profile_showcase_verified_tokens_contacts WHERE contact_id = ?"                                        // #nosec G101
    59  
    60  const upsertContactProfileShowcaseUnverifiedTokenQuery = "INSERT OR REPLACE INTO profile_showcase_unverified_tokens_contacts(contact_id, contract_address, chain_id, sort_order) VALUES (?, ?, ?, ?)" // #nosec G101
    61  const selectContactProfileShowcaseUnverifiedTokenQuery = "SELECT contract_address, chain_id, sort_order FROM profile_showcase_unverified_tokens_contacts WHERE contact_id = ?"                        // #nosec G101
    62  const removeContactProfileShowcaseUnverifiedTokenQuery = "DELETE FROM profile_showcase_unverified_tokens_contacts WHERE contact_id = ?"                                                               // #nosec G101
    63  
    64  const upsertContactProfileShowcaseSocialLinkQuery = "INSERT OR REPLACE INTO profile_showcase_social_links_contacts(contact_id, url, text, sort_order) VALUES (?, ?, ?, ?)" // #nosec G101
    65  const selectContactProfileShowcaseSocialLinkQuery = "SELECT url, text, sort_order FROM profile_showcase_social_links_contacts WHERE contact_id = ?"                        // #nosec G101
    66  const removeContactProfileShowcaseSocialLinkQuery = "DELETE FROM profile_showcase_social_links_contacts WHERE contact_id = ?"                                              // #nosec G101
    67  
    68  const selectProfileShowcaseAccountsWhichMatchTheAddress = `
    69  SELECT psa.*
    70  FROM
    71  	contacts c
    72  LEFT JOIN
    73  	profile_showcase_accounts_contacts psa
    74  ON
    75  	c.id = psa.contact_id
    76  WHERE
    77  	psa.address = ?
    78  `
    79  
    80  // Queries for the profile showcase preferences
    81  
    82  func (db sqlitePersistence) saveProfileShowcasePreferencesClock(tx *sql.Tx, clock uint64) error {
    83  	_, err := tx.Exec(upsertProfileShowcasePreferencesQuery, clock, clock)
    84  	return err
    85  }
    86  
    87  func (db sqlitePersistence) getProfileShowcasePreferencesClock(tx *sql.Tx) (uint64, error) {
    88  	var clock uint64
    89  	err := tx.QueryRow(selectProfileShowcasePreferencesQuery).Scan(&clock)
    90  	return clock, err
    91  }
    92  
    93  func (db sqlitePersistence) saveProfileShowcaseCommunityPreference(tx *sql.Tx, community *identity.ProfileShowcaseCommunityPreference) error {
    94  	_, err := tx.Exec(upsertProfileShowcaseCommunityPreferenceQuery,
    95  		community.CommunityID,
    96  		community.ShowcaseVisibility,
    97  		community.Order,
    98  	)
    99  
   100  	return err
   101  }
   102  
   103  func (db sqlitePersistence) processProfileShowcaseCommunityPreferences(rows *sql.Rows) (result []*identity.ProfileShowcaseCommunityPreference, err error) {
   104  	if rows == nil {
   105  		return nil, errors.New("rows is nil")
   106  	}
   107  
   108  	for rows.Next() {
   109  		community := &identity.ProfileShowcaseCommunityPreference{}
   110  
   111  		err := rows.Scan(
   112  			&community.CommunityID,
   113  			&community.ShowcaseVisibility,
   114  			&community.Order,
   115  		)
   116  
   117  		if err != nil {
   118  			return nil, err
   119  		}
   120  
   121  		result = append(result, community)
   122  	}
   123  
   124  	err = rows.Err()
   125  	return
   126  }
   127  
   128  func (db sqlitePersistence) getProfileShowcaseCommunitiesPreferences(tx *sql.Tx) ([]*identity.ProfileShowcaseCommunityPreference, error) {
   129  	rows, err := tx.Query(selectProfileShowcaseCommunityPreferenceQuery)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  
   134  	return db.processProfileShowcaseCommunityPreferences(rows)
   135  }
   136  
   137  func (db sqlitePersistence) GetProfileShowcaseCommunityPreference(communityID string) (*identity.ProfileShowcaseCommunityPreference, error) {
   138  	rows, err := db.db.Query(selectSpecifiedShowcaseCommunityPreferenceQuery, communityID)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	communities, err := db.processProfileShowcaseCommunityPreferences(rows)
   144  	if len(communities) > 0 {
   145  		return communities[0], err
   146  	}
   147  	return nil, err
   148  }
   149  
   150  func (db sqlitePersistence) DeleteProfileShowcaseCommunityPreference(communityID string) (bool, error) {
   151  	result, err := db.db.Exec(deleteProfileShowcaseCommunityPreferenceQuery, communityID)
   152  	if err != nil {
   153  		return false, err
   154  	}
   155  
   156  	rows, err := result.RowsAffected()
   157  	return rows > 0, err
   158  }
   159  
   160  func (db sqlitePersistence) clearProfileShowcaseCommunityPreferences(tx *sql.Tx) error {
   161  	_, err := tx.Exec(clearProfileShowcaseCommunitiyPreferencesQuery)
   162  	return err
   163  }
   164  
   165  func (db sqlitePersistence) saveProfileShowcaseAccountPreference(tx *sql.Tx, account *identity.ProfileShowcaseAccountPreference) error {
   166  	_, err := tx.Exec(upsertProfileShowcaseAccountPreferenceQuery,
   167  		account.Address,
   168  		account.ShowcaseVisibility,
   169  		account.Order,
   170  	)
   171  
   172  	return err
   173  }
   174  
   175  func (db sqlitePersistence) processProfileShowcaseAccountPreferences(rows *sql.Rows) (result []*identity.ProfileShowcaseAccountPreference, err error) {
   176  	if rows == nil {
   177  		return nil, errors.New("rows is nil")
   178  	}
   179  
   180  	for rows.Next() {
   181  		account := &identity.ProfileShowcaseAccountPreference{}
   182  
   183  		err := rows.Scan(
   184  			&account.Address,
   185  			&account.ShowcaseVisibility,
   186  			&account.Order,
   187  		)
   188  
   189  		if err != nil {
   190  			return nil, err
   191  		}
   192  
   193  		result = append(result, account)
   194  	}
   195  
   196  	err = rows.Err()
   197  	return
   198  }
   199  
   200  func (db sqlitePersistence) getProfileShowcaseAccountsPreferences(tx *sql.Tx) ([]*identity.ProfileShowcaseAccountPreference, error) {
   201  	rows, err := tx.Query(selectProfileShowcaseAccountPreferenceQuery)
   202  	if err != nil {
   203  		return nil, err
   204  	}
   205  
   206  	return db.processProfileShowcaseAccountPreferences(rows)
   207  }
   208  
   209  func (db sqlitePersistence) GetProfileShowcaseAccountPreference(accountAddress string) (*identity.ProfileShowcaseAccountPreference, error) {
   210  	rows, err := db.db.Query(selectSpecifiedShowcaseAccountPreferenceQuery, accountAddress)
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  
   215  	accounts, err := db.processProfileShowcaseAccountPreferences(rows)
   216  	if len(accounts) > 0 {
   217  		return accounts[0], err
   218  	}
   219  	return nil, err
   220  }
   221  
   222  func (db sqlitePersistence) DeleteProfileShowcaseAccountPreference(accountAddress string) (bool, error) {
   223  	result, err := db.db.Exec(deleteProfileShowcaseAccountPreferenceQuery, accountAddress)
   224  	if err != nil {
   225  		return false, err
   226  	}
   227  
   228  	rows, err := result.RowsAffected()
   229  	return rows > 0, err
   230  }
   231  
   232  func (db sqlitePersistence) clearProfileShowcaseAccountPreferences(tx *sql.Tx) error {
   233  	_, err := tx.Exec(clearProfileShowcaseAccountPreferencesQuery)
   234  	return err
   235  }
   236  
   237  func (db sqlitePersistence) saveProfileShowcaseCollectiblePreference(tx *sql.Tx, collectible *identity.ProfileShowcaseCollectiblePreference) error {
   238  	_, err := tx.Exec(upsertProfileShowcaseCollectiblePreferenceQuery,
   239  		collectible.ContractAddress,
   240  		collectible.ChainID,
   241  		collectible.TokenID,
   242  		collectible.ShowcaseVisibility,
   243  		collectible.Order,
   244  	)
   245  
   246  	return err
   247  }
   248  
   249  func (db sqlitePersistence) getProfileShowcaseCollectiblesPreferences(tx *sql.Tx) ([]*identity.ProfileShowcaseCollectiblePreference, error) {
   250  	rows, err := tx.Query(selectProfileShowcaseCollectiblePreferenceQuery)
   251  	if err != nil {
   252  		return nil, err
   253  	}
   254  
   255  	collectibles := []*identity.ProfileShowcaseCollectiblePreference{}
   256  
   257  	for rows.Next() {
   258  		collectible := &identity.ProfileShowcaseCollectiblePreference{}
   259  
   260  		err := rows.Scan(
   261  			&collectible.ContractAddress,
   262  			&collectible.ChainID,
   263  			&collectible.TokenID,
   264  			&collectible.ShowcaseVisibility,
   265  			&collectible.Order,
   266  		)
   267  
   268  		if err != nil {
   269  			return nil, err
   270  		}
   271  
   272  		collectibles = append(collectibles, collectible)
   273  	}
   274  	return collectibles, nil
   275  }
   276  
   277  func (db sqlitePersistence) clearProfileShowcaseCollectiblePreferences(tx *sql.Tx) error {
   278  	_, err := tx.Exec(clearProfileShowcaseCollectiblePreferencesQuery)
   279  	return err
   280  }
   281  
   282  func (db sqlitePersistence) saveProfileShowcaseVerifiedTokenPreference(tx *sql.Tx, token *identity.ProfileShowcaseVerifiedTokenPreference) error {
   283  	_, err := tx.Exec(upsertProfileShowcaseVerifiedTokenPreferenceQuery,
   284  		token.Symbol,
   285  		token.ShowcaseVisibility,
   286  		token.Order,
   287  	)
   288  
   289  	return err
   290  }
   291  
   292  func (db sqlitePersistence) getProfileShowcaseVerifiedTokensPreferences(tx *sql.Tx) ([]*identity.ProfileShowcaseVerifiedTokenPreference, error) {
   293  	rows, err := tx.Query(selectProfileShowcaseVerifiedTokenPreferenceQuery)
   294  	if err != nil {
   295  		return nil, err
   296  	}
   297  
   298  	tokens := []*identity.ProfileShowcaseVerifiedTokenPreference{}
   299  
   300  	for rows.Next() {
   301  		token := &identity.ProfileShowcaseVerifiedTokenPreference{}
   302  
   303  		err := rows.Scan(
   304  			&token.Symbol,
   305  			&token.ShowcaseVisibility,
   306  			&token.Order,
   307  		)
   308  
   309  		if err != nil {
   310  			return nil, err
   311  		}
   312  
   313  		tokens = append(tokens, token)
   314  	}
   315  	return tokens, nil
   316  }
   317  
   318  func (db sqlitePersistence) clearProfileShowcaseVerifiedTokenPreferences(tx *sql.Tx) error {
   319  	_, err := tx.Exec(clearProfileShowcaseVerifiedTokenPreferencesQuery)
   320  	return err
   321  }
   322  
   323  func (db sqlitePersistence) saveProfileShowcaseUnverifiedTokenPreference(tx *sql.Tx, token *identity.ProfileShowcaseUnverifiedTokenPreference) error {
   324  	_, err := tx.Exec(upsertProfileShowcaseUnverifiedTokenPreferenceQuery,
   325  		token.ContractAddress,
   326  		token.ChainID,
   327  		token.ShowcaseVisibility,
   328  		token.Order,
   329  	)
   330  
   331  	return err
   332  }
   333  
   334  func (db sqlitePersistence) getProfileShowcaseUnverifiedTokensPreferences(tx *sql.Tx) ([]*identity.ProfileShowcaseUnverifiedTokenPreference, error) {
   335  	rows, err := tx.Query(selectProfileShowcaseUnverifiedTokenPreferenceQuery)
   336  	if err != nil {
   337  		return nil, err
   338  	}
   339  
   340  	tokens := []*identity.ProfileShowcaseUnverifiedTokenPreference{}
   341  
   342  	for rows.Next() {
   343  		token := &identity.ProfileShowcaseUnverifiedTokenPreference{}
   344  
   345  		err := rows.Scan(
   346  			&token.ContractAddress,
   347  			&token.ChainID,
   348  			&token.ShowcaseVisibility,
   349  			&token.Order,
   350  		)
   351  
   352  		if err != nil {
   353  			return nil, err
   354  		}
   355  
   356  		tokens = append(tokens, token)
   357  	}
   358  	return tokens, nil
   359  }
   360  
   361  func (db sqlitePersistence) clearProfileShowcaseUnverifiedTokenPreferences(tx *sql.Tx) error {
   362  	_, err := tx.Exec(clearProfileShowcaseUnverifiedTokenPreferencesQuery)
   363  	return err
   364  }
   365  
   366  func (db sqlitePersistence) saveProfileShowcaseSocialLinkPreference(tx *sql.Tx, link *identity.ProfileShowcaseSocialLinkPreference) error {
   367  	_, err := tx.Exec(upsertProfileShowcaseSocialLinkPreferenceQuery,
   368  		link.URL,
   369  		link.Text,
   370  		link.ShowcaseVisibility,
   371  		link.Order,
   372  	)
   373  
   374  	return err
   375  }
   376  
   377  func (db sqlitePersistence) getProfileShowcaseSocialLinkPreferences(tx *sql.Tx) ([]*identity.ProfileShowcaseSocialLinkPreference, error) {
   378  	rows, err := tx.Query(selectProfileShowcaseSocialLinkPreferenceQuery)
   379  	if err != nil {
   380  		return nil, err
   381  	}
   382  
   383  	links := []*identity.ProfileShowcaseSocialLinkPreference{}
   384  
   385  	for rows.Next() {
   386  		link := &identity.ProfileShowcaseSocialLinkPreference{}
   387  
   388  		err := rows.Scan(
   389  			&link.URL,
   390  			&link.Text,
   391  			&link.ShowcaseVisibility,
   392  			&link.Order,
   393  		)
   394  
   395  		if err != nil {
   396  			return nil, err
   397  		}
   398  
   399  		links = append(links, link)
   400  	}
   401  	return links, nil
   402  }
   403  
   404  func (db sqlitePersistence) clearProfileShowcaseSocialLinkPreferences(tx *sql.Tx) error {
   405  	_, err := tx.Exec(clearProfileShowcaseSocialLinkPreferencesQuery)
   406  	return err
   407  }
   408  
   409  // Queries for the profile showcase for a contact
   410  func (db sqlitePersistence) saveProfileShowcaseCommunityContact(tx *sql.Tx, contactID string, community *identity.ProfileShowcaseCommunity) error {
   411  	_, err := tx.Exec(upsertContactProfileShowcaseCommunityQuery,
   412  		contactID,
   413  		community.CommunityID,
   414  		community.Order,
   415  		community.Grant,
   416  	)
   417  
   418  	return err
   419  }
   420  
   421  func (db sqlitePersistence) getProfileShowcaseCommunitiesContact(tx *sql.Tx, contactID string) ([]*identity.ProfileShowcaseCommunity, error) {
   422  	rows, err := tx.Query(selectContactProfileShowcaseCommunityQuery, contactID)
   423  	if err != nil {
   424  		return nil, err
   425  	}
   426  
   427  	communities := []*identity.ProfileShowcaseCommunity{}
   428  
   429  	for rows.Next() {
   430  		community := &identity.ProfileShowcaseCommunity{
   431  			MembershipStatus: identity.ProfileShowcaseMembershipStatusUnproven,
   432  		}
   433  
   434  		err := rows.Scan(&community.CommunityID, &community.Order, &community.Grant)
   435  		if err != nil {
   436  			return nil, err
   437  		}
   438  
   439  		communities = append(communities, community)
   440  	}
   441  	return communities, nil
   442  }
   443  
   444  func (db sqlitePersistence) clearProfileShowcaseCommunityContact(tx *sql.Tx, contactID string) error {
   445  	_, err := tx.Exec(removeContactProfileShowcaseCommunityQuery, contactID)
   446  	if err != nil {
   447  		return err
   448  	}
   449  
   450  	return nil
   451  }
   452  
   453  func (db sqlitePersistence) saveProfileShowcaseAccountContact(tx *sql.Tx, contactID string, account *identity.ProfileShowcaseAccount) error {
   454  	_, err := tx.Exec(upsertContactProfileShowcaseAccountQuery,
   455  		contactID,
   456  		account.Address,
   457  		account.Name,
   458  		account.ColorID,
   459  		account.Emoji,
   460  		account.Order,
   461  	)
   462  
   463  	return err
   464  }
   465  
   466  func (db sqlitePersistence) processProfileShowcaseAccounts(rows *sql.Rows) (result []*identity.ProfileShowcaseAccount, err error) {
   467  	if rows == nil {
   468  		return nil, errors.New("rows is nil")
   469  	}
   470  
   471  	for rows.Next() {
   472  		account := &identity.ProfileShowcaseAccount{}
   473  		err = rows.Scan(&account.Address, &account.Name, &account.ColorID, &account.Emoji, &account.Order, &account.ContactID)
   474  		if err != nil {
   475  			return
   476  		}
   477  
   478  		result = append(result, account)
   479  	}
   480  
   481  	err = rows.Err()
   482  	return
   483  }
   484  
   485  func (db sqlitePersistence) getProfileShowcaseAccountsContact(tx *sql.Tx, contactID string) ([]*identity.ProfileShowcaseAccount, error) {
   486  	rows, err := tx.Query(selectContactProfileShowcaseAccountQuery, contactID)
   487  	if err != nil {
   488  		return nil, err
   489  	}
   490  
   491  	return db.processProfileShowcaseAccounts(rows)
   492  }
   493  
   494  func (db sqlitePersistence) GetProfileShowcaseAccountsByAddress(address string) ([]*identity.ProfileShowcaseAccount, error) {
   495  	rows, err := db.db.Query(selectProfileShowcaseAccountsWhichMatchTheAddress, address)
   496  	if err != nil {
   497  		return nil, err
   498  	}
   499  
   500  	return db.processProfileShowcaseAccounts(rows)
   501  }
   502  
   503  func (db sqlitePersistence) clearProfileShowcaseAccountsContact(tx *sql.Tx, contactID string) error {
   504  	_, err := tx.Exec(removeContactProfileShowcaseAccountQuery, contactID)
   505  	return err
   506  }
   507  
   508  func (db sqlitePersistence) saveProfileShowcaseCollectibleContact(tx *sql.Tx, contactID string, collectible *identity.ProfileShowcaseCollectible) error {
   509  	_, err := tx.Exec(upsertContactProfileShowcaseCollectibleQuery,
   510  		contactID,
   511  		collectible.ContractAddress,
   512  		collectible.ChainID,
   513  		collectible.TokenID,
   514  		collectible.Order,
   515  	)
   516  
   517  	return err
   518  }
   519  
   520  func (db sqlitePersistence) getProfileShowcaseCollectiblesContact(tx *sql.Tx, contactID string) ([]*identity.ProfileShowcaseCollectible, error) {
   521  	rows, err := tx.Query(selectContactProfileShowcaseCollectibleQuery, contactID)
   522  	if err != nil {
   523  		return nil, err
   524  	}
   525  
   526  	collectibles := []*identity.ProfileShowcaseCollectible{}
   527  
   528  	for rows.Next() {
   529  		collectible := &identity.ProfileShowcaseCollectible{}
   530  
   531  		err := rows.Scan(
   532  			&collectible.ContractAddress,
   533  			&collectible.ChainID,
   534  			&collectible.TokenID,
   535  			&collectible.Order)
   536  		if err != nil {
   537  			return nil, err
   538  		}
   539  
   540  		collectibles = append(collectibles, collectible)
   541  	}
   542  	return collectibles, nil
   543  }
   544  
   545  func (db sqlitePersistence) clearProfileShowcaseCollectiblesContact(tx *sql.Tx, contactID string) error {
   546  	_, err := tx.Exec(removeContactProfileShowcaseCollectibleQuery, contactID)
   547  	return err
   548  }
   549  
   550  func (db sqlitePersistence) saveProfileShowcaseVerifiedTokenContact(tx *sql.Tx, contactID string, token *identity.ProfileShowcaseVerifiedToken) error {
   551  	_, err := tx.Exec(upsertContactProfileShowcaseVerifiedTokenQuery,
   552  		contactID,
   553  		token.Symbol,
   554  		token.Order,
   555  	)
   556  
   557  	return err
   558  }
   559  
   560  func (db sqlitePersistence) getProfileShowcaseVerifiedTokensContact(tx *sql.Tx, contactID string) ([]*identity.ProfileShowcaseVerifiedToken, error) {
   561  	rows, err := tx.Query(selectContactProfileShowcaseVerifiedTokenQuery, contactID)
   562  	if err != nil {
   563  		return nil, err
   564  	}
   565  
   566  	tokens := []*identity.ProfileShowcaseVerifiedToken{}
   567  
   568  	for rows.Next() {
   569  		token := &identity.ProfileShowcaseVerifiedToken{}
   570  
   571  		err := rows.Scan(
   572  			&token.Symbol,
   573  			&token.Order)
   574  		if err != nil {
   575  			return nil, err
   576  		}
   577  
   578  		tokens = append(tokens, token)
   579  	}
   580  	return tokens, nil
   581  }
   582  
   583  func (db sqlitePersistence) clearProfileShowcaseVerifiedTokensContact(tx *sql.Tx, contactID string) error {
   584  	_, err := tx.Exec(removeContactProfileShowcaseVerifiedTokenQuery, contactID)
   585  	return err
   586  }
   587  
   588  func (db sqlitePersistence) saveProfileShowcaseUnverifiedTokenContact(tx *sql.Tx, contactID string, token *identity.ProfileShowcaseUnverifiedToken) error {
   589  	_, err := tx.Exec(upsertContactProfileShowcaseUnverifiedTokenQuery,
   590  		contactID,
   591  		token.ContractAddress,
   592  		token.ChainID,
   593  		token.Order,
   594  	)
   595  
   596  	return err
   597  }
   598  
   599  func (db sqlitePersistence) getProfileShowcaseUnverifiedTokensContact(tx *sql.Tx, contactID string) ([]*identity.ProfileShowcaseUnverifiedToken, error) {
   600  	rows, err := tx.Query(selectContactProfileShowcaseUnverifiedTokenQuery, contactID)
   601  	if err != nil {
   602  		return nil, err
   603  	}
   604  
   605  	tokens := []*identity.ProfileShowcaseUnverifiedToken{}
   606  
   607  	for rows.Next() {
   608  		token := &identity.ProfileShowcaseUnverifiedToken{}
   609  
   610  		err := rows.Scan(
   611  			&token.ContractAddress,
   612  			&token.ChainID,
   613  			&token.Order)
   614  		if err != nil {
   615  			return nil, err
   616  		}
   617  
   618  		tokens = append(tokens, token)
   619  	}
   620  	return tokens, nil
   621  }
   622  
   623  func (db sqlitePersistence) clearProfileShowcaseUnverifiedTokensContact(tx *sql.Tx, contactID string) error {
   624  	_, err := tx.Exec(removeContactProfileShowcaseUnverifiedTokenQuery, contactID)
   625  	return err
   626  }
   627  
   628  func (db sqlitePersistence) saveProfileShowcaseSocialLinkContact(tx *sql.Tx, contactID string, link *identity.ProfileShowcaseSocialLink) error {
   629  	_, err := tx.Exec(upsertContactProfileShowcaseSocialLinkQuery,
   630  		contactID,
   631  		link.URL,
   632  		link.Text,
   633  		link.Order,
   634  	)
   635  
   636  	return err
   637  }
   638  
   639  func (db sqlitePersistence) getProfileShowcaseSocialLinksContact(tx *sql.Tx, contactID string) ([]*identity.ProfileShowcaseSocialLink, error) {
   640  	rows, err := tx.Query(selectContactProfileShowcaseSocialLinkQuery, contactID)
   641  	if err != nil {
   642  		return nil, err
   643  	}
   644  
   645  	links := []*identity.ProfileShowcaseSocialLink{}
   646  
   647  	for rows.Next() {
   648  		link := &identity.ProfileShowcaseSocialLink{}
   649  
   650  		err := rows.Scan(
   651  			&link.URL,
   652  			&link.Text,
   653  			&link.Order)
   654  		if err != nil {
   655  			return nil, err
   656  		}
   657  
   658  		links = append(links, link)
   659  	}
   660  	err = rows.Err()
   661  	if err != nil {
   662  		return nil, err
   663  	}
   664  	return links, nil
   665  }
   666  
   667  func (db sqlitePersistence) clearProfileShowcaseSocialLinksContact(tx *sql.Tx, contactID string) error {
   668  	_, err := tx.Exec(removeContactProfileShowcaseSocialLinkQuery, contactID)
   669  	return err
   670  }
   671  
   672  // public functions
   673  func (db sqlitePersistence) SaveProfileShowcasePreferences(preferences *identity.ProfileShowcasePreferences) error {
   674  	tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{})
   675  	if err != nil {
   676  		return err
   677  	}
   678  	defer func() {
   679  		if err == nil {
   680  			err = tx.Commit()
   681  			return
   682  		}
   683  		// don't shadow original error
   684  		_ = tx.Rollback()
   685  	}()
   686  
   687  	err = db.clearProfileShowcaseCommunityPreferences(tx)
   688  	if err != nil {
   689  		return err
   690  	}
   691  
   692  	for _, community := range preferences.Communities {
   693  		err = db.saveProfileShowcaseCommunityPreference(tx, community)
   694  		if err != nil {
   695  			return err
   696  		}
   697  	}
   698  
   699  	err = db.clearProfileShowcaseAccountPreferences(tx)
   700  	if err != nil {
   701  		return err
   702  	}
   703  
   704  	for _, account := range preferences.Accounts {
   705  		err = db.saveProfileShowcaseAccountPreference(tx, account)
   706  		if err != nil {
   707  			return err
   708  		}
   709  	}
   710  
   711  	err = db.clearProfileShowcaseCollectiblePreferences(tx)
   712  	if err != nil {
   713  		return err
   714  	}
   715  
   716  	for _, collectible := range preferences.Collectibles {
   717  		err = db.saveProfileShowcaseCollectiblePreference(tx, collectible)
   718  		if err != nil {
   719  			return err
   720  		}
   721  	}
   722  
   723  	err = db.clearProfileShowcaseVerifiedTokenPreferences(tx)
   724  	if err != nil {
   725  		return err
   726  	}
   727  
   728  	for _, token := range preferences.VerifiedTokens {
   729  		err = db.saveProfileShowcaseVerifiedTokenPreference(tx, token)
   730  		if err != nil {
   731  			return err
   732  		}
   733  	}
   734  
   735  	err = db.clearProfileShowcaseUnverifiedTokenPreferences(tx)
   736  	if err != nil {
   737  		return err
   738  	}
   739  
   740  	for _, token := range preferences.UnverifiedTokens {
   741  		err = db.saveProfileShowcaseUnverifiedTokenPreference(tx, token)
   742  		if err != nil {
   743  			return err
   744  		}
   745  	}
   746  
   747  	err = db.clearProfileShowcaseSocialLinkPreferences(tx)
   748  	if err != nil {
   749  		return err
   750  	}
   751  
   752  	for _, link := range preferences.SocialLinks {
   753  		err = db.saveProfileShowcaseSocialLinkPreference(tx, link)
   754  		if err != nil {
   755  			return err
   756  		}
   757  	}
   758  
   759  	err = db.saveProfileShowcasePreferencesClock(tx, preferences.Clock)
   760  	if err != nil {
   761  		return err
   762  	}
   763  
   764  	return nil
   765  }
   766  
   767  func (db sqlitePersistence) SaveProfileShowcaseAccountPreference(account *identity.ProfileShowcaseAccountPreference) error {
   768  	tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{})
   769  	if err != nil {
   770  		return err
   771  	}
   772  	defer func() {
   773  		if err == nil {
   774  			err = tx.Commit()
   775  			return
   776  		}
   777  		// don't shadow original error
   778  		_ = tx.Rollback()
   779  	}()
   780  	return db.saveProfileShowcaseAccountPreference(tx, account)
   781  }
   782  
   783  func (db sqlitePersistence) GetProfileShowcasePreferences() (*identity.ProfileShowcasePreferences, error) {
   784  	tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{})
   785  	if err != nil {
   786  		return nil, err
   787  	}
   788  	defer func() {
   789  		if err == nil {
   790  			err = tx.Commit()
   791  			return
   792  		}
   793  		// don't shadow original error
   794  		_ = tx.Rollback()
   795  	}()
   796  
   797  	clock, err := db.getProfileShowcasePreferencesClock(tx)
   798  	if err != nil {
   799  		return nil, err
   800  	}
   801  
   802  	communities, err := db.getProfileShowcaseCommunitiesPreferences(tx)
   803  	if err != nil {
   804  		return nil, err
   805  	}
   806  
   807  	accounts, err := db.getProfileShowcaseAccountsPreferences(tx)
   808  	if err != nil {
   809  		return nil, err
   810  	}
   811  
   812  	collectibles, err := db.getProfileShowcaseCollectiblesPreferences(tx)
   813  	if err != nil {
   814  		return nil, err
   815  	}
   816  
   817  	verifiedTokens, err := db.getProfileShowcaseVerifiedTokensPreferences(tx)
   818  	if err != nil {
   819  		return nil, err
   820  	}
   821  
   822  	unverifiedTokens, err := db.getProfileShowcaseUnverifiedTokensPreferences(tx)
   823  	if err != nil {
   824  		return nil, err
   825  	}
   826  
   827  	socialLinks, err := db.getProfileShowcaseSocialLinkPreferences(tx)
   828  	if err != nil {
   829  		return nil, err
   830  	}
   831  
   832  	return &identity.ProfileShowcasePreferences{
   833  		Clock:            clock,
   834  		Communities:      communities,
   835  		Accounts:         accounts,
   836  		Collectibles:     collectibles,
   837  		VerifiedTokens:   verifiedTokens,
   838  		UnverifiedTokens: unverifiedTokens,
   839  		SocialLinks:      socialLinks,
   840  	}, nil
   841  }
   842  
   843  func (db sqlitePersistence) SaveProfileShowcaseForContact(showcase *identity.ProfileShowcase) error {
   844  	tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{})
   845  	if err != nil {
   846  		return err
   847  	}
   848  	defer func() {
   849  		if err == nil {
   850  			err = tx.Commit()
   851  			return
   852  		}
   853  		// don't shadow original error
   854  		_ = tx.Rollback()
   855  	}()
   856  
   857  	for _, community := range showcase.Communities {
   858  		err = db.saveProfileShowcaseCommunityContact(tx, showcase.ContactID, community)
   859  		if err != nil {
   860  			return err
   861  		}
   862  	}
   863  
   864  	for _, account := range showcase.Accounts {
   865  		err = db.saveProfileShowcaseAccountContact(tx, showcase.ContactID, account)
   866  		if err != nil {
   867  			return err
   868  		}
   869  	}
   870  
   871  	for _, collectible := range showcase.Collectibles {
   872  		err = db.saveProfileShowcaseCollectibleContact(tx, showcase.ContactID, collectible)
   873  		if err != nil {
   874  			return err
   875  		}
   876  	}
   877  
   878  	for _, token := range showcase.VerifiedTokens {
   879  		err = db.saveProfileShowcaseVerifiedTokenContact(tx, showcase.ContactID, token)
   880  		if err != nil {
   881  			return err
   882  		}
   883  	}
   884  
   885  	for _, token := range showcase.UnverifiedTokens {
   886  		err = db.saveProfileShowcaseUnverifiedTokenContact(tx, showcase.ContactID, token)
   887  		if err != nil {
   888  			return err
   889  		}
   890  	}
   891  
   892  	for _, link := range showcase.SocialLinks {
   893  		err = db.saveProfileShowcaseSocialLinkContact(tx, showcase.ContactID, link)
   894  		if err != nil {
   895  			return err
   896  		}
   897  	}
   898  
   899  	return nil
   900  }
   901  
   902  func (db sqlitePersistence) GetProfileShowcaseForContact(contactID string) (*identity.ProfileShowcase, error) {
   903  	tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{})
   904  	if err != nil {
   905  		return nil, err
   906  	}
   907  	defer func() {
   908  		if err == nil {
   909  			err = tx.Commit()
   910  			return
   911  		}
   912  		// don't shadow original error
   913  		_ = tx.Rollback()
   914  	}()
   915  
   916  	communities, err := db.getProfileShowcaseCommunitiesContact(tx, contactID)
   917  	if err != nil {
   918  		return nil, err
   919  	}
   920  
   921  	accounts, err := db.getProfileShowcaseAccountsContact(tx, contactID)
   922  	if err != nil {
   923  		return nil, err
   924  	}
   925  
   926  	collectibles, err := db.getProfileShowcaseCollectiblesContact(tx, contactID)
   927  	if err != nil {
   928  		return nil, err
   929  	}
   930  
   931  	verifiedTokens, err := db.getProfileShowcaseVerifiedTokensContact(tx, contactID)
   932  	if err != nil {
   933  		return nil, err
   934  	}
   935  
   936  	unverifiedTokens, err := db.getProfileShowcaseUnverifiedTokensContact(tx, contactID)
   937  	if err != nil {
   938  		return nil, err
   939  	}
   940  
   941  	socialLinks, err := db.getProfileShowcaseSocialLinksContact(tx, contactID)
   942  	if err != nil {
   943  		return nil, err
   944  	}
   945  
   946  	return &identity.ProfileShowcase{
   947  		ContactID:        contactID,
   948  		Communities:      communities,
   949  		Accounts:         accounts,
   950  		Collectibles:     collectibles,
   951  		VerifiedTokens:   verifiedTokens,
   952  		UnverifiedTokens: unverifiedTokens,
   953  		SocialLinks:      socialLinks,
   954  	}, nil
   955  }
   956  
   957  func (db sqlitePersistence) ClearProfileShowcaseForContact(contactID string) error {
   958  	tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{})
   959  	if err != nil {
   960  		return err
   961  	}
   962  	defer func() {
   963  		if err == nil {
   964  			err = tx.Commit()
   965  			return
   966  		}
   967  		// don't shadow original error
   968  		_ = tx.Rollback()
   969  	}()
   970  
   971  	err = db.clearProfileShowcaseCommunityContact(tx, contactID)
   972  	if err != nil {
   973  		return err
   974  	}
   975  
   976  	err = db.clearProfileShowcaseAccountsContact(tx, contactID)
   977  	if err != nil {
   978  		return err
   979  	}
   980  
   981  	err = db.clearProfileShowcaseCollectiblesContact(tx, contactID)
   982  	if err != nil {
   983  		return err
   984  	}
   985  
   986  	err = db.clearProfileShowcaseVerifiedTokensContact(tx, contactID)
   987  	if err != nil {
   988  		return err
   989  	}
   990  
   991  	err = db.clearProfileShowcaseUnverifiedTokensContact(tx, contactID)
   992  	if err != nil {
   993  		return err
   994  	}
   995  
   996  	err = db.clearProfileShowcaseSocialLinksContact(tx, contactID)
   997  	if err != nil {
   998  		return err
   999  	}
  1000  
  1001  	return nil
  1002  }