Updated on 2026-08-14

This commit is contained in:
Tangem 2026-06-30 13:34:09 +04:00
parent 0eae1c84fa
commit 066ab573b3
19 changed files with 784 additions and 113 deletions

View file

@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "55f2651d215126dd0465b9c711165cba",
"identityHash": "5d37870ed1a5b37c62e9836dc3fd061c",
"entities": [
{
"tableName": "express_provider",
@ -510,7 +510,7 @@
"notNull": true
},
{
"fieldPath": "onrampAvailable",
"fieldPath": "isOnrampAvailable",
"columnName": "onramp_available",
"affinity": "INTEGER",
"notNull": true
@ -551,11 +551,66 @@
"code"
]
}
},
{
"tableName": "token_info",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`network_id` TEXT NOT NULL, `contract_address` TEXT NOT NULL, `coin_id` TEXT NOT NULL, `name` TEXT NOT NULL, `symbol` TEXT NOT NULL, `decimals` INTEGER NOT NULL, `updated_at` INTEGER NOT NULL, PRIMARY KEY(`network_id`, `contract_address`))",
"fields": [
{
"fieldPath": "networkId",
"columnName": "network_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "contractAddress",
"columnName": "contract_address",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "coinId",
"columnName": "coin_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "symbol",
"columnName": "symbol",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "decimals",
"columnName": "decimals",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "updatedAt",
"columnName": "updated_at",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"network_id",
"contract_address"
]
}
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '55f2651d215126dd0465b9c711165cba')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5d37870ed1a5b37c62e9836dc3fd061c')"
]
}
}

View file

@ -5,6 +5,7 @@ import androidx.room.Room
import com.tangem.datasource.local.txhistory.db.TxHistoryDatabase
import com.tangem.datasource.local.txhistory.db.dao.ExpressHistoryDao
import com.tangem.datasource.local.txhistory.db.dao.ExpressSyncStateDao
import com.tangem.datasource.local.txhistory.db.dao.TokenInfoDao
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -37,5 +38,8 @@ internal interface TxHistoryModule {
@Provides
fun provideSyncStateDao(database: TxHistoryDatabase): ExpressSyncStateDao = database.syncStateDao()
@Provides
fun provideTokenInfoDao(database: TxHistoryDatabase): TokenInfoDao = database.tokenInfoDao()
}
}

View file

@ -4,11 +4,13 @@ import androidx.room.Database
import androidx.room.RoomDatabase
import com.tangem.datasource.local.txhistory.db.dao.ExpressHistoryDao
import com.tangem.datasource.local.txhistory.db.dao.ExpressSyncStateDao
import com.tangem.datasource.local.txhistory.db.dao.TokenInfoDao
import com.tangem.datasource.local.txhistory.db.entity.express.ExpressSyncStateEntity
import com.tangem.datasource.local.txhistory.db.entity.express.ExpressExchangeEntity
import com.tangem.datasource.local.txhistory.db.entity.express.ExpressOnrampEntity
import com.tangem.datasource.local.txhistory.db.entity.express.ExpressProviderEntity
import com.tangem.datasource.local.txhistory.db.entity.express.OnrampCountryEntity
import com.tangem.datasource.local.txhistory.db.entity.express.TokenInfoEntity
@Database(
version = 1,
@ -18,6 +20,7 @@ import com.tangem.datasource.local.txhistory.db.entity.express.OnrampCountryEnti
ExpressOnrampEntity::class,
ExpressSyncStateEntity::class,
OnrampCountryEntity::class,
TokenInfoEntity::class,
],
)
abstract class TxHistoryDatabase : RoomDatabase() {
@ -25,4 +28,6 @@ abstract class TxHistoryDatabase : RoomDatabase() {
abstract fun expressHistoryDao(): ExpressHistoryDao
abstract fun syncStateDao(): ExpressSyncStateDao
abstract fun tokenInfoDao(): TokenInfoDao
}

View file

@ -0,0 +1,33 @@
package com.tangem.datasource.local.txhistory.db.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.tangem.datasource.local.txhistory.db.entity.express.TokenInfoEntity
@Dao
interface TokenInfoDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun upsert(items: List<TokenInfoEntity>)
/**
* Cached tokens for the given networks/contracts. Filters each column independently, so the result is a
* cross-product superset of the `(networkId, contractAddress)` pairs the caller must match exact pairs.
* Contract match is case-insensitive; pass [minUpdatedAt] = `now - ttl` to drop stale rows.
*/
@Query(
"""
SELECT * FROM token_info
WHERE network_id IN (:networkIds)
AND contract_address COLLATE NOCASE IN (:contractAddresses)
AND updated_at >= :minUpdatedAt
""",
)
suspend fun getCached(
networkIds: Collection<String>,
contractAddresses: Collection<String>,
minUpdatedAt: Long = 0,
): List<TokenInfoEntity>
}

View file

@ -0,0 +1,37 @@
package com.tangem.datasource.local.txhistory.db.entity.express
import androidx.room.ColumnInfo
import androidx.room.Entity
/**
* Cached token data fetched from [TangemTechApi.getCoins], keyed by network id + contract address.
*/
@Entity(
tableName = "token_info",
primaryKeys = ["network_id", "contract_address"],
)
data class TokenInfoEntity(
@ColumnInfo(name = "network_id")
val networkId: String,
@ColumnInfo(name = "contract_address")
val contractAddress: String,
/** Coin id from the backend (used as the token's raw currency id and for the icon URL). */
@ColumnInfo(name = "coin_id")
val coinId: String,
@ColumnInfo(name = "name")
val name: String,
@ColumnInfo(name = "symbol")
val symbol: String,
@ColumnInfo(name = "decimals")
val decimals: Int,
/** Last refresh timestamp in epoch milliseconds, used to evict stale entries. */
@ColumnInfo(name = "updated_at")
val updatedAt: Long,
)