github.com/status-im/status-go@v1.1.0/services/wallet/collectibles/ownership_db_test.go (about)

     1  package collectibles
     2  
     3  import (
     4  	"math/big"
     5  	"testing"
     6  
     7  	"github.com/ethereum/go-ethereum/common"
     8  
     9  	"github.com/status-im/status-go/services/wallet/bigint"
    10  	w_common "github.com/status-im/status-go/services/wallet/common"
    11  	"github.com/status-im/status-go/services/wallet/thirdparty"
    12  	"github.com/status-im/status-go/services/wallet/transfer"
    13  	"github.com/status-im/status-go/t/helpers"
    14  	"github.com/status-im/status-go/walletdatabase"
    15  
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  func setupOwnershipDBTest(t *testing.T) (*OwnershipDB, func()) {
    20  	db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
    21  	require.NoError(t, err)
    22  	return NewOwnershipDB(db), func() {
    23  		require.NoError(t, db.Close())
    24  	}
    25  }
    26  
    27  func generateTestCollectibles(offset int, count int) (result thirdparty.TokenBalancesPerContractAddress) {
    28  	result = make(thirdparty.TokenBalancesPerContractAddress)
    29  	for i := offset; i < offset+count; i++ {
    30  		contractAddress := common.BigToAddress(big.NewInt(int64(i % 10)))
    31  		tokenID := &bigint.BigInt{Int: big.NewInt(int64(i))}
    32  
    33  		result[contractAddress] = append(result[contractAddress], thirdparty.TokenBalance{
    34  			TokenID: tokenID,
    35  			Balance: &bigint.BigInt{Int: big.NewInt(int64(i%5 + 1))},
    36  		})
    37  	}
    38  	return result
    39  }
    40  
    41  func testCollectiblesToList(chainID w_common.ChainID, balances thirdparty.TokenBalancesPerContractAddress) (result []thirdparty.CollectibleUniqueID) {
    42  	result = make([]thirdparty.CollectibleUniqueID, 0, len(balances))
    43  	for contractAddress, balances := range balances {
    44  		for _, balance := range balances {
    45  			newCollectible := thirdparty.CollectibleUniqueID{
    46  				ContractID: thirdparty.ContractID{
    47  					ChainID: chainID,
    48  					Address: contractAddress,
    49  				},
    50  				TokenID: balance.TokenID,
    51  			}
    52  			result = append(result, newCollectible)
    53  		}
    54  	}
    55  	return result
    56  }
    57  
    58  func TestUpdateOwnership(t *testing.T) {
    59  	oDB, cleanDB := setupOwnershipDBTest(t)
    60  	defer cleanDB()
    61  
    62  	chainID0 := w_common.ChainID(0)
    63  	chainID1 := w_common.ChainID(1)
    64  	chainID2 := w_common.ChainID(2)
    65  
    66  	ownerAddress1 := common.HexToAddress("0x1234")
    67  	ownedBalancesChain0 := generateTestCollectibles(0, 10)
    68  	ownedListChain0 := testCollectiblesToList(chainID0, ownedBalancesChain0)
    69  	timestampChain0 := int64(1234567890)
    70  	ownedBalancesChain1 := generateTestCollectibles(0, 15)
    71  	ownedListChain1 := testCollectiblesToList(chainID1, ownedBalancesChain1)
    72  	timestampChain1 := int64(1234567891)
    73  
    74  	ownedList1 := append(ownedListChain0, ownedListChain1...)
    75  
    76  	ownerAddress2 := common.HexToAddress("0x5678")
    77  	ownedBalancesChain2 := generateTestCollectibles(0, 20)
    78  	ownedListChain2 := testCollectiblesToList(chainID2, ownedBalancesChain2)
    79  	timestampChain2 := int64(1234567892)
    80  
    81  	ownedList2 := ownedListChain2
    82  
    83  	ownerAddress3 := common.HexToAddress("0xABCD")
    84  	ownedBalancesChain1b := generateTestCollectibles(len(ownedListChain1), 5)
    85  	ownedListChain1b := testCollectiblesToList(chainID1, ownedBalancesChain1b)
    86  	timestampChain1b := timestampChain1 - 100
    87  	ownedBalancesChain2b := generateTestCollectibles(len(ownedListChain2), 20)
    88  	// Add one collectible that is already owned by ownerAddress2
    89  	commonChainID := chainID2
    90  	var commonContractAddress common.Address
    91  	var commonTokenID *bigint.BigInt
    92  	var commonBalanceAddress2 *bigint.BigInt
    93  	commonBalanceAddress3 := &bigint.BigInt{Int: big.NewInt(5)}
    94  
    95  	for contractAddress, balances := range ownedBalancesChain2 {
    96  		for _, balance := range balances {
    97  			commonContractAddress = contractAddress
    98  			commonTokenID = balance.TokenID
    99  			commonBalanceAddress2 = balance.Balance
   100  
   101  			newBalance := thirdparty.TokenBalance{
   102  				TokenID: commonTokenID,
   103  				Balance: commonBalanceAddress3,
   104  			}
   105  			ownedBalancesChain2b[commonContractAddress] = append(ownedBalancesChain2b[commonContractAddress], newBalance)
   106  			break
   107  		}
   108  		break
   109  	}
   110  
   111  	ownedListChain2b := testCollectiblesToList(chainID2, ownedBalancesChain2b)
   112  	timestampChain2b := timestampChain2 + 100
   113  
   114  	ownedList3 := append(ownedListChain1b, ownedListChain2b...)
   115  
   116  	allChains := []w_common.ChainID{chainID0, chainID1, chainID2}
   117  	allOwnerAddresses := []common.Address{ownerAddress1, ownerAddress2, ownerAddress3}
   118  	allCollectibles := append(ownedList1[1:], ownedList2...)
   119  	allCollectibles = append(allCollectibles, ownedList3[:len(ownedList3)-1]...) // the last element of ownerdList3 is a duplicate of the first element of ownedList2
   120  
   121  	randomAddress := common.HexToAddress("0xFFFF")
   122  
   123  	var err error
   124  	var removedIDs, updatedIDs, insertedIDs []thirdparty.CollectibleUniqueID
   125  
   126  	var loadedTimestamp int64
   127  	var loadedList []thirdparty.CollectibleUniqueID
   128  
   129  	loadedTimestamp, err = oDB.GetOwnershipUpdateTimestamp(ownerAddress1, chainID0)
   130  	require.NoError(t, err)
   131  	require.Equal(t, InvalidTimestamp, loadedTimestamp)
   132  
   133  	loadedTimestamp, err = oDB.GetLatestOwnershipUpdateTimestamp(chainID0)
   134  	require.NoError(t, err)
   135  	require.Equal(t, InvalidTimestamp, loadedTimestamp)
   136  
   137  	loadedTimestamp, err = oDB.GetOwnershipUpdateTimestamp(ownerAddress1, chainID1)
   138  	require.NoError(t, err)
   139  	require.Equal(t, InvalidTimestamp, loadedTimestamp)
   140  
   141  	loadedTimestamp, err = oDB.GetLatestOwnershipUpdateTimestamp(chainID1)
   142  	require.NoError(t, err)
   143  	require.Equal(t, InvalidTimestamp, loadedTimestamp)
   144  
   145  	loadedTimestamp, err = oDB.GetOwnershipUpdateTimestamp(ownerAddress2, chainID2)
   146  	require.NoError(t, err)
   147  	require.Equal(t, InvalidTimestamp, loadedTimestamp)
   148  
   149  	loadedTimestamp, err = oDB.GetLatestOwnershipUpdateTimestamp(chainID2)
   150  	require.NoError(t, err)
   151  	require.Equal(t, InvalidTimestamp, loadedTimestamp)
   152  
   153  	removedIDs, updatedIDs, insertedIDs, err = oDB.Update(chainID0, ownerAddress1, ownedBalancesChain0, timestampChain0)
   154  	require.NoError(t, err)
   155  	require.Empty(t, removedIDs)
   156  	require.Empty(t, updatedIDs)
   157  	require.ElementsMatch(t, ownedListChain0, insertedIDs)
   158  
   159  	loadedTimestamp, err = oDB.GetOwnershipUpdateTimestamp(ownerAddress1, chainID0)
   160  	require.NoError(t, err)
   161  	require.Equal(t, timestampChain0, loadedTimestamp)
   162  
   163  	loadedTimestamp, err = oDB.GetLatestOwnershipUpdateTimestamp(chainID0)
   164  	require.NoError(t, err)
   165  	require.Equal(t, timestampChain0, loadedTimestamp)
   166  
   167  	loadedTimestamp, err = oDB.GetOwnershipUpdateTimestamp(ownerAddress1, chainID1)
   168  	require.NoError(t, err)
   169  	require.Equal(t, InvalidTimestamp, loadedTimestamp)
   170  
   171  	loadedTimestamp, err = oDB.GetLatestOwnershipUpdateTimestamp(chainID1)
   172  	require.NoError(t, err)
   173  	require.Equal(t, InvalidTimestamp, loadedTimestamp)
   174  
   175  	loadedTimestamp, err = oDB.GetOwnershipUpdateTimestamp(ownerAddress2, chainID2)
   176  	require.NoError(t, err)
   177  	require.Equal(t, InvalidTimestamp, loadedTimestamp)
   178  
   179  	loadedTimestamp, err = oDB.GetLatestOwnershipUpdateTimestamp(chainID2)
   180  	require.NoError(t, err)
   181  	require.Equal(t, InvalidTimestamp, loadedTimestamp)
   182  
   183  	removedIDs, updatedIDs, insertedIDs, err = oDB.Update(chainID1, ownerAddress1, ownedBalancesChain1, timestampChain1)
   184  	require.NoError(t, err)
   185  	require.Empty(t, removedIDs)
   186  	require.Empty(t, updatedIDs)
   187  	require.ElementsMatch(t, ownedListChain1, insertedIDs)
   188  
   189  	removedIDs, updatedIDs, insertedIDs, err = oDB.Update(chainID2, ownerAddress2, ownedBalancesChain2, timestampChain2)
   190  	require.NoError(t, err)
   191  	require.Empty(t, removedIDs)
   192  	require.Empty(t, updatedIDs)
   193  	require.ElementsMatch(t, ownedListChain2, insertedIDs)
   194  
   195  	removedIDs, updatedIDs, insertedIDs, err = oDB.Update(chainID1, ownerAddress3, ownedBalancesChain1b, timestampChain1b)
   196  	require.NoError(t, err)
   197  	require.Empty(t, removedIDs)
   198  	require.Empty(t, updatedIDs)
   199  	require.ElementsMatch(t, ownedListChain1b, insertedIDs)
   200  
   201  	removedIDs, updatedIDs, insertedIDs, err = oDB.Update(chainID2, ownerAddress3, ownedBalancesChain2b, timestampChain2b)
   202  	require.NoError(t, err)
   203  	require.Empty(t, removedIDs)
   204  	require.Empty(t, updatedIDs)
   205  	require.ElementsMatch(t, ownedListChain2b, insertedIDs)
   206  
   207  	loadedTimestamp, err = oDB.GetOwnershipUpdateTimestamp(ownerAddress1, chainID0)
   208  	require.NoError(t, err)
   209  	require.Equal(t, timestampChain0, loadedTimestamp)
   210  
   211  	loadedTimestamp, err = oDB.GetLatestOwnershipUpdateTimestamp(chainID0)
   212  	require.NoError(t, err)
   213  	require.Equal(t, timestampChain0, loadedTimestamp)
   214  
   215  	loadedTimestamp, err = oDB.GetOwnershipUpdateTimestamp(ownerAddress1, chainID1)
   216  	require.NoError(t, err)
   217  	require.Equal(t, timestampChain1, loadedTimestamp)
   218  
   219  	loadedTimestamp, err = oDB.GetOwnershipUpdateTimestamp(ownerAddress3, chainID1)
   220  	require.NoError(t, err)
   221  	require.Equal(t, timestampChain1b, loadedTimestamp)
   222  
   223  	loadedTimestamp, err = oDB.GetLatestOwnershipUpdateTimestamp(chainID1)
   224  	require.NoError(t, err)
   225  	require.Equal(t, timestampChain1, loadedTimestamp)
   226  
   227  	loadedTimestamp, err = oDB.GetOwnershipUpdateTimestamp(ownerAddress2, chainID2)
   228  	require.NoError(t, err)
   229  	require.Equal(t, timestampChain2, loadedTimestamp)
   230  
   231  	loadedTimestamp, err = oDB.GetOwnershipUpdateTimestamp(ownerAddress3, chainID2)
   232  	require.NoError(t, err)
   233  	require.Equal(t, timestampChain2b, loadedTimestamp)
   234  
   235  	loadedTimestamp, err = oDB.GetLatestOwnershipUpdateTimestamp(chainID2)
   236  	require.NoError(t, err)
   237  	require.Equal(t, timestampChain2b, loadedTimestamp)
   238  
   239  	loadedList, err = oDB.GetOwnedCollectibles([]w_common.ChainID{chainID0}, []common.Address{ownerAddress1}, 0, len(allCollectibles))
   240  	require.NoError(t, err)
   241  	require.ElementsMatch(t, ownedListChain0, loadedList)
   242  
   243  	loadedList, err = oDB.GetOwnedCollectibles([]w_common.ChainID{chainID0, chainID1}, []common.Address{ownerAddress1, randomAddress}, 0, len(allCollectibles))
   244  	require.NoError(t, err)
   245  	require.ElementsMatch(t, ownedList1, loadedList)
   246  
   247  	loadedList, err = oDB.GetOwnedCollectibles([]w_common.ChainID{chainID2}, []common.Address{ownerAddress2}, 0, len(allCollectibles))
   248  	require.NoError(t, err)
   249  	require.ElementsMatch(t, ownedList2, loadedList)
   250  
   251  	loadedList, err = oDB.GetOwnedCollectibles(allChains, allOwnerAddresses, 0, len(allCollectibles))
   252  	require.NoError(t, err)
   253  	require.Equal(t, len(allCollectibles), len(loadedList))
   254  
   255  	loadedList, err = oDB.GetOwnedCollectibles([]w_common.ChainID{chainID0}, []common.Address{randomAddress}, 0, len(allCollectibles))
   256  	require.NoError(t, err)
   257  	require.Empty(t, loadedList)
   258  
   259  	// Test GetOwnership for common token
   260  	commonID := thirdparty.CollectibleUniqueID{
   261  		ContractID: thirdparty.ContractID{
   262  			ChainID: commonChainID,
   263  			Address: commonContractAddress,
   264  		},
   265  		TokenID: commonTokenID,
   266  	}
   267  	loadedOwnership, err := oDB.GetOwnership(commonID)
   268  	require.NoError(t, err)
   269  
   270  	expectedOwnership := []thirdparty.AccountBalance{
   271  		{
   272  			Address:     ownerAddress2,
   273  			Balance:     commonBalanceAddress2,
   274  			TxTimestamp: InvalidTimestamp,
   275  		},
   276  		{
   277  			Address:     ownerAddress3,
   278  			Balance:     commonBalanceAddress3,
   279  			TxTimestamp: InvalidTimestamp,
   280  		},
   281  	}
   282  
   283  	require.ElementsMatch(t, expectedOwnership, loadedOwnership)
   284  
   285  	// Test GetOwnership for random token
   286  	randomID := thirdparty.CollectibleUniqueID{
   287  		ContractID: thirdparty.ContractID{
   288  			ChainID: 0xABCDEF,
   289  			Address: common.BigToAddress(big.NewInt(int64(123456789))),
   290  		},
   291  		TokenID: &bigint.BigInt{Int: big.NewInt(int64(987654321))},
   292  	}
   293  
   294  	loadedOwnership, err = oDB.GetOwnership(randomID)
   295  	require.NoError(t, err)
   296  	require.Empty(t, loadedOwnership)
   297  }
   298  
   299  func TestUpdateOwnershipChanges(t *testing.T) {
   300  	oDB, cleanDB := setupOwnershipDBTest(t)
   301  	defer cleanDB()
   302  
   303  	chainID0 := w_common.ChainID(0)
   304  	ownerAddress1 := common.HexToAddress("0x1234")
   305  	ownedBalancesChain0 := generateTestCollectibles(0, 10)
   306  	ownedListChain0 := testCollectiblesToList(chainID0, ownedBalancesChain0)
   307  	timestampChain0 := int64(1234567890)
   308  
   309  	var err error
   310  	var removedIDs, updatedIDs, insertedIDs []thirdparty.CollectibleUniqueID
   311  
   312  	var loadedList []thirdparty.CollectibleUniqueID
   313  
   314  	removedIDs, updatedIDs, insertedIDs, err = oDB.Update(chainID0, ownerAddress1, ownedBalancesChain0, timestampChain0)
   315  	require.NoError(t, err)
   316  	require.Empty(t, removedIDs)
   317  	require.Empty(t, updatedIDs)
   318  	require.ElementsMatch(t, ownedListChain0, insertedIDs)
   319  
   320  	loadedList, err = oDB.GetOwnedCollectibles([]w_common.ChainID{chainID0}, []common.Address{ownerAddress1}, 0, len(ownedListChain0))
   321  	require.NoError(t, err)
   322  	require.ElementsMatch(t, ownedListChain0, loadedList)
   323  
   324  	// Remove one collectible and change balance of another
   325  	var removedID, updatedID thirdparty.CollectibleUniqueID
   326  
   327  	count := 0
   328  	for contractAddress, balances := range ownedBalancesChain0 {
   329  		for i, balance := range balances {
   330  			if count == 0 {
   331  				count++
   332  				ownedBalancesChain0[contractAddress] = ownedBalancesChain0[contractAddress][1:]
   333  				removedID = thirdparty.CollectibleUniqueID{
   334  					ContractID: thirdparty.ContractID{
   335  						ChainID: chainID0,
   336  						Address: contractAddress,
   337  					},
   338  					TokenID: balance.TokenID,
   339  				}
   340  			} else if count == 1 {
   341  				count++
   342  				ownedBalancesChain0[contractAddress][i].Balance = &bigint.BigInt{Int: big.NewInt(100)}
   343  				updatedID = thirdparty.CollectibleUniqueID{
   344  					ContractID: thirdparty.ContractID{
   345  						ChainID: chainID0,
   346  						Address: contractAddress,
   347  					},
   348  					TokenID: balance.TokenID,
   349  				}
   350  			}
   351  		}
   352  	}
   353  	ownedListChain0 = testCollectiblesToList(chainID0, ownedBalancesChain0)
   354  
   355  	removedIDs, updatedIDs, insertedIDs, err = oDB.Update(chainID0, ownerAddress1, ownedBalancesChain0, timestampChain0)
   356  	require.NoError(t, err)
   357  	require.ElementsMatch(t, []thirdparty.CollectibleUniqueID{removedID}, removedIDs)
   358  	require.ElementsMatch(t, []thirdparty.CollectibleUniqueID{updatedID}, updatedIDs)
   359  	require.Empty(t, insertedIDs)
   360  
   361  	loadedList, err = oDB.GetOwnedCollectibles([]w_common.ChainID{chainID0}, []common.Address{ownerAddress1}, 0, len(ownedListChain0))
   362  	require.NoError(t, err)
   363  	require.ElementsMatch(t, ownedListChain0, loadedList)
   364  }
   365  
   366  func TestLargeTokenID(t *testing.T) {
   367  	oDB, cleanDB := setupOwnershipDBTest(t)
   368  	defer cleanDB()
   369  
   370  	ownerAddress := common.HexToAddress("0xABCD")
   371  	chainID := w_common.ChainID(0)
   372  	contractAddress := common.HexToAddress("0x1234")
   373  	tokenID := &bigint.BigInt{Int: big.NewInt(0).SetBytes([]byte("0x1234567890123456789012345678901234567890"))}
   374  	balance := &bigint.BigInt{Int: big.NewInt(100)}
   375  
   376  	ownedBalancesChain := thirdparty.TokenBalancesPerContractAddress{
   377  		contractAddress: []thirdparty.TokenBalance{
   378  			{
   379  				TokenID: tokenID,
   380  				Balance: balance,
   381  			},
   382  		},
   383  	}
   384  	ownedListChain := testCollectiblesToList(chainID, ownedBalancesChain)
   385  
   386  	ownership := []thirdparty.AccountBalance{
   387  		{
   388  			Address:     ownerAddress,
   389  			Balance:     balance,
   390  			TxTimestamp: InvalidTimestamp,
   391  		},
   392  	}
   393  
   394  	timestamp := int64(1234567890)
   395  
   396  	var err error
   397  
   398  	_, _, _, err = oDB.Update(chainID, ownerAddress, ownedBalancesChain, timestamp)
   399  	require.NoError(t, err)
   400  
   401  	loadedList, err := oDB.GetOwnedCollectibles([]w_common.ChainID{chainID}, []common.Address{ownerAddress}, 0, len(ownedListChain))
   402  	require.NoError(t, err)
   403  	require.Equal(t, ownedListChain, loadedList)
   404  
   405  	// Test GetOwnership
   406  	id := thirdparty.CollectibleUniqueID{
   407  		ContractID: thirdparty.ContractID{
   408  			ChainID: chainID,
   409  			Address: contractAddress,
   410  		},
   411  		TokenID: tokenID,
   412  	}
   413  	loadedOwnership, err := oDB.GetOwnership(id)
   414  	require.NoError(t, err)
   415  	require.Equal(t, ownership, loadedOwnership)
   416  }
   417  
   418  func TestCollectibleTransferID(t *testing.T) {
   419  	oDB, cleanDB := setupOwnershipDBTest(t)
   420  	defer cleanDB()
   421  
   422  	chainID0 := w_common.ChainID(0)
   423  	ownerAddress1 := common.HexToAddress("0x1234")
   424  	ownedBalancesChain0 := generateTestCollectibles(0, 10)
   425  	ownedListChain0 := testCollectiblesToList(chainID0, ownedBalancesChain0)
   426  	timestampChain0 := int64(1234567890)
   427  
   428  	var err error
   429  	var changed bool
   430  
   431  	_, _, _, err = oDB.Update(chainID0, ownerAddress1, ownedBalancesChain0, timestampChain0)
   432  	require.NoError(t, err)
   433  
   434  	loadedList, err := oDB.GetCollectiblesWithNoTransferID(ownerAddress1, chainID0)
   435  	require.NoError(t, err)
   436  	require.ElementsMatch(t, ownedListChain0, loadedList)
   437  
   438  	for _, id := range ownedListChain0 {
   439  		loadedTransferID, err := oDB.GetTransferID(ownerAddress1, id)
   440  		require.NoError(t, err)
   441  		require.Nil(t, loadedTransferID)
   442  	}
   443  
   444  	randomAddress := common.HexToAddress("0xFFFF")
   445  	randomCollectibleID := thirdparty.CollectibleUniqueID{
   446  		ContractID: thirdparty.ContractID{
   447  			ChainID: 0xABCDEF,
   448  			Address: common.BigToAddress(big.NewInt(int64(123456789))),
   449  		},
   450  		TokenID: &bigint.BigInt{Int: big.NewInt(int64(987654321))},
   451  	}
   452  	randomTxID := common.HexToHash("0xEEEE")
   453  	changed, err = oDB.SetTransferID(randomAddress, randomCollectibleID, randomTxID)
   454  	require.NoError(t, err)
   455  	require.False(t, changed)
   456  
   457  	firstCollectibleID := ownedListChain0[0]
   458  	firstTxID := common.HexToHash("0x1234")
   459  	changed, err = oDB.SetTransferID(ownerAddress1, firstCollectibleID, firstTxID)
   460  	require.NoError(t, err)
   461  	require.True(t, changed)
   462  
   463  	for _, id := range ownedListChain0 {
   464  		loadedTransferID, err := oDB.GetTransferID(ownerAddress1, id)
   465  		require.NoError(t, err)
   466  		if id == firstCollectibleID {
   467  			require.Equal(t, firstTxID, *loadedTransferID)
   468  		} else {
   469  			require.Nil(t, loadedTransferID)
   470  		}
   471  	}
   472  
   473  	// Even though the first collectible has a TransferID set, since there's no matching entry in the transfers table it
   474  	// should return InvalidTimestamp
   475  	firstOwnership, err := oDB.GetOwnership(firstCollectibleID)
   476  	require.NoError(t, err)
   477  	require.Equal(t, InvalidTimestamp, firstOwnership[0].TxTimestamp)
   478  
   479  	trs, _, _ := transfer.GenerateTestTransfers(t, oDB.db, 1, 5)
   480  	trs[0].To = ownerAddress1
   481  	trs[0].ChainID = chainID0
   482  	trs[0].Hash = firstTxID
   483  
   484  	for i := range trs {
   485  		if i == 0 {
   486  			transfer.InsertTestTransferWithOptions(t, oDB.db, trs[i].To, &trs[i], &transfer.TestTransferOptions{
   487  				TokenAddress: firstCollectibleID.ContractID.Address,
   488  				TokenID:      firstCollectibleID.TokenID.Int,
   489  			})
   490  		} else {
   491  			transfer.InsertTestTransfer(t, oDB.db, trs[i].To, &trs[i])
   492  		}
   493  	}
   494  
   495  	// There should now be a valid timestamp
   496  	firstOwnership, err = oDB.GetOwnership(firstCollectibleID)
   497  	require.NoError(t, err)
   498  	require.Equal(t, trs[0].Timestamp, firstOwnership[0].TxTimestamp)
   499  }