mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-04-22 02:07:58 +00:00
Initial commit: Pezkuwi Wallet Android
Security hardened release: - Code obfuscation enabled (minifyEnabled=true, shrinkResources=true) - Sensitive files excluded (google-services.json, keystores) - Branch.io key moved to BuildConfig placeholder - Updated dependencies: OkHttp 4.12.0, Gson 2.10.1, BouncyCastle 1.77 - Comprehensive ProGuard rules for crypto wallet - Navigation 2.7.7, Lifecycle 2.7.0, ConstraintLayout 2.1.4
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
android {
|
||||
|
||||
defaultConfig {
|
||||
|
||||
|
||||
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
arguments += ["room.schemaLocation":
|
||||
"$projectDir/schemas".toString()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
|
||||
}
|
||||
namespace 'io.novafoundation.nova.core_db'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
implementation substrateSdkDep
|
||||
|
||||
implementation project(":common")
|
||||
implementation gsonDep
|
||||
|
||||
implementation kotlinDep
|
||||
|
||||
implementation coroutinesDep
|
||||
|
||||
implementation daggerDep
|
||||
ksp daggerCompiler
|
||||
|
||||
implementation roomDep
|
||||
implementation roomKtxDep
|
||||
ksp roomCompiler
|
||||
|
||||
androidTestImplementation androidTestRunnerDep
|
||||
androidTestImplementation androidTestRulesDep
|
||||
androidTestImplementation androidJunitDep
|
||||
|
||||
androidTestImplementation roomTestsDep
|
||||
testImplementation project(':test-shared')
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,106 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.novafoundation.nova.core_db.AppDatabase
|
||||
import io.novafoundation.nova.core_db.model.AssetLocal
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AssetsDaoTest : DaoTest<AssetDao>(AppDatabase::assetDao) {
|
||||
|
||||
private val chainDao by dao<ChainDao>()
|
||||
private val metaAccountDao by dao<MetaAccountDao>()
|
||||
private val currencyDao by dao<CurrencyDao>()
|
||||
private val assetDao by dao<AssetDao>()
|
||||
|
||||
private var metaId: Long = 0
|
||||
|
||||
private val chainId = "0"
|
||||
private val testChain = createTestChain(chainId)
|
||||
private val asset = testChain.assets.first()
|
||||
private val assetId = asset.id
|
||||
|
||||
@Before
|
||||
fun setupDb() = runBlocking {
|
||||
metaId = metaAccountDao.insertMetaAccount(testMetaAccount())
|
||||
chainDao.addChain(testChain)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldDeleteAssetAfterChainIsDeleted() = runBlocking {
|
||||
dao.insertAsset(AssetLocal.createEmpty(assetId = assetId, chainId = chainId, metaId))
|
||||
chainDao.removeChain(testChain)
|
||||
|
||||
val assets = dao.getSupportedAssets(metaId)
|
||||
|
||||
assert(assets.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRetrievingAssetsByMetaId() = runBlocking {
|
||||
currencyDao.insert(createCurrency(selected = true))
|
||||
|
||||
val assetWithToken = dao.getAssetWithToken(metaId, chainId, assetId)
|
||||
|
||||
assert(assetWithToken != null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRetrievingAssetsByMetaIdWithoutCurrency() = runBlocking {
|
||||
currencyDao.insert(createCurrency(selected = false))
|
||||
|
||||
val assetWithToken = dao.getAssetWithToken(metaId, chainId, assetId)
|
||||
|
||||
assert(assetWithToken == null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRetrievingSyncedAssets() = runBlocking {
|
||||
assetDao.insertAsset(AssetLocal.createEmpty(assetId, chainId, metaId))
|
||||
currencyDao.insert(createCurrency(selected = true))
|
||||
|
||||
val assetWithToken = dao.getSyncedAssets(metaId)
|
||||
|
||||
assert(assetWithToken.isNotEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRetrievingSyncedAssetsWithoutCurrency() = runBlocking {
|
||||
assetDao.insertAsset(AssetLocal.createEmpty(assetId, chainId, metaId))
|
||||
currencyDao.insert(createCurrency(selected = false))
|
||||
|
||||
val assetsWithTokens = dao.getSyncedAssets(metaId)
|
||||
|
||||
assert(assetsWithTokens.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRetrievingSyncedAssetsWithoutAssetBalance() = runBlocking {
|
||||
currencyDao.insert(createCurrency(selected = false))
|
||||
|
||||
val assetsWithTokens = dao.getSyncedAssets(metaId)
|
||||
|
||||
assert(assetsWithTokens.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRetrievingSupportedAssets() = runBlocking {
|
||||
currencyDao.insert(createCurrency(selected = true))
|
||||
|
||||
val assetsWithTokens = dao.getSupportedAssets(metaId)
|
||||
|
||||
assert(assetsWithTokens.isNotEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRetrievingSupportedAssetsWithoutCurrency() = runBlocking {
|
||||
currencyDao.insert(createCurrency(selected = false))
|
||||
|
||||
val assetsWithTokens = dao.getSupportedAssets(metaId)
|
||||
|
||||
assert(assetsWithTokens.isEmpty())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.novafoundation.nova.common.utils.CollectionDiffer
|
||||
import io.novafoundation.nova.core_db.AppDatabase
|
||||
import io.novafoundation.nova.core_db.model.chain.JoinedChainInfo
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ChainDaoTest : DaoTest<ChainDao>(AppDatabase::chainDao) {
|
||||
|
||||
@Test
|
||||
fun shouldInsertWholeChain() = runBlocking {
|
||||
val chainInfo = createTestChain("0x00")
|
||||
|
||||
dao.addChain(chainInfo)
|
||||
|
||||
val chainsFromDb = dao.getJoinChainInfo()
|
||||
|
||||
assertEquals(1, chainsFromDb.size)
|
||||
|
||||
val chainFromDb = chainsFromDb.first()
|
||||
|
||||
assertEquals(chainInfo.assets.size, chainFromDb.assets.size)
|
||||
assertEquals(chainInfo.nodes.size, chainFromDb.nodes.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldDeleteChainWithCascade() = runBlocking {
|
||||
val chainInfo = createTestChain("0x00")
|
||||
|
||||
dao.addChain(chainInfo)
|
||||
dao.removeChain(chainInfo)
|
||||
|
||||
val assetsCursor = db.query("SELECT * FROM chain_assets", emptyArray())
|
||||
assertEquals(0, assetsCursor.count)
|
||||
|
||||
val nodesCursor = db.query("SELECT * FROM chain_nodes", emptyArray())
|
||||
assertEquals(0, nodesCursor.count)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldNotDeleteRuntimeCacheEntryAfterChainUpdate() = runBlocking {
|
||||
val chainInfo = createTestChain("0x00")
|
||||
|
||||
dao.addChain(chainInfo)
|
||||
dao.updateRemoteRuntimeVersionIfChainExists(chainInfo.chain.id, runtimeVersion = 1, transactionVersion = 1)
|
||||
|
||||
dao.updateChain(chainInfo)
|
||||
|
||||
val runtimeEntry = dao.runtimeInfo(chainInfo.chain.id)
|
||||
|
||||
assertNotNull(runtimeEntry)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldDeleteRemovedNestedFields() = runBlocking {
|
||||
val chainInfo = createTestChain("0x00", nodesCount = 3, assetsCount = 3)
|
||||
|
||||
dao.addChain(chainInfo)
|
||||
|
||||
dao.applyDiff(
|
||||
chainDiff = updatedDiff(chainInfo.chain),
|
||||
assetsDiff = CollectionDiffer.Diff(
|
||||
added = emptyList(),
|
||||
updated = emptyList(),
|
||||
removed = chainInfo.assets.takeLast(1),
|
||||
all = chainInfo.assets
|
||||
),
|
||||
nodesDiff = CollectionDiffer.Diff(
|
||||
added = emptyList(),
|
||||
updated = emptyList(),
|
||||
removed = chainInfo.nodes.takeLast(1),
|
||||
all = chainInfo.nodes
|
||||
),
|
||||
explorersDiff = emptyDiff(),
|
||||
externalApisDiff = emptyDiff(),
|
||||
nodeSelectionPreferencesDiff = emptyDiff()
|
||||
)
|
||||
|
||||
val chainFromDb2 = dao.getJoinChainInfo().first()
|
||||
|
||||
assertEquals(2, chainFromDb2.nodes.size)
|
||||
assertEquals(2, chainFromDb2.assets.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldUpdate() = runBlocking {
|
||||
val toBeRemoved = listOf(
|
||||
createTestChain("to be removed 1"),
|
||||
createTestChain("to be removed 2"),
|
||||
)
|
||||
|
||||
val stayTheSame = listOf(
|
||||
createTestChain("stay the same")
|
||||
)
|
||||
|
||||
val chainsInitial = listOf(createTestChain("to be changed")) + stayTheSame + toBeRemoved
|
||||
|
||||
dao.addChains(chainsInitial)
|
||||
|
||||
val added = listOf(createTestChain("to be added"))
|
||||
val updated = listOf(createTestChain("to be changed", "new name"))
|
||||
|
||||
val expectedResult = stayTheSame + added + updated
|
||||
|
||||
dao.applyDiff(
|
||||
chainDiff = CollectionDiffer.Diff(
|
||||
added = added.map(JoinedChainInfo::chain),
|
||||
updated = updated.map(JoinedChainInfo::chain),
|
||||
removed = toBeRemoved.map(JoinedChainInfo::chain),
|
||||
all = emptyList()
|
||||
),
|
||||
assetsDiff = emptyDiff(),
|
||||
nodesDiff = emptyDiff(),
|
||||
explorersDiff = emptyDiff(),
|
||||
externalApisDiff = emptyDiff(),
|
||||
nodeSelectionPreferencesDiff = emptyDiff()
|
||||
)
|
||||
|
||||
val chainsFromDb = dao.getJoinChainInfo()
|
||||
|
||||
assertEquals(expectedResult.size, chainsFromDb.size)
|
||||
expectedResult.forEach { expected ->
|
||||
val tryFind = chainsFromDb.firstOrNull { actual -> expected.chain.id == actual.chain.id && expected.chain.name == actual.chain.name }
|
||||
|
||||
assertNotNull("Did not find ${expected.chain.id} in result set", tryFind)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldUpdateRuntimeVersions() {
|
||||
runBlocking {
|
||||
val chainId = "0x00"
|
||||
|
||||
dao.addChain(createTestChain(chainId))
|
||||
|
||||
dao.updateRemoteRuntimeVersionIfChainExists(chainId, 1, transactionVersion = 1)
|
||||
|
||||
checkRuntimeVersions(remote = 1, synced = 0)
|
||||
|
||||
dao.updateSyncedRuntimeVersion(chainId, 1, localMigratorVersion = 1)
|
||||
|
||||
checkRuntimeVersions(remote = 1, synced = 1)
|
||||
|
||||
dao.updateRemoteRuntimeVersionIfChainExists(chainId, 2, transactionVersion = 1)
|
||||
|
||||
checkRuntimeVersions(remote = 2, synced = 1)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun checkRuntimeVersions(remote: Int, synced: Int) {
|
||||
val runtimeInfo = dao.runtimeInfo("0x00")
|
||||
|
||||
requireNotNull(runtimeInfo)
|
||||
|
||||
assertEquals(runtimeInfo.remoteVersion, remote)
|
||||
assertEquals(runtimeInfo.syncedVersion, synced)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Room
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import io.novafoundation.nova.core_db.AppDatabase
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import java.io.IOException
|
||||
|
||||
abstract class DaoTest<D : Any>(private val daoFetcher: (AppDatabase) -> D) {
|
||||
protected lateinit var dao: D
|
||||
protected lateinit var db: AppDatabase
|
||||
|
||||
@Before
|
||||
fun createDb() {
|
||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||
|
||||
db = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
|
||||
.build()
|
||||
|
||||
dao = daoFetcher(db)
|
||||
}
|
||||
|
||||
@After
|
||||
@Throws(IOException::class)
|
||||
fun closeDb() {
|
||||
db.close()
|
||||
}
|
||||
|
||||
protected inline fun <reified T> dao(): Lazy<T> = lazy {
|
||||
val method = db.javaClass.declaredMethods.first { it.returnType == T::class.java }
|
||||
|
||||
method.invoke(db) as T
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import io.novafoundation.nova.common.utils.CollectionDiffer
|
||||
import io.novafoundation.nova.core.model.CryptoType
|
||||
import io.novafoundation.nova.core_db.model.CurrencyLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.AssetSourceLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainAssetLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainExplorerLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainExternalApiLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainNodeLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.JoinedChainInfo
|
||||
import io.novafoundation.nova.core_db.model.chain.NodeSelectionPreferencesLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.account.ChainAccountLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.account.MetaAccountLocal
|
||||
|
||||
fun createTestChain(
|
||||
id: String,
|
||||
name: String = id,
|
||||
nodesCount: Int = 3,
|
||||
assetsCount: Int = 2,
|
||||
): JoinedChainInfo {
|
||||
val chain = chainOf(id, name)
|
||||
val nodes = with(chain) {
|
||||
(1..nodesCount).map {
|
||||
nodeOf("link${it}")
|
||||
}
|
||||
}
|
||||
val assets = with(chain) {
|
||||
(1..assetsCount).map {
|
||||
assetOf(assetId = it, symbol = it.toString())
|
||||
}
|
||||
}
|
||||
val explorers = emptyList<ChainExplorerLocal>()
|
||||
val externalApis = emptyList<ChainExternalApiLocal>()
|
||||
|
||||
return JoinedChainInfo(
|
||||
chain,
|
||||
NodeSelectionPreferencesLocal(chain.id, autoBalanceEnabled = true, selectedNodeUrl = null),
|
||||
nodes,
|
||||
assets,
|
||||
explorers,
|
||||
externalApis
|
||||
)
|
||||
}
|
||||
|
||||
fun chainOf(
|
||||
id: String,
|
||||
name: String = id,
|
||||
) = ChainLocal(
|
||||
id = id,
|
||||
parentId = null,
|
||||
name = name,
|
||||
icon = "Test",
|
||||
types = null,
|
||||
prefix = 0,
|
||||
legacyPrefix = null,
|
||||
isTestNet = false,
|
||||
isEthereumBased = false,
|
||||
hasCrowdloans = false,
|
||||
additional = "",
|
||||
governance = "governance",
|
||||
connectionState = ChainLocal.ConnectionStateLocal.FULL_SYNC,
|
||||
pushSupport = true,
|
||||
supportProxy = false,
|
||||
swap = "",
|
||||
hasSubstrateRuntime = true,
|
||||
nodeSelectionStrategy = ChainLocal.AutoBalanceStrategyLocal.ROUND_ROBIN,
|
||||
source = ChainLocal.Source.CUSTOM,
|
||||
customFee = "",
|
||||
multisigSupport = true
|
||||
)
|
||||
|
||||
fun ChainLocal.nodeOf(
|
||||
link: String,
|
||||
) = ChainNodeLocal(
|
||||
name = "Test",
|
||||
url = link,
|
||||
chainId = id,
|
||||
orderId = 0,
|
||||
source = ChainNodeLocal.Source.CUSTOM,
|
||||
)
|
||||
|
||||
fun ChainLocal.assetOf(
|
||||
assetId: Int,
|
||||
symbol: String,
|
||||
) = ChainAssetLocal(
|
||||
name = "Test",
|
||||
chainId = id,
|
||||
symbol = symbol,
|
||||
id = assetId,
|
||||
precision = 10,
|
||||
priceId = null,
|
||||
staking = "test",
|
||||
icon = "test",
|
||||
type = "test",
|
||||
buyProviders = "test",
|
||||
sellProviders = "test",
|
||||
typeExtras = null,
|
||||
enabled = true,
|
||||
source = AssetSourceLocal.DEFAULT
|
||||
)
|
||||
|
||||
suspend fun ChainDao.addChains(chains: List<JoinedChainInfo>) {
|
||||
applyDiff(
|
||||
chainDiff = addedDiff(chains.map(JoinedChainInfo::chain)),
|
||||
assetsDiff = addedDiff(chains.flatMap(JoinedChainInfo::assets)),
|
||||
nodesDiff = addedDiff(chains.flatMap(JoinedChainInfo::nodes)),
|
||||
explorersDiff = addedDiff(chains.flatMap(JoinedChainInfo::explorers)),
|
||||
externalApisDiff = addedDiff(chains.flatMap(JoinedChainInfo::externalApis)),
|
||||
nodeSelectionPreferencesDiff = emptyDiff()
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun ChainDao.addChain(joinedChainInfo: JoinedChainInfo) = addChains(listOf(joinedChainInfo))
|
||||
|
||||
suspend fun ChainDao.removeChain(joinedChainInfo: JoinedChainInfo) {
|
||||
applyDiff(
|
||||
chainDiff = removedDiff(joinedChainInfo.chain),
|
||||
assetsDiff = removedDiff(joinedChainInfo.assets),
|
||||
nodesDiff = removedDiff(joinedChainInfo.nodes),
|
||||
explorersDiff = removedDiff(joinedChainInfo.explorers),
|
||||
externalApisDiff = removedDiff(joinedChainInfo.externalApis),
|
||||
nodeSelectionPreferencesDiff = emptyDiff()
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun ChainDao.updateChain(joinedChainInfo: JoinedChainInfo) {
|
||||
applyDiff(
|
||||
chainDiff = updatedDiff(joinedChainInfo.chain),
|
||||
assetsDiff = updatedDiff(joinedChainInfo.assets),
|
||||
nodesDiff = updatedDiff(joinedChainInfo.nodes),
|
||||
explorersDiff = updatedDiff(joinedChainInfo.explorers),
|
||||
externalApisDiff = updatedDiff(joinedChainInfo.externalApis),
|
||||
nodeSelectionPreferencesDiff = emptyDiff()
|
||||
)
|
||||
}
|
||||
|
||||
fun <T> addedDiff(elements: List<T>) = CollectionDiffer.Diff(
|
||||
added = elements,
|
||||
updated = emptyList(),
|
||||
removed = emptyList(),
|
||||
all = elements
|
||||
)
|
||||
|
||||
fun <T> updatedDiff(elements: List<T>) = CollectionDiffer.Diff(
|
||||
added = emptyList(),
|
||||
updated = elements,
|
||||
removed = emptyList(),
|
||||
all = elements
|
||||
)
|
||||
|
||||
fun <T> updatedDiff(element: T) = updatedDiff(listOf(element))
|
||||
|
||||
fun <T> addedDiff(element: T) = addedDiff(listOf(element))
|
||||
|
||||
fun <T> removedDiff(element: T) = removedDiff(listOf(element))
|
||||
|
||||
fun <T> removedDiff(elements: List<T>) = CollectionDiffer.Diff(
|
||||
added = emptyList(),
|
||||
updated = emptyList(),
|
||||
removed = elements,
|
||||
all = elements
|
||||
)
|
||||
|
||||
fun <T> emptyDiff() = CollectionDiffer.Diff<T>(emptyList(), emptyList(), emptyList(), emptyList())
|
||||
|
||||
fun testMetaAccount(name: String = "Test") = MetaAccountLocal(
|
||||
substratePublicKey = byteArrayOf(),
|
||||
substrateCryptoType = CryptoType.SR25519,
|
||||
ethereumPublicKey = null,
|
||||
name = name,
|
||||
isSelected = false,
|
||||
substrateAccountId = byteArrayOf(),
|
||||
ethereumAddress = null,
|
||||
position = 0,
|
||||
type = MetaAccountLocal.Type.WATCH_ONLY,
|
||||
globallyUniqueId = "",
|
||||
parentMetaId = 1,
|
||||
status = MetaAccountLocal.Status.ACTIVE,
|
||||
typeExtras = null
|
||||
)
|
||||
|
||||
fun testChainAccount(
|
||||
metaId: Long,
|
||||
chainId: String,
|
||||
accountId: ByteArray = byteArrayOf()
|
||||
) = ChainAccountLocal(
|
||||
metaId = metaId,
|
||||
chainId = chainId,
|
||||
publicKey = byteArrayOf(),
|
||||
cryptoType = CryptoType.SR25519,
|
||||
accountId = accountId
|
||||
)
|
||||
|
||||
fun createCurrency(symbol: String = "$", selected: Boolean = true): CurrencyLocal {
|
||||
return CurrencyLocal(
|
||||
code = "USD",
|
||||
name = "Dollar",
|
||||
symbol = symbol,
|
||||
category = CurrencyLocal.Category.FIAT,
|
||||
popular = true,
|
||||
id = 0,
|
||||
coingeckoId = "usd",
|
||||
selected = selected
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.novafoundation.nova.core_db.AppDatabase
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
private const val CHAIN_ID = "1"
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class MetaAccountDaoTest : DaoTest<MetaAccountDao>(AppDatabase::metaAccountDao) {
|
||||
|
||||
private val chainDao by dao<ChainDao>()
|
||||
|
||||
@Before
|
||||
fun insertChain() = runBlocking {
|
||||
chainDao.addChain(createTestChain(id = CHAIN_ID))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldInsertMetaAccount() {
|
||||
runBlocking {
|
||||
dao.insertMetaAccount(testMetaAccount())
|
||||
dao.insertMetaAccount(testMetaAccount())
|
||||
|
||||
val accountsFromDb = dao.getMetaAccounts()
|
||||
|
||||
assertEquals(2, accountsFromDb.size)
|
||||
|
||||
val isIdAutoGenerated = accountsFromDb.withIndex().all { (index, account) ->
|
||||
account.id == index + 1L
|
||||
}
|
||||
|
||||
assertTrue("Id should be autogenerated", isIdAutoGenerated)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldInsertAndRetrieveChainAccounts() {
|
||||
runBlocking {
|
||||
val metaId = dao.insertMetaAccount(testMetaAccount())
|
||||
|
||||
assertNotEquals(-1, metaId)
|
||||
|
||||
dao.insertChainAccount(testChainAccount(metaId, CHAIN_ID))
|
||||
|
||||
val joinedMetaAccountInfo = dao.getJoinedMetaAccountInfo(metaId)
|
||||
|
||||
assertEquals(1, joinedMetaAccountInfo.chainAccounts.size)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldReplaceChainAccounts() {
|
||||
runBlocking {
|
||||
val metaId = dao.insertMetaAccount(testMetaAccount())
|
||||
|
||||
val newAccountId = byteArrayOf(1)
|
||||
|
||||
dao.insertChainAccount(testChainAccount(metaId, CHAIN_ID, accountId = byteArrayOf(0)))
|
||||
dao.insertChainAccount(testChainAccount(metaId, CHAIN_ID, accountId = newAccountId))
|
||||
|
||||
val chainAccounts = dao.getJoinedMetaAccountInfo(metaId).chainAccounts
|
||||
|
||||
assertEquals(1, chainAccounts.size)
|
||||
assertArrayEquals(newAccountId, chainAccounts.single().accountId)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.novafoundation.nova.core_db.AppDatabase
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class TokenDaoTest : DaoTest<TokenDao>(AppDatabase::tokenDao) {
|
||||
|
||||
private val currencyDao by dao<CurrencyDao>()
|
||||
|
||||
private val tokenSymbol = "$"
|
||||
|
||||
@Test
|
||||
fun getTokenWhenCurrencySelected() = runBlocking {
|
||||
currencyDao.insert(createCurrency(tokenSymbol, true))
|
||||
|
||||
val tokenWithCurrency = dao.getTokenWithCurrency(tokenSymbol)
|
||||
|
||||
assert(tokenWithCurrency != null)
|
||||
assert(tokenWithCurrency?.token == null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getTokenWhenCurrencyNotSelected() = runBlocking {
|
||||
currencyDao.insert(createCurrency(tokenSymbol, false))
|
||||
|
||||
val token = dao.getTokenWithCurrency(tokenSymbol)
|
||||
|
||||
assert(token == null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getTokensWhenCurrencySelected() = runBlocking {
|
||||
currencyDao.insert(createCurrency(tokenSymbol, true))
|
||||
|
||||
val tokensWithCurrencies = dao.getTokensWithCurrency(listOf(tokenSymbol))
|
||||
|
||||
assert(tokensWithCurrencies.isNotEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getTokensWhenCurrencyNotSelected() = runBlocking {
|
||||
currencyDao.insert(createCurrency(tokenSymbol, false))
|
||||
|
||||
val tokensWithCurrencies = dao.getTokensWithCurrency(listOf(tokenSymbol))
|
||||
|
||||
assert(tokensWithCurrencies.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldInsertTokenWithDefaultCurrency() = runBlocking {
|
||||
currencyDao.insert(createCurrency(tokenSymbol, true))
|
||||
|
||||
dao.insertTokenWithSelectedCurrency(tokenSymbol)
|
||||
val tokenWithCurrency = dao.getTokenWithCurrency(tokenSymbol)
|
||||
assert(tokenWithCurrency != null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldInsertTokenWithoutCurrency() = runBlocking {
|
||||
currencyDao.insert(createCurrency(tokenSymbol, false))
|
||||
|
||||
dao.insertTokenWithSelectedCurrency(tokenSymbol)
|
||||
val tokenWithCurrency = dao.getTokenWithCurrency(tokenSymbol)
|
||||
assert(tokenWithCurrency == null)
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.Room
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.room.testing.MigrationTestHelper
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import io.novafoundation.nova.core_db.AppDatabase
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Rule
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
|
||||
private const val DB_TEST_NAME = "test-db"
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
abstract class BaseMigrationTest {
|
||||
|
||||
@get:Rule
|
||||
val migrationHelper = MigrationTestHelper(
|
||||
InstrumentationRegistry.getInstrumentation(),
|
||||
AppDatabase::class.java.canonicalName,
|
||||
|
||||
FrameworkSQLiteOpenHelperFactory()
|
||||
)
|
||||
|
||||
protected fun runMigrationTest(
|
||||
from: Int,
|
||||
to: Int,
|
||||
vararg migrations: Migration,
|
||||
preMigrateBlock: (SupportSQLiteDatabase) -> Unit = {},
|
||||
postMigrateBlock: suspend (AppDatabase) -> Unit = {}
|
||||
) {
|
||||
runBlocking {
|
||||
val db = migrationHelper.createDatabase(DB_TEST_NAME, from)
|
||||
preMigrateBlock(db)
|
||||
|
||||
val validateDroppedTables = true
|
||||
migrationHelper.runMigrationsAndValidate(DB_TEST_NAME, to, validateDroppedTables, *migrations)
|
||||
|
||||
postMigrateBlock(getMigratedRoomDatabase(*migrations))
|
||||
}
|
||||
}
|
||||
|
||||
protected fun validateSchema(
|
||||
from: Int,
|
||||
to: Int,
|
||||
vararg migrations: Migration,
|
||||
) = runMigrationTest(from, to, *migrations)
|
||||
|
||||
private fun getMigratedRoomDatabase(vararg migrations: Migration): AppDatabase {
|
||||
val database: AppDatabase = Room.databaseBuilder(ApplicationProvider.getApplicationContext(),
|
||||
AppDatabase::class.java, DB_TEST_NAME)
|
||||
.addMigrations(*migrations)
|
||||
.build()
|
||||
|
||||
migrationHelper.closeWhenFinished(database)
|
||||
|
||||
return database
|
||||
}
|
||||
}
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import io.novafoundation.nova.core_db.AppDatabase
|
||||
import io.novafoundation.nova.core_db.converters.CryptoTypeConverters
|
||||
import io.novafoundation.nova.core_db.dao.assetOf
|
||||
import io.novafoundation.nova.core_db.dao.chainOf
|
||||
import io.novafoundation.nova.core_db.dao.testMetaAccount
|
||||
import io.novafoundation.nova.core_db.model.chain.account.MetaAccountLocal
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import java.math.BigInteger
|
||||
|
||||
private class OldAsset(
|
||||
val metaId: Long,
|
||||
val chainId: String,
|
||||
val tokenSymbol: String,
|
||||
val freeInPlanks: Int
|
||||
)
|
||||
|
||||
class BetterChainDiffingTest_8_9 : BaseMigrationTest() {
|
||||
|
||||
private val cryptoTypeConverters = CryptoTypeConverters()
|
||||
|
||||
var meta1Id: Long = -1
|
||||
var meta2Id: Long = -1
|
||||
|
||||
val chain1Id = "1"
|
||||
val chain2Id = "2"
|
||||
|
||||
private lateinit var assetsOld: List<OldAsset>
|
||||
|
||||
@Test
|
||||
fun validateMigration() = runMigrationTest(
|
||||
from = 8,
|
||||
to = 9,
|
||||
BetterChainDiffing_8_9,
|
||||
preMigrateBlock = ::preMigrate,
|
||||
postMigrateBlock = ::postMigrate
|
||||
)
|
||||
|
||||
private fun preMigrate(db: SupportSQLiteDatabase) {
|
||||
db.beginTransaction()
|
||||
|
||||
db.insertChain(chain1Id, assetSymbols = listOf("A", "B", "C"))
|
||||
db.insertChain(chain2Id, assetSymbols = listOf("C", "D", "E"))
|
||||
|
||||
meta1Id = db.insertMetaAccount(name = "1")
|
||||
meta2Id = db.insertMetaAccount(name = "2")
|
||||
|
||||
assetsOld = listOf(
|
||||
OldAsset(meta1Id, chain1Id, tokenSymbol = "A", freeInPlanks = 1),
|
||||
OldAsset(meta1Id, chain1Id, tokenSymbol = "B", freeInPlanks = 2),
|
||||
OldAsset(meta1Id, chain1Id, tokenSymbol = "C", freeInPlanks = 3),
|
||||
OldAsset(meta1Id, chain2Id, tokenSymbol = "C", freeInPlanks = 4),
|
||||
OldAsset(meta1Id, chain2Id, tokenSymbol = "D", freeInPlanks = 5),
|
||||
OldAsset(meta1Id, chain2Id, tokenSymbol = "E", freeInPlanks = 6),
|
||||
|
||||
OldAsset(meta2Id, chain1Id, tokenSymbol = "A", freeInPlanks = 11),
|
||||
OldAsset(meta2Id, chain1Id, tokenSymbol = "C", freeInPlanks = 13),
|
||||
)
|
||||
|
||||
assetsOld.forEach { db.insertAsset(it) }
|
||||
|
||||
db.setTransactionSuccessful()
|
||||
db.endTransaction()
|
||||
}
|
||||
|
||||
private suspend fun postMigrate(db: AppDatabase) {
|
||||
val assetsForMeta1 = db.assetDao().getSupportedAssets(meta1Id)
|
||||
|
||||
val symbolToAssetIdMapping = mapOf(
|
||||
(chain1Id to "A") to 0,
|
||||
(chain1Id to "B") to 1,
|
||||
(chain1Id to "C") to 2,
|
||||
(chain2Id to "C") to 0,
|
||||
(chain2Id to "D") to 1,
|
||||
(chain2Id to "E") to 2,
|
||||
)
|
||||
|
||||
assetsForMeta1.forEach {
|
||||
val actualChainId = it.asset!!.chainId
|
||||
val actualTokenSymbol = it.token!!.tokenSymbol
|
||||
|
||||
val assetIdExpected = symbolToAssetIdMapping[actualChainId to actualTokenSymbol]
|
||||
assertEquals(assetIdExpected, it.asset!!.assetId)
|
||||
|
||||
val expectedOldAsset = assetsOld.first { it.chainId == actualChainId && it.metaId == meta1Id && it.tokenSymbol == actualTokenSymbol }
|
||||
assertEquals(expectedOldAsset.freeInPlanks.toBigInteger(), it.asset!!.freeInPlanks)
|
||||
}
|
||||
}
|
||||
|
||||
private fun SupportSQLiteDatabase.insertMetaAccount(
|
||||
name: String
|
||||
): Long {
|
||||
val metaAccount = testMetaAccount(name)
|
||||
|
||||
val contentValues = ContentValues().apply {
|
||||
put(MetaAccountLocal.Table.Column.SUBSTRATE_PUBKEY, metaAccount.substratePublicKey)
|
||||
put(MetaAccountLocal.Table.Column.SUBSTRATE_ACCOUNT_ID, metaAccount.substrateAccountId)
|
||||
put(MetaAccountLocal.Table.Column.ETHEREUM_ADDRESS, metaAccount.ethereumAddress)
|
||||
put(MetaAccountLocal.Table.Column.ETHEREUM_PUBKEY, metaAccount.ethereumPublicKey)
|
||||
put(MetaAccountLocal.Table.Column.NAME, metaAccount.name)
|
||||
put(MetaAccountLocal.Table.Column.SUBSTRATE_CRYPTO_TYPE, cryptoTypeConverters.from(metaAccount.substrateCryptoType))
|
||||
put(MetaAccountLocal.Table.Column.IS_SELECTED, metaAccount.isSelected)
|
||||
put(MetaAccountLocal.Table.Column.POSITION, metaAccount.position)
|
||||
}
|
||||
|
||||
return insert(MetaAccountLocal.TABLE_NAME, 0, contentValues)
|
||||
}
|
||||
|
||||
private fun SupportSQLiteDatabase.insertChain(
|
||||
id: String,
|
||||
assetSymbols: List<String>
|
||||
) {
|
||||
val chain = chainOf(id)
|
||||
|
||||
val contentValues = ContentValues().apply {
|
||||
put("parentId", chain.parentId)
|
||||
put("name", chain.name)
|
||||
put("additional", chain.additional)
|
||||
put("id", chain.id)
|
||||
put("icon", chain.icon)
|
||||
// types
|
||||
putNull("url")
|
||||
putNull("overridesCommon")
|
||||
// externalApi
|
||||
putNull("staking_url")
|
||||
putNull("staking_type")
|
||||
putNull("history_type")
|
||||
putNull("history_url")
|
||||
putNull("crowdloans_url")
|
||||
putNull("crowdloans_type")
|
||||
|
||||
put("prefix", chain.prefix)
|
||||
put("isEthereumBased", chain.isEthereumBased)
|
||||
put("isTestNet", chain.isTestNet)
|
||||
put("hasCrowdloans", chain.hasCrowdloans)
|
||||
}
|
||||
|
||||
insert("chains", 0, contentValues)
|
||||
|
||||
val assets = assetSymbols.mapIndexed { index, symbol ->
|
||||
chain.assetOf(assetId = index, symbol)
|
||||
}
|
||||
|
||||
assets.forEach {
|
||||
val contentValues = ContentValues().apply {
|
||||
put("id", it.id)
|
||||
put("chainId", it.chainId)
|
||||
put("name", it.name)
|
||||
put("symbol", it.symbol)
|
||||
put("priceId", it.priceId)
|
||||
put("staking", it.staking)
|
||||
put("precision", it.precision)
|
||||
put("icon", it.icon)
|
||||
put("type", it.type)
|
||||
put("typeExtras", it.typeExtras)
|
||||
put("buyProviders", it.buyProviders)
|
||||
}
|
||||
|
||||
insert("chain_assets", 0, contentValues)
|
||||
}
|
||||
}
|
||||
|
||||
private fun SupportSQLiteDatabase.insertAsset(oldAsset: OldAsset) {
|
||||
val tokenContentValues = ContentValues().apply {
|
||||
put("symbol", oldAsset.tokenSymbol)
|
||||
putNull("dollarRate")
|
||||
putNull("recentRateChange")
|
||||
}
|
||||
|
||||
insert("tokens", SQLiteDatabase.CONFLICT_REPLACE, tokenContentValues)
|
||||
|
||||
val assetContentValues = ContentValues().apply {
|
||||
put("tokenSymbol", oldAsset.tokenSymbol)
|
||||
put("chainId", oldAsset.chainId)
|
||||
put("metaId", oldAsset.metaId)
|
||||
|
||||
val amountZero = BigInteger.ZERO.toString()
|
||||
|
||||
put("freeInPlanks", oldAsset.freeInPlanks.toString())
|
||||
put("frozenInPlanks", amountZero)
|
||||
put("reservedInPlanks", amountZero)
|
||||
|
||||
put("bondedInPlanks", amountZero)
|
||||
put("redeemableInPlanks", amountZero)
|
||||
put("unbondingInPlanks", amountZero)
|
||||
}
|
||||
|
||||
insert("assets", 0, assetContentValues)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<manifest />
|
||||
@@ -0,0 +1,341 @@
|
||||
package io.novafoundation.nova.core_db
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import io.novafoundation.nova.core_db.converters.AssetConverters
|
||||
import io.novafoundation.nova.core_db.converters.ChainConverters
|
||||
import io.novafoundation.nova.core_db.converters.CryptoTypeConverters
|
||||
import io.novafoundation.nova.core_db.converters.CurrencyConverters
|
||||
import io.novafoundation.nova.core_db.converters.ExternalApiConverters
|
||||
import io.novafoundation.nova.core_db.converters.ExternalBalanceTypeConverters
|
||||
import io.novafoundation.nova.core_db.converters.LongMathConverters
|
||||
import io.novafoundation.nova.core_db.converters.MetaAccountTypeConverters
|
||||
import io.novafoundation.nova.core_db.converters.NetworkTypeConverters
|
||||
import io.novafoundation.nova.core_db.converters.NftConverters
|
||||
import io.novafoundation.nova.core_db.converters.OperationConverters
|
||||
import io.novafoundation.nova.core_db.converters.ProxyAccountConverters
|
||||
import io.novafoundation.nova.core_db.dao.AccountDao
|
||||
import io.novafoundation.nova.core_db.dao.AccountStakingDao
|
||||
import io.novafoundation.nova.core_db.dao.AssetDao
|
||||
import io.novafoundation.nova.core_db.dao.BrowserHostSettingsDao
|
||||
import io.novafoundation.nova.core_db.dao.BrowserTabsDao
|
||||
import io.novafoundation.nova.core_db.dao.ChainAssetDao
|
||||
import io.novafoundation.nova.core_db.dao.ChainDao
|
||||
import io.novafoundation.nova.core_db.dao.CoinPriceDao
|
||||
import io.novafoundation.nova.core_db.dao.ContributionDao
|
||||
import io.novafoundation.nova.core_db.dao.CurrencyDao
|
||||
import io.novafoundation.nova.core_db.dao.DappAuthorizationDao
|
||||
import io.novafoundation.nova.core_db.dao.ExternalBalanceDao
|
||||
import io.novafoundation.nova.core_db.dao.FavouriteDAppsDao
|
||||
import io.novafoundation.nova.core_db.dao.GiftsDao
|
||||
import io.novafoundation.nova.core_db.dao.GovernanceDAppsDao
|
||||
import io.novafoundation.nova.core_db.dao.HoldsDao
|
||||
import io.novafoundation.nova.core_db.dao.LockDao
|
||||
import io.novafoundation.nova.core_db.dao.MetaAccountDao
|
||||
import io.novafoundation.nova.core_db.dao.MultisigOperationsDao
|
||||
import io.novafoundation.nova.core_db.dao.NftDao
|
||||
import io.novafoundation.nova.core_db.dao.NodeDao
|
||||
import io.novafoundation.nova.core_db.dao.OperationDao
|
||||
import io.novafoundation.nova.core_db.dao.PhishingAddressDao
|
||||
import io.novafoundation.nova.core_db.dao.PhishingSitesDao
|
||||
import io.novafoundation.nova.core_db.dao.StakingDashboardDao
|
||||
import io.novafoundation.nova.core_db.dao.StakingRewardPeriodDao
|
||||
import io.novafoundation.nova.core_db.dao.StakingTotalRewardDao
|
||||
import io.novafoundation.nova.core_db.dao.StorageDao
|
||||
import io.novafoundation.nova.core_db.dao.TinderGovDao
|
||||
import io.novafoundation.nova.core_db.dao.TokenDao
|
||||
import io.novafoundation.nova.core_db.dao.WalletConnectSessionsDao
|
||||
import io.novafoundation.nova.core_db.migrations.AddAdditionalFieldToChains_12_13
|
||||
import io.novafoundation.nova.core_db.migrations.AddBalanceHolds_60_61
|
||||
import io.novafoundation.nova.core_db.migrations.AddBalanceModesToAssets_51_52
|
||||
import io.novafoundation.nova.core_db.migrations.AddBrowserHostSettings_34_35
|
||||
import io.novafoundation.nova.core_db.migrations.AddBrowserTabs_64_65
|
||||
import io.novafoundation.nova.core_db.migrations.AddBuyProviders_7_8
|
||||
import io.novafoundation.nova.core_db.migrations.AddChainColor_4_5
|
||||
import io.novafoundation.nova.core_db.migrations.AddChainForeignKeyForProxy_63_64
|
||||
import io.novafoundation.nova.core_db.migrations.AddConnectionStateToChains_53_54
|
||||
import io.novafoundation.nova.core_db.migrations.AddFieldsToContributions
|
||||
import io.novafoundation.nova.core_db.migrations.AddContributions_23_24
|
||||
import io.novafoundation.nova.core_db.migrations.AddCurrencies_18_19
|
||||
import io.novafoundation.nova.core_db.migrations.AddDAppAuthorizations_1_2
|
||||
import io.novafoundation.nova.core_db.migrations.AddEnabledColumnToChainAssets_30_31
|
||||
import io.novafoundation.nova.core_db.migrations.AddEventIdToOperation_47_48
|
||||
import io.novafoundation.nova.core_db.migrations.AddExternalBalances_45_46
|
||||
import io.novafoundation.nova.core_db.migrations.AddExtrinsicContentField_37_38
|
||||
import io.novafoundation.nova.core_db.migrations.AddFavoriteDAppsOrdering_65_66
|
||||
import io.novafoundation.nova.core_db.migrations.AddFavouriteDApps_9_10
|
||||
import io.novafoundation.nova.core_db.migrations.AddFungibleNfts_55_56
|
||||
import io.novafoundation.nova.core_db.migrations.AddGifts_71_72
|
||||
import io.novafoundation.nova.core_db.migrations.AddGloballyUniqueIdToMetaAccounts_58_59
|
||||
import io.novafoundation.nova.core_db.migrations.AddGovernanceDapps_25_26
|
||||
import io.novafoundation.nova.core_db.migrations.AddGovernanceExternalApiToChain_27_28
|
||||
import io.novafoundation.nova.core_db.migrations.AddGovernanceFlagToChains_24_25
|
||||
import io.novafoundation.nova.core_db.migrations.AddGovernanceNetworkToExternalApi_33_34
|
||||
import io.novafoundation.nova.core_db.migrations.AddLegacyAddressPrefix_66_67
|
||||
import io.novafoundation.nova.core_db.migrations.AddLocalMigratorVersionToChainRuntimes_57_58
|
||||
import io.novafoundation.nova.core_db.migrations.AddLocks_22_23
|
||||
import io.novafoundation.nova.core_db.migrations.AddMetaAccountType_14_15
|
||||
import io.novafoundation.nova.core_db.migrations.AddMultisigCalls_69_70
|
||||
import io.novafoundation.nova.core_db.migrations.AddMultisigSupportFlag_70_71
|
||||
import io.novafoundation.nova.core_db.migrations.AddNfts_5_6
|
||||
import io.novafoundation.nova.core_db.migrations.AddNodeSelectionStrategyField_38_39
|
||||
import io.novafoundation.nova.core_db.migrations.AddPoolIdToOperations_46_47
|
||||
import io.novafoundation.nova.core_db.migrations.AddProxyAccount_54_55
|
||||
import io.novafoundation.nova.core_db.migrations.AddRewardAccountToStakingDashboard_43_44
|
||||
import io.novafoundation.nova.core_db.migrations.AddRuntimeFlagToChains_36_37
|
||||
import io.novafoundation.nova.core_db.migrations.AddSellProviders_67_68
|
||||
import io.novafoundation.nova.core_db.migrations.AddSitePhishing_6_7
|
||||
import io.novafoundation.nova.core_db.migrations.AddSourceToLocalAsset_28_29
|
||||
import io.novafoundation.nova.core_db.migrations.AddStakingDashboardItems_41_42
|
||||
import io.novafoundation.nova.core_db.migrations.AddStakingTypeToTotalRewards_44_45
|
||||
import io.novafoundation.nova.core_db.migrations.AddSwapOption_48_49
|
||||
import io.novafoundation.nova.core_db.migrations.AddTransactionVersionToRuntime_50_51
|
||||
import io.novafoundation.nova.core_db.migrations.AddTransferApisTable_29_30
|
||||
import io.novafoundation.nova.core_db.migrations.AddTypeExtrasToMetaAccount_68_69
|
||||
import io.novafoundation.nova.core_db.migrations.AddVersioningToGovernanceDapps_32_33
|
||||
import io.novafoundation.nova.core_db.migrations.AddWalletConnectSessions_39_40
|
||||
import io.novafoundation.nova.core_db.migrations.AssetTypes_2_3
|
||||
import io.novafoundation.nova.core_db.migrations.BetterChainDiffing_8_9
|
||||
import io.novafoundation.nova.core_db.migrations.ChainNetworkManagement_59_60
|
||||
import io.novafoundation.nova.core_db.migrations.ChainNetworkManagement_61_62
|
||||
import io.novafoundation.nova.core_db.migrations.ChainPushSupport_56_57
|
||||
import io.novafoundation.nova.core_db.migrations.ChangeAsset_3_4
|
||||
import io.novafoundation.nova.core_db.migrations.ChangeChainNodes_20_21
|
||||
import io.novafoundation.nova.core_db.migrations.ChangeDAppAuthorization_10_11
|
||||
import io.novafoundation.nova.core_db.migrations.ChangeSessionTopicToParing_52_53
|
||||
import io.novafoundation.nova.core_db.migrations.ChangeTokens_19_20
|
||||
import io.novafoundation.nova.core_db.migrations.ExtractExternalApiToSeparateTable_35_36
|
||||
import io.novafoundation.nova.core_db.migrations.FixBrokenForeignKeys_31_32
|
||||
import io.novafoundation.nova.core_db.migrations.FixMigrationConflicts_13_14
|
||||
import io.novafoundation.nova.core_db.migrations.GovernanceFlagToEnum_26_27
|
||||
import io.novafoundation.nova.core_db.migrations.NullableSubstrateAccountId_21_22
|
||||
import io.novafoundation.nova.core_db.migrations.NullableSubstratePublicKey_15_16
|
||||
import io.novafoundation.nova.core_db.migrations.RefactorOperations_49_50
|
||||
import io.novafoundation.nova.core_db.migrations.RemoveChainForeignKeyFromChainAccount_11_12
|
||||
import io.novafoundation.nova.core_db.migrations.RemoveColorFromChains_17_18
|
||||
import io.novafoundation.nova.core_db.migrations.StakingRewardPeriods_42_43
|
||||
import io.novafoundation.nova.core_db.migrations.TinderGovBasket_62_63
|
||||
import io.novafoundation.nova.core_db.migrations.TransferFiatAmount_40_41
|
||||
import io.novafoundation.nova.core_db.migrations.WatchOnlyChainAccounts_16_17
|
||||
import io.novafoundation.nova.core_db.model.AccountLocal
|
||||
import io.novafoundation.nova.core_db.model.AccountStakingLocal
|
||||
import io.novafoundation.nova.core_db.model.AssetLocal
|
||||
import io.novafoundation.nova.core_db.model.BalanceHoldLocal
|
||||
import io.novafoundation.nova.core_db.model.BalanceLockLocal
|
||||
import io.novafoundation.nova.core_db.model.BrowserHostSettingsLocal
|
||||
import io.novafoundation.nova.core_db.model.BrowserTabLocal
|
||||
import io.novafoundation.nova.core_db.model.CoinPriceLocal
|
||||
import io.novafoundation.nova.core_db.model.ContributionLocal
|
||||
import io.novafoundation.nova.core_db.model.CurrencyLocal
|
||||
import io.novafoundation.nova.core_db.model.DappAuthorizationLocal
|
||||
import io.novafoundation.nova.core_db.model.ExternalBalanceLocal
|
||||
import io.novafoundation.nova.core_db.model.FavouriteDAppLocal
|
||||
import io.novafoundation.nova.core_db.model.GiftLocal
|
||||
import io.novafoundation.nova.core_db.model.GovernanceDAppLocal
|
||||
import io.novafoundation.nova.core_db.model.MultisigOperationCallLocal
|
||||
import io.novafoundation.nova.core_db.model.NftLocal
|
||||
import io.novafoundation.nova.core_db.model.NodeLocal
|
||||
import io.novafoundation.nova.core_db.model.PhishingAddressLocal
|
||||
import io.novafoundation.nova.core_db.model.PhishingSiteLocal
|
||||
import io.novafoundation.nova.core_db.model.StakingDashboardItemLocal
|
||||
import io.novafoundation.nova.core_db.model.StakingRewardPeriodLocal
|
||||
import io.novafoundation.nova.core_db.model.StorageEntryLocal
|
||||
import io.novafoundation.nova.core_db.model.TinderGovBasketItemLocal
|
||||
import io.novafoundation.nova.core_db.model.TinderGovVotingPowerLocal
|
||||
import io.novafoundation.nova.core_db.model.TokenLocal
|
||||
import io.novafoundation.nova.core_db.model.TotalRewardLocal
|
||||
import io.novafoundation.nova.core_db.model.WalletConnectPairingLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainAssetLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainExplorerLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainExternalApiLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainNodeLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainRuntimeInfoLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.NodeSelectionPreferencesLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.account.ChainAccountLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.account.MetaAccountLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.account.ProxyAccountLocal
|
||||
import io.novafoundation.nova.core_db.model.operation.DirectRewardTypeLocal
|
||||
import io.novafoundation.nova.core_db.model.operation.ExtrinsicTypeLocal
|
||||
import io.novafoundation.nova.core_db.model.operation.OperationBaseLocal
|
||||
import io.novafoundation.nova.core_db.model.operation.PoolRewardTypeLocal
|
||||
import io.novafoundation.nova.core_db.model.operation.SwapTypeLocal
|
||||
import io.novafoundation.nova.core_db.model.operation.TransferTypeLocal
|
||||
|
||||
@Database(
|
||||
version = 73,
|
||||
entities = [
|
||||
AccountLocal::class,
|
||||
NodeLocal::class,
|
||||
AssetLocal::class,
|
||||
TokenLocal::class,
|
||||
PhishingAddressLocal::class,
|
||||
StorageEntryLocal::class,
|
||||
AccountStakingLocal::class,
|
||||
TotalRewardLocal::class,
|
||||
OperationBaseLocal::class,
|
||||
TransferTypeLocal::class,
|
||||
DirectRewardTypeLocal::class,
|
||||
PoolRewardTypeLocal::class,
|
||||
ExtrinsicTypeLocal::class,
|
||||
SwapTypeLocal::class,
|
||||
ChainLocal::class,
|
||||
ChainNodeLocal::class,
|
||||
ChainAssetLocal::class,
|
||||
ChainRuntimeInfoLocal::class,
|
||||
ChainExplorerLocal::class,
|
||||
ChainExternalApiLocal::class,
|
||||
MetaAccountLocal::class,
|
||||
ChainAccountLocal::class,
|
||||
DappAuthorizationLocal::class,
|
||||
NftLocal::class,
|
||||
PhishingSiteLocal::class,
|
||||
FavouriteDAppLocal::class,
|
||||
CurrencyLocal::class,
|
||||
BalanceLockLocal::class,
|
||||
ContributionLocal::class,
|
||||
GovernanceDAppLocal::class,
|
||||
BrowserHostSettingsLocal::class,
|
||||
WalletConnectPairingLocal::class,
|
||||
CoinPriceLocal::class,
|
||||
StakingDashboardItemLocal::class,
|
||||
StakingRewardPeriodLocal::class,
|
||||
ExternalBalanceLocal::class,
|
||||
ProxyAccountLocal::class,
|
||||
BalanceHoldLocal::class,
|
||||
NodeSelectionPreferencesLocal::class,
|
||||
TinderGovBasketItemLocal::class,
|
||||
TinderGovVotingPowerLocal::class,
|
||||
BrowserTabLocal::class,
|
||||
MultisigOperationCallLocal::class,
|
||||
GiftLocal::class
|
||||
],
|
||||
)
|
||||
@TypeConverters(
|
||||
LongMathConverters::class,
|
||||
NetworkTypeConverters::class,
|
||||
OperationConverters::class,
|
||||
CryptoTypeConverters::class,
|
||||
NftConverters::class,
|
||||
MetaAccountTypeConverters::class,
|
||||
CurrencyConverters::class,
|
||||
AssetConverters::class,
|
||||
ExternalApiConverters::class,
|
||||
ChainConverters::class,
|
||||
ExternalBalanceTypeConverters::class,
|
||||
ProxyAccountConverters::class,
|
||||
)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
companion object {
|
||||
|
||||
private var instance: AppDatabase? = null
|
||||
|
||||
@Synchronized
|
||||
fun get(
|
||||
context: Context
|
||||
): AppDatabase {
|
||||
if (instance == null) {
|
||||
instance = Room.databaseBuilder(
|
||||
context.applicationContext,
|
||||
AppDatabase::class.java,
|
||||
"app.db"
|
||||
)
|
||||
.addMigrations(AddDAppAuthorizations_1_2, AssetTypes_2_3, ChangeAsset_3_4)
|
||||
.addMigrations(AddChainColor_4_5, AddNfts_5_6, AddSitePhishing_6_7, AddBuyProviders_7_8, BetterChainDiffing_8_9)
|
||||
.addMigrations(AddFavouriteDApps_9_10, ChangeDAppAuthorization_10_11, RemoveChainForeignKeyFromChainAccount_11_12)
|
||||
.addMigrations(AddAdditionalFieldToChains_12_13, FixMigrationConflicts_13_14, AddMetaAccountType_14_15)
|
||||
.addMigrations(NullableSubstratePublicKey_15_16, WatchOnlyChainAccounts_16_17, RemoveColorFromChains_17_18)
|
||||
.addMigrations(AddCurrencies_18_19, ChangeTokens_19_20, ChangeChainNodes_20_21)
|
||||
.addMigrations(NullableSubstrateAccountId_21_22, AddLocks_22_23, AddContributions_23_24)
|
||||
.addMigrations(AddGovernanceFlagToChains_24_25, AddGovernanceDapps_25_26, GovernanceFlagToEnum_26_27)
|
||||
.addMigrations(AddGovernanceExternalApiToChain_27_28)
|
||||
.addMigrations(AddSourceToLocalAsset_28_29, AddTransferApisTable_29_30, AddEnabledColumnToChainAssets_30_31)
|
||||
.addMigrations(FixBrokenForeignKeys_31_32, AddVersioningToGovernanceDapps_32_33)
|
||||
.addMigrations(AddGovernanceNetworkToExternalApi_33_34, AddBrowserHostSettings_34_35)
|
||||
.addMigrations(ExtractExternalApiToSeparateTable_35_36, AddRuntimeFlagToChains_36_37)
|
||||
.addMigrations(AddExtrinsicContentField_37_38, AddNodeSelectionStrategyField_38_39)
|
||||
.addMigrations(AddWalletConnectSessions_39_40, TransferFiatAmount_40_41)
|
||||
.addMigrations(AddStakingDashboardItems_41_42, StakingRewardPeriods_42_43)
|
||||
.addMigrations(AddRewardAccountToStakingDashboard_43_44, AddStakingTypeToTotalRewards_44_45, AddExternalBalances_45_46)
|
||||
.addMigrations(AddPoolIdToOperations_46_47, AddEventIdToOperation_47_48, AddSwapOption_48_49)
|
||||
.addMigrations(RefactorOperations_49_50, AddTransactionVersionToRuntime_50_51, AddBalanceModesToAssets_51_52)
|
||||
.addMigrations(ChangeSessionTopicToParing_52_53, AddConnectionStateToChains_53_54, AddProxyAccount_54_55)
|
||||
.addMigrations(AddFungibleNfts_55_56, ChainPushSupport_56_57)
|
||||
.addMigrations(AddLocalMigratorVersionToChainRuntimes_57_58, AddGloballyUniqueIdToMetaAccounts_58_59)
|
||||
.addMigrations(ChainNetworkManagement_59_60, AddBalanceHolds_60_61, ChainNetworkManagement_61_62)
|
||||
.addMigrations(TinderGovBasket_62_63, AddChainForeignKeyForProxy_63_64, AddBrowserTabs_64_65)
|
||||
.addMigrations(AddFavoriteDAppsOrdering_65_66, AddLegacyAddressPrefix_66_67, AddSellProviders_67_68)
|
||||
.addMigrations(AddTypeExtrasToMetaAccount_68_69, AddMultisigCalls_69_70, AddMultisigSupportFlag_70_71)
|
||||
.addMigrations(AddGifts_71_72, AddFieldsToContributions)
|
||||
.build()
|
||||
}
|
||||
return instance!!
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun nodeDao(): NodeDao
|
||||
|
||||
abstract fun userDao(): AccountDao
|
||||
|
||||
abstract fun assetDao(): AssetDao
|
||||
|
||||
abstract fun operationDao(): OperationDao
|
||||
|
||||
abstract fun phishingAddressesDao(): PhishingAddressDao
|
||||
|
||||
abstract fun storageDao(): StorageDao
|
||||
|
||||
abstract fun tokenDao(): TokenDao
|
||||
|
||||
abstract fun accountStakingDao(): AccountStakingDao
|
||||
|
||||
abstract fun stakingTotalRewardDao(): StakingTotalRewardDao
|
||||
|
||||
abstract fun chainDao(): ChainDao
|
||||
|
||||
abstract fun chainAssetDao(): ChainAssetDao
|
||||
|
||||
abstract fun metaAccountDao(): MetaAccountDao
|
||||
|
||||
abstract fun dAppAuthorizationDao(): DappAuthorizationDao
|
||||
|
||||
abstract fun nftDao(): NftDao
|
||||
|
||||
abstract fun phishingSitesDao(): PhishingSitesDao
|
||||
|
||||
abstract fun favouriteDAppsDao(): FavouriteDAppsDao
|
||||
|
||||
abstract fun currencyDao(): CurrencyDao
|
||||
|
||||
abstract fun lockDao(): LockDao
|
||||
|
||||
abstract fun contributionDao(): ContributionDao
|
||||
|
||||
abstract fun governanceDAppsDao(): GovernanceDAppsDao
|
||||
|
||||
abstract fun browserHostSettingsDao(): BrowserHostSettingsDao
|
||||
|
||||
abstract fun walletConnectSessionsDao(): WalletConnectSessionsDao
|
||||
|
||||
abstract fun stakingDashboardDao(): StakingDashboardDao
|
||||
|
||||
abstract fun coinPriceDao(): CoinPriceDao
|
||||
|
||||
abstract fun stakingRewardPeriodDao(): StakingRewardPeriodDao
|
||||
|
||||
abstract fun externalBalanceDao(): ExternalBalanceDao
|
||||
|
||||
abstract fun holdsDao(): HoldsDao
|
||||
|
||||
abstract fun tinderGovDao(): TinderGovDao
|
||||
|
||||
abstract fun browserTabsDao(): BrowserTabsDao
|
||||
|
||||
abstract fun multisigOperationsDao(): MultisigOperationsDao
|
||||
|
||||
abstract fun giftsDao(): GiftsDao
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package io.novafoundation.nova.core_db.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import io.novafoundation.nova.core_db.model.AssetLocal.EDCountingModeLocal
|
||||
import io.novafoundation.nova.core_db.model.AssetLocal.TransferableModeLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.AssetSourceLocal
|
||||
|
||||
class AssetConverters {
|
||||
|
||||
@TypeConverter
|
||||
fun fromCategory(type: AssetSourceLocal): String {
|
||||
return type.name
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toCategory(name: String): AssetSourceLocal {
|
||||
return enumValueOf(name)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromTransferableMode(mode: TransferableModeLocal): Int {
|
||||
return mode.ordinal
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toTransferableMode(index: Int): TransferableModeLocal {
|
||||
return TransferableModeLocal.values()[index]
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromEdCountingMode(mode: EDCountingModeLocal): Int {
|
||||
return mode.ordinal
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toEdCountingMode(index: Int): EDCountingModeLocal {
|
||||
return EDCountingModeLocal.values()[index]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package io.novafoundation.nova.core_db.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import io.novafoundation.nova.common.utils.enumValueOfOrNull
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainLocal.ConnectionStateLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainLocal.AutoBalanceStrategyLocal
|
||||
|
||||
class ChainConverters {
|
||||
|
||||
@TypeConverter
|
||||
fun fromNodeStrategy(strategy: AutoBalanceStrategyLocal): String = strategy.name
|
||||
|
||||
@TypeConverter
|
||||
fun toNodeStrategy(name: String): AutoBalanceStrategyLocal {
|
||||
return enumValueOfOrNull<AutoBalanceStrategyLocal>(name) ?: AutoBalanceStrategyLocal.UNKNOWN
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromConnection(connectionState: ConnectionStateLocal): String = connectionState.name
|
||||
|
||||
@TypeConverter
|
||||
fun toConnection(name: String): ConnectionStateLocal {
|
||||
return enumValueOfOrNull<ConnectionStateLocal>(name) ?: ConnectionStateLocal.LIGHT_SYNC
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package io.novafoundation.nova.core_db.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import io.novafoundation.nova.core.model.CryptoType
|
||||
|
||||
class CryptoTypeConverters {
|
||||
|
||||
@TypeConverter
|
||||
fun from(cryptoType: CryptoType?): String? = cryptoType?.name
|
||||
|
||||
@TypeConverter
|
||||
fun to(name: String?): CryptoType? = name?.let { enumValueOf<CryptoType>(it) }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.novafoundation.nova.core_db.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import io.novafoundation.nova.core_db.model.CurrencyLocal
|
||||
|
||||
class CurrencyConverters {
|
||||
|
||||
@TypeConverter
|
||||
fun fromCategory(type: CurrencyLocal.Category): String {
|
||||
return type.name
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toCategory(name: String): CurrencyLocal.Category {
|
||||
return enumValueOf(name)
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package io.novafoundation.nova.core_db.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import io.novafoundation.nova.common.utils.enumValueOfOrNull
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainExternalApiLocal.SourceType
|
||||
|
||||
class ExternalApiConverters {
|
||||
|
||||
@TypeConverter
|
||||
fun fromApiType(apiType: SourceType): String {
|
||||
return apiType.name
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toApiType(raw: String): SourceType {
|
||||
return enumValueOfOrNull<SourceType>(raw) ?: SourceType.UNKNOWN
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package io.novafoundation.nova.core_db.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import io.novafoundation.nova.core_db.model.ExternalBalanceLocal
|
||||
|
||||
class ExternalBalanceTypeConverters {
|
||||
|
||||
@TypeConverter
|
||||
fun fromType(type: ExternalBalanceLocal.Type): String {
|
||||
return type.name
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toType(name: String): ExternalBalanceLocal.Type {
|
||||
return ExternalBalanceLocal.Type.valueOf(name)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package io.novafoundation.nova.core_db.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
|
||||
class LongMathConverters {
|
||||
|
||||
@TypeConverter
|
||||
fun fromBigDecimal(balance: BigDecimal?): String? {
|
||||
return balance?.toString()
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toBigDecimal(balance: String?): BigDecimal? {
|
||||
return balance?.let { BigDecimal(it) }
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromBigInteger(balance: BigInteger?): String? {
|
||||
return balance?.toString()
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toBigInteger(balance: String?): BigInteger? {
|
||||
return balance?.let {
|
||||
// When using aggregates like SUM in SQL queries, SQLite might return the result in a scientific notation especially if aggregation is done
|
||||
// BigInteger, which is stored as a string and SQLite casts it to REAL which causing the scientific notation on big numbers
|
||||
// This can be avoided by adjusting the query but we keep the fallback to BigDecimal parsing here anyways to avoid unpleasant crashes
|
||||
// It doesn't bring much impact since try-catch doesn't have an overhead unless the exception is thrown
|
||||
try {
|
||||
BigInteger(it)
|
||||
} catch (e: NumberFormatException) {
|
||||
BigDecimal(it).toBigInteger()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package io.novafoundation.nova.core_db.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import io.novafoundation.nova.core_db.model.chain.account.MetaAccountLocal
|
||||
|
||||
class MetaAccountTypeConverters {
|
||||
|
||||
@TypeConverter
|
||||
fun fromEnum(type: MetaAccountLocal.Type): String {
|
||||
return type.name
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toEnum(name: String): MetaAccountLocal.Type {
|
||||
return MetaAccountLocal.Type.valueOf(name)
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package io.novafoundation.nova.core_db.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import io.novafoundation.nova.core.model.Node
|
||||
|
||||
class NetworkTypeConverters {
|
||||
|
||||
@TypeConverter
|
||||
fun fromNetworkType(networkType: Node.NetworkType): Int {
|
||||
return networkType.ordinal
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toNetworkType(ordinal: Int): Node.NetworkType {
|
||||
return Node.NetworkType.values()[ordinal]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.novafoundation.nova.core_db.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import io.novafoundation.nova.core_db.model.NftLocal
|
||||
|
||||
class NftConverters {
|
||||
|
||||
@TypeConverter
|
||||
fun fromNftType(type: NftLocal.Type): String {
|
||||
return type.name
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toNftType(name: String): NftLocal.Type {
|
||||
return enumValueOf(name)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromNftIssuanceType(type: NftLocal.IssuanceType): String {
|
||||
return type.name
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toNftIssuanceType(name: String): NftLocal.IssuanceType {
|
||||
return enumValueOf(name)
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package io.novafoundation.nova.core_db.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import io.novafoundation.nova.core_db.model.operation.ExtrinsicTypeLocal
|
||||
import io.novafoundation.nova.core_db.model.operation.OperationBaseLocal
|
||||
|
||||
class OperationConverters {
|
||||
|
||||
@TypeConverter
|
||||
fun fromOperationSource(source: OperationBaseLocal.Source) = source.ordinal
|
||||
|
||||
@TypeConverter
|
||||
fun toOperationSource(ordinal: Int) = OperationBaseLocal.Source.values()[ordinal]
|
||||
|
||||
@TypeConverter
|
||||
fun fromOperationStatus(status: OperationBaseLocal.Status) = status.ordinal
|
||||
|
||||
@TypeConverter
|
||||
fun toOperationStatus(ordinal: Int) = OperationBaseLocal.Status.values()[ordinal]
|
||||
|
||||
@TypeConverter
|
||||
fun fromExtrinsicContentType(type: ExtrinsicTypeLocal.ContentType) = type.name
|
||||
|
||||
@TypeConverter
|
||||
fun toExtrinsicContentType(name: String): ExtrinsicTypeLocal.ContentType = enumValueOf(name)
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package io.novafoundation.nova.core_db.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import io.novafoundation.nova.core_db.model.chain.account.MetaAccountLocal
|
||||
|
||||
class ProxyAccountConverters {
|
||||
@TypeConverter
|
||||
fun fromStatusType(type: MetaAccountLocal.Status): String {
|
||||
return type.name
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toStatusType(name: String): MetaAccountLocal.Status {
|
||||
return MetaAccountLocal.Status.valueOf(name)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import io.novafoundation.nova.core.model.Node
|
||||
import io.novafoundation.nova.core_db.model.AccountLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
abstract class AccountDao {
|
||||
|
||||
@Query("select * from users order by networkType, position")
|
||||
abstract fun accountsFlow(): Flow<List<AccountLocal>>
|
||||
|
||||
@Query("select * from users order by networkType, position")
|
||||
abstract suspend fun getAccounts(): List<AccountLocal>
|
||||
|
||||
@Query("select * from users where address = :address")
|
||||
abstract suspend fun getAccount(address: String): AccountLocal?
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||
abstract suspend fun insert(account: AccountLocal): Long
|
||||
|
||||
@Query("DELETE FROM users where address = :address")
|
||||
abstract suspend fun remove(address: String)
|
||||
|
||||
@Update
|
||||
abstract suspend fun updateAccount(account: AccountLocal)
|
||||
|
||||
@Update
|
||||
abstract suspend fun updateAccounts(accounts: List<AccountLocal>)
|
||||
|
||||
@Query("SELECT COALESCE(MAX(position), 0) + 1 from users")
|
||||
abstract suspend fun getNextPosition(): Int
|
||||
|
||||
@Query("select * from users where networkType = :networkType")
|
||||
abstract suspend fun getAccountsByNetworkType(networkType: Int): List<AccountLocal>
|
||||
|
||||
@Query("select * from users where (address LIKE '%' || :query || '%') AND networkType = :networkType")
|
||||
abstract suspend fun getAccounts(query: String, networkType: Node.NetworkType): List<AccountLocal>
|
||||
|
||||
@Query("SELECT EXISTS(SELECT * FROM users WHERE address = :accountAddress)")
|
||||
abstract suspend fun accountExists(accountAddress: String): Boolean
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import io.novafoundation.nova.core_db.model.AccountStakingLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
|
||||
private const val SELECT_QUERY = """
|
||||
SELECT * FROM account_staking_accesses
|
||||
WHERE accountId = :accountId AND chainId = :chainId AND chainAssetId = :chainAssetId
|
||||
"""
|
||||
|
||||
@Dao
|
||||
abstract class AccountStakingDao {
|
||||
|
||||
@Query(SELECT_QUERY)
|
||||
abstract suspend fun get(chainId: String, chainAssetId: Int, accountId: ByteArray): AccountStakingLocal
|
||||
|
||||
@Query(SELECT_QUERY)
|
||||
protected abstract fun observeInternal(chainId: String, chainAssetId: Int, accountId: ByteArray): Flow<AccountStakingLocal?>
|
||||
|
||||
fun observeDistinct(chainId: String, chainAssetId: Int, accountId: ByteArray): Flow<AccountStakingLocal> {
|
||||
return observeInternal(chainId, chainAssetId, accountId)
|
||||
.filterNotNull()
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insert(accountStaking: AccountStakingLocal)
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import io.novafoundation.nova.common.utils.flowOfAll
|
||||
import io.novafoundation.nova.common.utils.mapToSet
|
||||
import io.novafoundation.nova.core_db.model.AssetAndChainId
|
||||
import io.novafoundation.nova.core_db.model.AssetLocal
|
||||
import io.novafoundation.nova.core_db.model.AssetWithToken
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
private const val RETRIEVE_ASSET_SQL_META_ID = """
|
||||
SELECT *, ca.chainId as ca_chainId, ca.id as ca_assetId FROM chain_assets AS ca
|
||||
LEFT JOIN assets AS a ON a.assetId = ca.id AND a.chainId = ca.chainId AND a.metaId = :metaId
|
||||
INNER JOIN currencies as currency ON currency.selected = 1
|
||||
LEFT JOIN tokens AS t ON ca.symbol = t.tokenSymbol AND currency.id = t.currencyId
|
||||
WHERE ca.chainId = :chainId AND ca.id = :assetId
|
||||
"""
|
||||
|
||||
private const val RETRIEVE_SYNCED_ACCOUNT_ASSETS_QUERY = """
|
||||
SELECT *, ca.chainId as ca_chainId, ca.id as ca_assetId FROM assets AS a
|
||||
INNER JOIN chain_assets AS ca ON a.assetId = ca.id AND a.chainId = ca.chainId
|
||||
INNER JOIN currencies as currency ON currency.selected = 1
|
||||
LEFT JOIN tokens AS t ON ca.symbol = t.tokenSymbol AND currency.id = t.currencyId
|
||||
WHERE a.metaId = :metaId
|
||||
"""
|
||||
|
||||
private const val RETRIEVE_SUPPORTED_ACCOUNT_ASSETS_QUERY = """
|
||||
SELECT *, ca.chainId as ca_chainId, ca.id as ca_assetId FROM chain_assets AS ca
|
||||
LEFT JOIN assets AS a ON a.assetId = ca.id AND a.chainId = ca.chainId AND a.metaId = :metaId
|
||||
INNER JOIN currencies as currency ON currency.selected = 1
|
||||
LEFT JOIN tokens AS t ON ca.symbol = t.tokenSymbol AND currency.id = t.currencyId
|
||||
"""
|
||||
|
||||
private const val RETRIEVE_ASSETS_SQL_META_ID = """
|
||||
SELECT *, ca.chainId as ca_chainId, ca.id as ca_assetId FROM chain_assets AS ca
|
||||
LEFT JOIN assets AS a ON a.assetId = ca.id AND a.chainId = ca.chainId AND a.metaId = :metaId
|
||||
INNER JOIN currencies as currency ON currency.selected = 1
|
||||
LEFT JOIN tokens AS t ON ca.symbol = t.tokenSymbol AND currency.id = t.currencyId
|
||||
WHERE ca.chainId || ':' || ca.id in (:joinedChainAndAssetIds)
|
||||
"""
|
||||
|
||||
interface AssetReadOnlyCache {
|
||||
|
||||
fun observeSyncedAssets(metaId: Long): Flow<List<AssetWithToken>>
|
||||
|
||||
suspend fun getSyncedAssets(metaId: Long): List<AssetWithToken>
|
||||
|
||||
fun observeSupportedAssets(metaId: Long): Flow<List<AssetWithToken>>
|
||||
|
||||
suspend fun getSupportedAssets(metaId: Long): List<AssetWithToken>
|
||||
|
||||
fun observeAsset(metaId: Long, chainId: String, assetId: Int): Flow<AssetWithToken>
|
||||
|
||||
fun observeAssetOrNull(metaId: Long, chainId: String, assetId: Int): Flow<AssetWithToken?>
|
||||
|
||||
fun observeAssets(metaId: Long, assetIds: Collection<AssetAndChainId>): Flow<List<AssetWithToken>>
|
||||
|
||||
suspend fun getAssetWithToken(metaId: Long, chainId: String, assetId: Int): AssetWithToken?
|
||||
|
||||
suspend fun getAsset(metaId: Long, chainId: String, assetId: Int): AssetLocal?
|
||||
|
||||
suspend fun getAssetsInChain(metaId: Long, chainId: String): List<AssetLocal>
|
||||
|
||||
suspend fun getAllAssets(): List<AssetLocal>
|
||||
|
||||
suspend fun getAssetsById(id: Int): List<AssetLocal>
|
||||
}
|
||||
|
||||
@Dao
|
||||
abstract class AssetDao : AssetReadOnlyCache {
|
||||
|
||||
@Query(RETRIEVE_SYNCED_ACCOUNT_ASSETS_QUERY)
|
||||
abstract override fun observeSyncedAssets(metaId: Long): Flow<List<AssetWithToken>>
|
||||
|
||||
@Query(RETRIEVE_SYNCED_ACCOUNT_ASSETS_QUERY)
|
||||
abstract override suspend fun getSyncedAssets(metaId: Long): List<AssetWithToken>
|
||||
|
||||
@Query(RETRIEVE_SUPPORTED_ACCOUNT_ASSETS_QUERY)
|
||||
abstract override fun observeSupportedAssets(metaId: Long): Flow<List<AssetWithToken>>
|
||||
|
||||
@Query(RETRIEVE_SUPPORTED_ACCOUNT_ASSETS_QUERY)
|
||||
abstract override suspend fun getSupportedAssets(metaId: Long): List<AssetWithToken>
|
||||
|
||||
@Query(RETRIEVE_ASSET_SQL_META_ID)
|
||||
abstract override fun observeAsset(metaId: Long, chainId: String, assetId: Int): Flow<AssetWithToken>
|
||||
|
||||
@Query(RETRIEVE_ASSET_SQL_META_ID)
|
||||
abstract override fun observeAssetOrNull(metaId: Long, chainId: String, assetId: Int): Flow<AssetWithToken?>
|
||||
|
||||
@Query(RETRIEVE_ASSET_SQL_META_ID)
|
||||
abstract override suspend fun getAssetWithToken(metaId: Long, chainId: String, assetId: Int): AssetWithToken?
|
||||
|
||||
@Query("SELECT * FROM assets WHERE metaId = :metaId AND chainId = :chainId AND assetId = :assetId")
|
||||
abstract override suspend fun getAsset(metaId: Long, chainId: String, assetId: Int): AssetLocal?
|
||||
|
||||
@Query("SELECT * FROM assets WHERE metaId = :metaId AND chainId = :chainId")
|
||||
abstract override suspend fun getAssetsInChain(metaId: Long, chainId: String): List<AssetLocal>
|
||||
|
||||
@Query("SELECT * FROM assets")
|
||||
abstract override suspend fun getAllAssets(): List<AssetLocal>
|
||||
|
||||
@Query("SELECT * FROM assets WHERE assetId IS :id")
|
||||
abstract override suspend fun getAssetsById(id: Int): List<AssetLocal>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertAsset(asset: AssetLocal)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertAssets(assets: List<AssetLocal>)
|
||||
|
||||
@Delete(entity = AssetLocal::class)
|
||||
abstract suspend fun clearAssets(assetIds: List<ClearAssetsParams>)
|
||||
|
||||
@Query(RETRIEVE_ASSETS_SQL_META_ID)
|
||||
protected abstract fun observeJoinedAssets(metaId: Long, joinedChainAndAssetIds: Set<String>): Flow<List<AssetWithToken>>
|
||||
|
||||
override fun observeAssets(metaId: Long, assetIds: Collection<AssetAndChainId>): Flow<List<AssetWithToken>> {
|
||||
return flowOfAll {
|
||||
val joinedChainAndAssetIds = assetIds.mapToSet { (chainId, assetId) -> "$chainId:$assetId" }
|
||||
|
||||
observeJoinedAssets(metaId, joinedChainAndAssetIds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ClearAssetsParams(val chainId: String, val assetId: Int)
|
||||
@@ -0,0 +1,23 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import io.novafoundation.nova.core_db.model.BrowserHostSettingsLocal
|
||||
|
||||
@Dao
|
||||
abstract class BrowserHostSettingsDao {
|
||||
|
||||
@Query("SELECT * FROM browser_host_settings")
|
||||
abstract suspend fun getBrowserAllHostSettings(): List<BrowserHostSettingsLocal>
|
||||
|
||||
@Query("SELECT * FROM browser_host_settings WHERE hostUrl = :host")
|
||||
abstract suspend fun getBrowserHostSettings(host: String): BrowserHostSettingsLocal?
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertBrowserHostSettings(settings: BrowserHostSettingsLocal)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertBrowserHostSettings(settings: List<BrowserHostSettingsLocal>)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import io.novafoundation.nova.core_db.model.BrowserTabLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
abstract class BrowserTabsDao {
|
||||
|
||||
@Query("SELECT id FROM browser_tabs WHERE metaId = :metaId")
|
||||
abstract fun getTabIdsFor(metaId: Long): List<String>
|
||||
|
||||
@Query("SELECT * FROM browser_tabs WHERE metaId = :metaId ORDER BY creationTime DESC")
|
||||
abstract fun observeTabsByMetaId(metaId: Long): Flow<List<BrowserTabLocal>>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertTab(tab: BrowserTabLocal)
|
||||
|
||||
@Transaction
|
||||
open suspend fun removeTabsByMetaId(metaId: Long): List<String> {
|
||||
val tabIds = getTabIdsFor(metaId)
|
||||
removeTabsByIds(tabIds)
|
||||
return tabIds
|
||||
}
|
||||
|
||||
@Query("DELETE FROM browser_tabs WHERE id = :tabId")
|
||||
abstract suspend fun removeTab(tabId: String)
|
||||
|
||||
@Query("DELETE FROM browser_tabs WHERE id IN (:tabIds)")
|
||||
abstract suspend fun removeTabsByIds(tabIds: List<String>)
|
||||
|
||||
@Query("UPDATE browser_tabs SET pageName = :pageName, pageIconPath = :pageIconPath, pagePicturePath = :pagePicturePath WHERE id = :tabId")
|
||||
abstract suspend fun updatePageSnapshot(tabId: String, pageName: String?, pageIconPath: String?, pagePicturePath: String?)
|
||||
|
||||
@Query("UPDATE browser_tabs SET currentUrl = :url WHERE id = :tabId")
|
||||
abstract fun updateCurrentUrl(tabId: String, url: String)
|
||||
|
||||
@Query("UPDATE browser_tabs SET dappMetadata_iconLink = :dappIconUrl WHERE id = :tabId")
|
||||
abstract fun updateKnownDAppMetadata(tabId: String, dappIconUrl: String?)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import io.novafoundation.nova.common.utils.CollectionDiffer
|
||||
import io.novafoundation.nova.core_db.model.chain.AssetSourceLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainAssetLocal
|
||||
|
||||
typealias FullAssetIdLocal = Pair<String, Int>
|
||||
|
||||
@Dao
|
||||
abstract class ChainAssetDao {
|
||||
|
||||
@Transaction
|
||||
open suspend fun updateAssets(diff: CollectionDiffer.Diff<ChainAssetLocal>) {
|
||||
insertAssets(diff.newOrUpdated)
|
||||
deleteChainAssets(diff.removed)
|
||||
}
|
||||
|
||||
@Query("SELECT * FROM chain_assets WHERE id = :id AND chainId = :chainId")
|
||||
abstract suspend fun getAsset(id: Int, chainId: String): ChainAssetLocal?
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertAsset(asset: ChainAssetLocal)
|
||||
|
||||
@Query("SELECT * FROM chain_assets WHERE source = :source")
|
||||
abstract suspend fun getAssetsBySource(source: AssetSourceLocal): List<ChainAssetLocal>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
protected abstract suspend fun insertAssets(assets: List<ChainAssetLocal>)
|
||||
|
||||
@Query("UPDATE chain_assets SET enabled = :enabled WHERE chainId = :chainId AND id = :assetId")
|
||||
protected abstract suspend fun setAssetEnabled(enabled: Boolean, chainId: String, assetId: Int)
|
||||
|
||||
@Query("SELECT * FROM chain_assets WHERE enabled=1")
|
||||
abstract suspend fun getEnabledAssets(): List<ChainAssetLocal>
|
||||
|
||||
@Update(entity = ChainAssetLocal::class)
|
||||
abstract suspend fun setAssetsEnabled(params: List<SetAssetEnabledParams>)
|
||||
|
||||
@Delete
|
||||
protected abstract suspend fun deleteChainAssets(assets: List<ChainAssetLocal>)
|
||||
}
|
||||
|
||||
class SetAssetEnabledParams(val enabled: Boolean, val chainId: String, val id: Int)
|
||||
@@ -0,0 +1,245 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import io.novafoundation.nova.common.utils.CollectionDiffer
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainAssetLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainExplorerLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainExternalApiLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainNodeLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainRuntimeInfoLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.JoinedChainInfo
|
||||
import io.novafoundation.nova.core_db.model.chain.NodeSelectionPreferencesLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
abstract class ChainDao {
|
||||
|
||||
@Transaction
|
||||
open suspend fun applyDiff(
|
||||
chainDiff: CollectionDiffer.Diff<ChainLocal>,
|
||||
assetsDiff: CollectionDiffer.Diff<ChainAssetLocal>,
|
||||
nodesDiff: CollectionDiffer.Diff<ChainNodeLocal>,
|
||||
explorersDiff: CollectionDiffer.Diff<ChainExplorerLocal>,
|
||||
externalApisDiff: CollectionDiffer.Diff<ChainExternalApiLocal>,
|
||||
nodeSelectionPreferencesDiff: CollectionDiffer.Diff<NodeSelectionPreferencesLocal>
|
||||
) {
|
||||
deleteChains(chainDiff.removed)
|
||||
deleteChainAssets(assetsDiff.removed)
|
||||
deleteChainNodes(nodesDiff.removed)
|
||||
deleteChainExplorers(explorersDiff.removed)
|
||||
deleteExternalApis(externalApisDiff.removed)
|
||||
deleteNodePreferences(nodeSelectionPreferencesDiff.removed)
|
||||
|
||||
addChains(chainDiff.added)
|
||||
addChainAssets(assetsDiff.added)
|
||||
addChainNodes(nodesDiff.added)
|
||||
addChainExplorers(explorersDiff.added)
|
||||
addExternalApis(externalApisDiff.added)
|
||||
addNodePreferences(nodeSelectionPreferencesDiff.added)
|
||||
|
||||
updateChains(chainDiff.updated)
|
||||
updateChainAssets(assetsDiff.updated)
|
||||
updateChainNodes(nodesDiff.updated)
|
||||
updateChainExplorers(explorersDiff.updated)
|
||||
updateExternalApis(externalApisDiff.updated)
|
||||
updateNodePreferences(nodeSelectionPreferencesDiff.added)
|
||||
}
|
||||
|
||||
@Transaction
|
||||
open suspend fun addChainOrUpdate(
|
||||
chain: ChainLocal,
|
||||
assets: List<ChainAssetLocal>,
|
||||
nodes: List<ChainNodeLocal>,
|
||||
explorers: List<ChainExplorerLocal>,
|
||||
externalApis: List<ChainExternalApiLocal>,
|
||||
nodeSelectionPreferences: NodeSelectionPreferencesLocal
|
||||
) {
|
||||
addChainOrUpdate(chain)
|
||||
addChainAssetsOrUpdate(assets)
|
||||
addChainNodesOrUpdate(nodes)
|
||||
addChainExplorersOrUpdate(explorers)
|
||||
addExternalApisOrUpdate(externalApis)
|
||||
addNodePreferencesOrUpdate(nodeSelectionPreferences)
|
||||
}
|
||||
|
||||
@Transaction
|
||||
open suspend fun editChain(
|
||||
chainId: String,
|
||||
assetId: Int,
|
||||
chainName: String,
|
||||
symbol: String,
|
||||
explorer: ChainExplorerLocal?,
|
||||
priceId: String?
|
||||
) {
|
||||
updateChainName(chainId, chainName)
|
||||
updateAssetToken(chainId, assetId, symbol, priceId)
|
||||
addChainExplorersOrUpdate(listOfNotNull(explorer))
|
||||
}
|
||||
|
||||
// ------ Delete --------
|
||||
@Delete
|
||||
protected abstract suspend fun deleteChains(chains: List<ChainLocal>)
|
||||
|
||||
@Delete
|
||||
protected abstract suspend fun deleteChainNodes(nodes: List<ChainNodeLocal>)
|
||||
|
||||
@Delete
|
||||
protected abstract suspend fun deleteChainAssets(assets: List<ChainAssetLocal>)
|
||||
|
||||
@Delete
|
||||
protected abstract suspend fun deleteChainExplorers(explorers: List<ChainExplorerLocal>)
|
||||
|
||||
@Delete
|
||||
protected abstract suspend fun deleteExternalApis(apis: List<ChainExternalApiLocal>)
|
||||
|
||||
@Delete
|
||||
protected abstract suspend fun deleteNodePreferences(apis: List<NodeSelectionPreferencesLocal>)
|
||||
|
||||
// ------ Add --------
|
||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||
protected abstract suspend fun addChains(chains: List<ChainLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||
protected abstract suspend fun addChainNodes(nodes: List<ChainNodeLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||
protected abstract suspend fun addChainAssets(assets: List<ChainAssetLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||
protected abstract suspend fun addChainExplorers(explorers: List<ChainExplorerLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||
protected abstract suspend fun addExternalApis(apis: List<ChainExternalApiLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun addChainOrUpdate(node: ChainLocal)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||
abstract suspend fun addChainNode(node: ChainNodeLocal)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||
protected abstract suspend fun addNodePreferences(model: List<NodeSelectionPreferencesLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
protected abstract suspend fun addNodePreferencesOrUpdate(model: NodeSelectionPreferencesLocal)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
protected abstract suspend fun addChainNodesOrUpdate(nodes: List<ChainNodeLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
protected abstract suspend fun addChainAssetsOrUpdate(assets: List<ChainAssetLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
protected abstract suspend fun addChainExplorersOrUpdate(explorers: List<ChainExplorerLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
protected abstract suspend fun addExternalApisOrUpdate(apis: List<ChainExternalApiLocal>)
|
||||
|
||||
// ------ Update -----
|
||||
|
||||
@Update
|
||||
protected abstract suspend fun updateChains(chains: List<ChainLocal>)
|
||||
|
||||
@Update
|
||||
protected abstract suspend fun updateChainNodes(nodes: List<ChainNodeLocal>)
|
||||
|
||||
@Update
|
||||
protected abstract suspend fun updateChainAssets(assets: List<ChainAssetLocal>)
|
||||
|
||||
@Update
|
||||
protected abstract suspend fun updateChainExplorers(explorers: List<ChainExplorerLocal>)
|
||||
|
||||
@Update
|
||||
protected abstract suspend fun updateExternalApis(apis: List<ChainExternalApiLocal>)
|
||||
|
||||
@Update
|
||||
protected abstract suspend fun updateNodePreferences(apis: List<NodeSelectionPreferencesLocal>)
|
||||
|
||||
// ------- Queries ------
|
||||
|
||||
@Query("SELECT * FROM chains")
|
||||
@Transaction
|
||||
abstract suspend fun getJoinChainInfo(): List<JoinedChainInfo>
|
||||
|
||||
@Query("SELECT id FROM chains")
|
||||
@Transaction
|
||||
abstract suspend fun getAllChainIds(): List<String>
|
||||
|
||||
@Query("SELECT * FROM chains")
|
||||
@Transaction
|
||||
abstract fun joinChainInfoFlow(): Flow<List<JoinedChainInfo>>
|
||||
|
||||
@Query("SELECT orderId FROM chain_nodes WHERE chainId = :chainId ORDER BY orderId DESC LIMIT 1")
|
||||
abstract suspend fun getLastChainNodeOrderId(chainId: String): Int
|
||||
|
||||
@Query("SELECT EXISTS(SELECT * FROM chains WHERE id = :chainId)")
|
||||
abstract suspend fun chainExists(chainId: String): Boolean
|
||||
|
||||
@Query("SELECT * FROM chain_runtimes WHERE chainId = :chainId")
|
||||
abstract suspend fun runtimeInfo(chainId: String): ChainRuntimeInfoLocal?
|
||||
|
||||
@Query("SELECT * FROM chain_runtimes")
|
||||
abstract suspend fun allRuntimeInfos(): List<ChainRuntimeInfoLocal>
|
||||
|
||||
@Query("UPDATE chain_runtimes SET syncedVersion = :syncedVersion, localMigratorVersion = :localMigratorVersion WHERE chainId = :chainId")
|
||||
abstract suspend fun updateSyncedRuntimeVersion(chainId: String, syncedVersion: Int, localMigratorVersion: Int)
|
||||
|
||||
@Query("UPDATE chains SET connectionState = :connectionState WHERE id = :chainId")
|
||||
abstract suspend fun setConnectionState(chainId: String, connectionState: ChainLocal.ConnectionStateLocal)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun setNodePreferences(model: NodeSelectionPreferencesLocal)
|
||||
|
||||
@Transaction
|
||||
open suspend fun updateRemoteRuntimeVersionIfChainExists(
|
||||
chainId: String,
|
||||
runtimeVersion: Int,
|
||||
transactionVersion: Int,
|
||||
) {
|
||||
if (!chainExists(chainId)) return
|
||||
|
||||
if (isRuntimeInfoExists(chainId)) {
|
||||
updateRemoteRuntimeVersionUnsafe(chainId, runtimeVersion, transactionVersion)
|
||||
} else {
|
||||
val runtimeInfoLocal = ChainRuntimeInfoLocal(
|
||||
chainId,
|
||||
syncedVersion = 0,
|
||||
remoteVersion = runtimeVersion,
|
||||
transactionVersion = transactionVersion,
|
||||
localMigratorVersion = 1
|
||||
)
|
||||
insertRuntimeInfo(runtimeInfoLocal)
|
||||
}
|
||||
}
|
||||
|
||||
@Query("UPDATE chain_nodes SET url = :newUrl, name = :name WHERE chainId = :chainId AND url = :oldUrl")
|
||||
abstract suspend fun updateChainNode(chainId: String, oldUrl: String, newUrl: String, name: String)
|
||||
|
||||
@Query("UPDATE chain_runtimes SET remoteVersion = :remoteVersion, transactionVersion = :transactionVersion WHERE chainId = :chainId")
|
||||
protected abstract suspend fun updateRemoteRuntimeVersionUnsafe(chainId: String, remoteVersion: Int, transactionVersion: Int)
|
||||
|
||||
@Query("SELECT EXISTS (SELECT * FROM chain_runtimes WHERE chainId = :chainId)")
|
||||
protected abstract suspend fun isRuntimeInfoExists(chainId: String): Boolean
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
protected abstract suspend fun insertRuntimeInfo(runtimeInfoLocal: ChainRuntimeInfoLocal)
|
||||
|
||||
@Query("UPDATE chains SET name = :name WHERE id = :chainId")
|
||||
abstract suspend fun updateChainName(chainId: String, name: String)
|
||||
|
||||
@Query("UPDATE chain_assets SET symbol = :symbol, priceId = :priceId WHERE chainId = :chainId and id == :assetId")
|
||||
abstract suspend fun updateAssetToken(chainId: String, assetId: Int, symbol: String, priceId: String?)
|
||||
|
||||
@Query("DELETE FROM chains WHERE id = :chainId")
|
||||
abstract suspend fun deleteChain(chainId: String)
|
||||
|
||||
@Query("DELETE FROM chain_nodes WHERE chainId = :chainId AND url = :url")
|
||||
abstract suspend fun deleteNode(chainId: String, url: String)
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import io.novafoundation.nova.core_db.model.CoinPriceLocal
|
||||
|
||||
@Dao
|
||||
interface CoinPriceDao {
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT * FROM coin_prices
|
||||
WHERE priceId = :priceId AND currencyId = :currencyId
|
||||
AND timestamp <= :timestamp
|
||||
ORDER BY timestamp DESC LIMIT 1
|
||||
"""
|
||||
)
|
||||
suspend fun getFloorCoinPriceAtTime(priceId: String, currencyId: String, timestamp: Long): CoinPriceLocal?
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT EXISTS(
|
||||
SELECT * FROM coin_prices
|
||||
WHERE priceId = :priceId AND currencyId = :currencyId
|
||||
AND timestamp >= :timestamp
|
||||
ORDER BY timestamp ASC LIMIT 1
|
||||
)
|
||||
"""
|
||||
)
|
||||
suspend fun hasCeilingCoinPriceAtTime(priceId: String, currencyId: String, timestamp: Long): Boolean
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT * FROM coin_prices
|
||||
WHERE priceId = :priceId AND currencyId = :currencyId
|
||||
AND timestamp BETWEEN :fromTimestamp AND :toTimestamp
|
||||
ORDER BY timestamp ASC
|
||||
"""
|
||||
)
|
||||
suspend fun getCoinPriceRange(priceId: String, currencyId: String, fromTimestamp: Long, toTimestamp: Long): List<CoinPriceLocal>
|
||||
|
||||
@Transaction
|
||||
suspend fun updateCoinPrices(priceId: String, currencyId: String, coinRates: List<CoinPriceLocal>) {
|
||||
deleteCoinPrices(priceId, currencyId)
|
||||
setCoinPrices(coinRates)
|
||||
}
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun setCoinPrices(coinPrices: List<CoinPriceLocal>)
|
||||
|
||||
@Query(
|
||||
"""
|
||||
DELETE FROM coin_prices
|
||||
WHERE priceId = :priceId AND currencyId = :currencyId
|
||||
"""
|
||||
)
|
||||
fun deleteCoinPrices(priceId: String, currencyId: String)
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import io.novafoundation.nova.common.utils.CollectionDiffer
|
||||
import io.novafoundation.nova.core_db.model.ContributionLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
abstract class ContributionDao {
|
||||
|
||||
@Transaction
|
||||
open suspend fun updateContributions(contributions: CollectionDiffer.Diff<ContributionLocal>) {
|
||||
insertContributions(contributions.added)
|
||||
updateContributions(contributions.updated)
|
||||
deleteContributions(contributions.removed)
|
||||
}
|
||||
|
||||
@Query("SELECT * FROM contributions WHERE metaId = :metaId AND chainId = :chainId AND assetId = :assetId")
|
||||
abstract fun observeContributions(metaId: Long, chainId: String, assetId: Int): Flow<List<ContributionLocal>>
|
||||
|
||||
@Query("SELECT * FROM contributions WHERE metaId = :metaId")
|
||||
abstract fun observeContributions(metaId: Long): Flow<List<ContributionLocal>>
|
||||
|
||||
@Query("SELECT * FROM contributions WHERE metaId = :metaId AND chainId = :chainId AND assetId = :assetId AND sourceId = :sourceId")
|
||||
abstract suspend fun getContributions(metaId: Long, chainId: String, assetId: Int, sourceId: String): List<ContributionLocal>
|
||||
|
||||
@Query("DELETE FROM contributions WHERE chainId = :chainId AND assetId = :assetId")
|
||||
abstract suspend fun deleteContributions(chainId: String, assetId: Int)
|
||||
|
||||
@Delete
|
||||
protected abstract suspend fun deleteContributions(contributions: List<ContributionLocal>)
|
||||
|
||||
@Update(onConflict = OnConflictStrategy.REPLACE)
|
||||
protected abstract suspend fun updateContributions(contributions: List<ContributionLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
protected abstract suspend fun insertContributions(contributions: List<ContributionLocal>)
|
||||
|
||||
@Delete(entity = ContributionLocal::class)
|
||||
abstract suspend fun deleteAssetContributions(params: List<DeleteAssetContributionsParams>)
|
||||
}
|
||||
|
||||
class DeleteAssetContributionsParams(val chainId: String, val assetId: Int)
|
||||
@@ -0,0 +1,61 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import io.novafoundation.nova.common.utils.CollectionDiffer
|
||||
import io.novafoundation.nova.core_db.model.CurrencyLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
private const val RETRIEVE_CURRENCIES = "SELECT * FROM currencies"
|
||||
|
||||
private const val RETRIEVE_SELECTED_CURRENCY = "SELECT * FROM currencies WHERE selected = 1"
|
||||
|
||||
@Dao
|
||||
abstract class CurrencyDao {
|
||||
|
||||
@Transaction
|
||||
open suspend fun updateCurrencies(currencies: CollectionDiffer.Diff<CurrencyLocal>) {
|
||||
deleteCurrencies(currencies.removed)
|
||||
insertCurrencies(currencies.added)
|
||||
updateCurrencies(currencies.updated)
|
||||
|
||||
if (getSelectedCurrency() == null) {
|
||||
selectCurrency(0)
|
||||
}
|
||||
}
|
||||
|
||||
@Query("SELECT * FROM currencies WHERE id = 0")
|
||||
abstract fun getFirst(): CurrencyLocal
|
||||
|
||||
@Query(RETRIEVE_CURRENCIES)
|
||||
abstract suspend fun getCurrencies(): List<CurrencyLocal>
|
||||
|
||||
@Query(RETRIEVE_CURRENCIES)
|
||||
abstract fun observeCurrencies(): Flow<List<CurrencyLocal>>
|
||||
|
||||
@Query(RETRIEVE_SELECTED_CURRENCY)
|
||||
abstract suspend fun getSelectedCurrency(): CurrencyLocal?
|
||||
|
||||
@Query(RETRIEVE_SELECTED_CURRENCY)
|
||||
abstract fun observeSelectCurrency(): Flow<CurrencyLocal>
|
||||
|
||||
@Query("UPDATE currencies SET selected = (id = :currencyId)")
|
||||
abstract fun selectCurrency(currencyId: Int)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||
abstract suspend fun insert(currency: CurrencyLocal)
|
||||
|
||||
@Delete
|
||||
protected abstract suspend fun deleteCurrencies(currencies: List<CurrencyLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||
protected abstract suspend fun insertCurrencies(currencies: List<CurrencyLocal>)
|
||||
|
||||
@Update
|
||||
protected abstract suspend fun updateCurrencies(currencies: List<CurrencyLocal>)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import io.novafoundation.nova.core_db.model.DappAuthorizationLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface DappAuthorizationDao {
|
||||
|
||||
@Query("SELECT * FROM dapp_authorizations WHERE baseUrl = :baseUrl AND metaId = :metaId")
|
||||
suspend fun getAuthorization(baseUrl: String, metaId: Long): DappAuthorizationLocal?
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun updateAuthorization(dappAuthorization: DappAuthorizationLocal)
|
||||
|
||||
@Query("UPDATE dapp_authorizations SET authorized = 0 WHERE baseUrl = :baseUrl AND metaId = :metaId")
|
||||
suspend fun removeAuthorization(baseUrl: String, metaId: Long)
|
||||
|
||||
@Query("SELECT * FROM dapp_authorizations WHERE metaId = :metaId")
|
||||
fun observeAuthorizations(metaId: Long): Flow<List<DappAuthorizationLocal>>
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import io.novafoundation.nova.core_db.model.AggregatedExternalBalanceLocal
|
||||
import io.novafoundation.nova.core_db.model.ExternalBalanceLocal
|
||||
import io.novasama.substrate_sdk_android.hash.isPositive
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface ExternalBalanceDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertExternalBalance(externalBalance: ExternalBalanceLocal)
|
||||
|
||||
@Query("DELETE FROM externalBalances WHERE metaId = :metaId AND chainId = :chainId AND assetId = :assetId AND type = :type AND subtype = :subtype")
|
||||
suspend fun removeExternalBalance(
|
||||
metaId: Long,
|
||||
chainId: String,
|
||||
assetId: Int,
|
||||
type: ExternalBalanceLocal.Type,
|
||||
subtype: String?,
|
||||
)
|
||||
|
||||
@Delete(entity = ExternalBalanceLocal::class)
|
||||
suspend fun deleteAssetExternalBalances(params: List<ExternalBalanceAssetDeleteParams>)
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT chainId, assetId, type, SUM(amount) as aggregatedAmount
|
||||
FROM externalBalances
|
||||
WHERE metaId = :metaId
|
||||
GROUP BY chainId, assetId, type
|
||||
"""
|
||||
)
|
||||
fun observeAggregatedExternalBalances(metaId: Long): Flow<List<AggregatedExternalBalanceLocal>>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT chainId, assetId, type, SUM(amount) as aggregatedAmount
|
||||
FROM externalBalances
|
||||
WHERE metaId = :metaId AND chainId = :chainId AND assetId = :assetId
|
||||
GROUP BY type
|
||||
"""
|
||||
)
|
||||
fun observeChainAggregatedExternalBalances(metaId: Long, chainId: String, assetId: Int): Flow<List<AggregatedExternalBalanceLocal>>
|
||||
}
|
||||
|
||||
suspend fun ExternalBalanceDao.updateExternalBalance(externalBalance: ExternalBalanceLocal) {
|
||||
if (externalBalance.amount.isPositive()) {
|
||||
insertExternalBalance(externalBalance)
|
||||
} else {
|
||||
removeExternalBalance(
|
||||
metaId = externalBalance.metaId,
|
||||
chainId = externalBalance.chainId,
|
||||
assetId = externalBalance.assetId,
|
||||
type = externalBalance.type,
|
||||
subtype = externalBalance.subtype
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ExternalBalanceAssetDeleteParams(val chainId: String, val assetId: Int)
|
||||
@@ -0,0 +1,34 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import io.novafoundation.nova.core_db.model.FavouriteDAppLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface FavouriteDAppsDao {
|
||||
|
||||
@Query("SELECT * FROM favourite_dapps")
|
||||
fun observeFavouriteDApps(): Flow<List<FavouriteDAppLocal>>
|
||||
|
||||
@Query("SELECT * FROM favourite_dapps")
|
||||
suspend fun getFavouriteDApps(): List<FavouriteDAppLocal>
|
||||
|
||||
@Query("SELECT EXISTS(SELECT * FROM favourite_dapps WHERE url = :dAppUrl)")
|
||||
fun observeIsFavourite(dAppUrl: String): Flow<Boolean>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertFavouriteDApp(dApp: FavouriteDAppLocal)
|
||||
|
||||
@Query("DELETE FROM favourite_dapps WHERE url = :dAppUrl")
|
||||
suspend fun deleteFavouriteDApp(dAppUrl: String)
|
||||
|
||||
@Update(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun updateFavourites(dapps: List<FavouriteDAppLocal>)
|
||||
|
||||
@Query("SELECT MAX(orderingIndex) FROM favourite_dapps")
|
||||
suspend fun getMaxOrderingIndex(): Int
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import io.novafoundation.nova.core_db.model.GiftLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface GiftsDao {
|
||||
|
||||
@Query("SELECT * from gifts WHERE id = :id")
|
||||
suspend fun getGiftById(id: Long): GiftLocal
|
||||
|
||||
@Query("SELECT * from gifts")
|
||||
suspend fun getAllGifts(): List<GiftLocal>
|
||||
|
||||
@Query("SELECT * from gifts WHERE id = :id")
|
||||
fun observeGiftById(id: Long): Flow<GiftLocal>
|
||||
|
||||
@Query("SELECT * from gifts")
|
||||
fun observeAllGifts(): Flow<List<GiftLocal>>
|
||||
|
||||
@Query("SELECT * from gifts WHERE chainId = :chainId AND assetId = :assetId")
|
||||
fun observeGiftsByAsset(chainId: String, assetId: Int): Flow<List<GiftLocal>>
|
||||
|
||||
@Insert
|
||||
suspend fun createNewGift(giftLocal: GiftLocal): Long
|
||||
|
||||
@Query("UPDATE gifts SET status = :status WHERE id = :id")
|
||||
suspend fun setGiftState(id: Long, status: GiftLocal.Status)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import io.novafoundation.nova.common.utils.CollectionDiffer
|
||||
import io.novafoundation.nova.core_db.model.GovernanceDAppLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
abstract class GovernanceDAppsDao {
|
||||
|
||||
@Transaction
|
||||
open suspend fun update(newDapps: List<GovernanceDAppLocal>) {
|
||||
val oldDapps = getAll()
|
||||
val dappDiffs = CollectionDiffer.findDiff(newDapps, oldDapps, false)
|
||||
|
||||
deleteDapps(dappDiffs.removed)
|
||||
updateDapps(dappDiffs.updated)
|
||||
insertDapps(dappDiffs.added)
|
||||
}
|
||||
|
||||
@Query("SELECT * FROM governance_dapps")
|
||||
abstract fun getAll(): List<GovernanceDAppLocal>
|
||||
|
||||
@Query("SELECT * FROM governance_dapps WHERE chainId = :chainId")
|
||||
abstract fun observeChainDapps(chainId: String): Flow<List<GovernanceDAppLocal>>
|
||||
|
||||
@Delete
|
||||
abstract suspend fun deleteDapps(dapps: List<GovernanceDAppLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||
abstract suspend fun insertDapps(dapps: List<GovernanceDAppLocal>)
|
||||
|
||||
@Update
|
||||
abstract suspend fun updateDapps(dapps: List<GovernanceDAppLocal>)
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import io.novafoundation.nova.core_db.model.BalanceHoldLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
abstract class HoldsDao {
|
||||
|
||||
@Transaction
|
||||
open suspend fun updateHolds(
|
||||
holds: List<BalanceHoldLocal>,
|
||||
metaId: Long,
|
||||
chainId: String,
|
||||
chainAssetId: Int
|
||||
) {
|
||||
deleteHolds(metaId, chainId, chainAssetId)
|
||||
|
||||
insert(holds)
|
||||
}
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
protected abstract fun insert(holds: List<BalanceHoldLocal>)
|
||||
|
||||
@Query("DELETE FROM holds WHERE metaId = :metaId AND chainId = :chainId AND assetId = :chainAssetId")
|
||||
protected abstract fun deleteHolds(metaId: Long, chainId: String, chainAssetId: Int)
|
||||
|
||||
@Query("SELECT * FROM holds WHERE metaId = :metaId")
|
||||
abstract fun observeHoldsForMetaAccount(metaId: Long): Flow<List<BalanceHoldLocal>>
|
||||
|
||||
@Query("SELECT * FROM holds WHERE metaId = :metaId AND chainId = :chainId AND assetId = :chainAssetId")
|
||||
abstract fun observeBalanceHolds(metaId: Long, chainId: String, chainAssetId: Int): Flow<List<BalanceHoldLocal>>
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import io.novafoundation.nova.core_db.model.BalanceLockLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
abstract class LockDao {
|
||||
|
||||
@Transaction
|
||||
open suspend fun updateLocks(
|
||||
locks: List<BalanceLockLocal>,
|
||||
metaId: Long,
|
||||
chainId: String,
|
||||
chainAssetId: Int
|
||||
) {
|
||||
deleteLocks(metaId, chainId, chainAssetId)
|
||||
|
||||
insert(locks)
|
||||
}
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract fun insert(locks: List<BalanceLockLocal>)
|
||||
|
||||
@Query("DELETE FROM locks WHERE metaId = :metaId AND chainId = :chainId AND assetId = :chainAssetId")
|
||||
abstract fun deleteLocks(metaId: Long, chainId: String, chainAssetId: Int)
|
||||
|
||||
@Query("SELECT * FROM locks WHERE metaId = :metaId")
|
||||
abstract fun observeLocksForMetaAccount(metaId: Long): Flow<List<BalanceLockLocal>>
|
||||
|
||||
@Query("SELECT * FROM locks WHERE metaId = :metaId AND chainId = :chainId AND assetId = :chainAssetId")
|
||||
abstract fun observeBalanceLocks(metaId: Long, chainId: String, chainAssetId: Int): Flow<List<BalanceLockLocal>>
|
||||
|
||||
@Query("SELECT * FROM locks WHERE metaId = :metaId AND chainId = :chainId AND assetId = :chainAssetId")
|
||||
abstract suspend fun getBalanceLocks(metaId: Long, chainId: String, chainAssetId: Int): List<BalanceLockLocal>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT * FROM locks
|
||||
WHERE metaId = :metaId AND chainId = :chainId AND assetId = :chainAssetId
|
||||
ORDER BY amount DESC
|
||||
LIMIT 1
|
||||
"""
|
||||
)
|
||||
abstract suspend fun getBiggestBalanceLock(metaId: Long, chainId: String, chainAssetId: Int): BalanceLockLocal?
|
||||
|
||||
@Query("SELECT * FROM locks WHERE metaId = :metaId AND chainId = :chainId AND assetId = :chainAssetId AND type = :lockId")
|
||||
abstract fun observeBalanceLock(metaId: Long, chainId: String, chainAssetId: Int, lockId: String): Flow<BalanceLockLocal?>
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import io.novafoundation.nova.core_db.model.chain.account.ChainAccountLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.account.MetaAccountIdWithType
|
||||
import io.novafoundation.nova.core_db.model.chain.account.MetaAccountLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.account.MetaAccountPositionUpdate
|
||||
import io.novafoundation.nova.core_db.model.chain.account.ProxyAccountLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.account.RelationJoinedMetaAccountInfo
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.intellij.lang.annotations.Language
|
||||
import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
/**
|
||||
* Fetch meta account where either
|
||||
* 1. chain account for specified chain is present and its accountId matches
|
||||
* 2. chain account for specified is missing but one of base accountIds matches
|
||||
*
|
||||
* Note that if both chain account and base accounts are present than we should filter out entries where chain account matches but base accounts does not
|
||||
*/
|
||||
@Language("RoomSql")
|
||||
private const val FIND_BY_ADDRESS_WHERE_CLAUSE = """
|
||||
LEFT JOIN chain_accounts as c ON m.id = c.metaId AND c.chainId = :chainId
|
||||
WHERE
|
||||
(c.accountId IS NOT NULL AND c.accountId = :accountId)
|
||||
OR (c.accountId IS NULL AND (substrateAccountId = :accountId OR ethereumAddress = :accountId))
|
||||
ORDER BY (CASE WHEN isSelected THEN 0 ELSE 1 END)
|
||||
"""
|
||||
|
||||
@Language("RoomSql")
|
||||
private const val FIND_ACCOUNT_BY_ADDRESS_QUERY = """
|
||||
SELECT * FROM meta_accounts as m
|
||||
$FIND_BY_ADDRESS_WHERE_CLAUSE
|
||||
"""
|
||||
|
||||
@Language("RoomSql")
|
||||
private const val FIND_NAME_BY_ADDRESS_QUERY = """
|
||||
SELECT name FROM meta_accounts as m
|
||||
$FIND_BY_ADDRESS_WHERE_CLAUSE
|
||||
"""
|
||||
|
||||
@Language("RoomSql")
|
||||
private const val META_ACCOUNTS_WITH_BALANCE_PART = """
|
||||
SELECT
|
||||
m.id,
|
||||
a.freeInPlanks,
|
||||
a.reservedInPlanks,
|
||||
(SELECT SUM(amountInPlanks) FROM contributions WHERE chainId = a.chainId AND assetId = a.assetId AND metaId = m.id) offChainBalance,
|
||||
ca.precision,
|
||||
t.rate
|
||||
FROM meta_accounts as m
|
||||
INNER JOIN assets as a ON a.metaId = m.id
|
||||
INNER JOIN chain_assets AS ca ON a.assetId = ca.id AND a.chainId = ca.chainId
|
||||
INNER JOIN currencies as currency ON currency.selected = 1
|
||||
INNER JOIN tokens as t ON t.tokenSymbol = ca.symbol AND t.currencyId = currency.id
|
||||
"""
|
||||
|
||||
@Language("RoomSql")
|
||||
private const val META_ACCOUNTS_WITH_BALANCE_QUERY = """
|
||||
$META_ACCOUNTS_WITH_BALANCE_PART
|
||||
ORDER BY m.position
|
||||
"""
|
||||
|
||||
@Language("RoomSql")
|
||||
private const val META_ACCOUNT_WITH_BALANCE_QUERY = """
|
||||
$META_ACCOUNTS_WITH_BALANCE_PART
|
||||
WHERE m.id == :metaId
|
||||
"""
|
||||
|
||||
@Dao
|
||||
interface MetaAccountDao {
|
||||
|
||||
@Transaction
|
||||
suspend fun insertProxiedMetaAccount(
|
||||
metaAccount: MetaAccountLocal,
|
||||
chainAccount: (metaId: Long) -> ChainAccountLocal,
|
||||
proxyAccount: (metaId: Long) -> ProxyAccountLocal
|
||||
): Long {
|
||||
val metaId = insertMetaAccount(metaAccount)
|
||||
insertChainAccount(chainAccount(metaId))
|
||||
insertProxy(proxyAccount(metaId))
|
||||
|
||||
return metaId
|
||||
}
|
||||
|
||||
@Transaction
|
||||
suspend fun runInTransaction(action: suspend () -> Unit) {
|
||||
action()
|
||||
}
|
||||
|
||||
@Insert
|
||||
suspend fun insertMetaAccount(metaAccount: MetaAccountLocal): Long
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun updateMetaAccount(metaAccount: MetaAccountLocal): Long
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertChainAccount(chainAccount: ChainAccountLocal)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertChainAccounts(chainAccounts: List<ChainAccountLocal>)
|
||||
|
||||
@Delete
|
||||
suspend fun deleteChainAccounts(chainAccounts: List<ChainAccountLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertProxy(proxyLocal: ProxyAccountLocal)
|
||||
|
||||
@Query("SELECT * FROM meta_accounts")
|
||||
suspend fun getMetaAccounts(): List<MetaAccountLocal>
|
||||
|
||||
@Query("SELECT * FROM meta_accounts WHERE id IN (:metaIds)")
|
||||
suspend fun getMetaAccountsByIds(metaIds: List<Long>): List<RelationJoinedMetaAccountInfo>
|
||||
|
||||
@Query("SELECT * FROM meta_accounts WHERE id = :id")
|
||||
suspend fun getMetaAccount(id: Long): MetaAccountLocal?
|
||||
|
||||
@Query("SELECT COUNT(*) FROM meta_accounts WHERE status = :status")
|
||||
@Transaction
|
||||
suspend fun getMetaAccountsQuantityByStatus(status: MetaAccountLocal.Status): Int
|
||||
|
||||
@Query("SELECT * FROM meta_accounts WHERE status = :status")
|
||||
@Transaction
|
||||
suspend fun getMetaAccountsByStatus(status: MetaAccountLocal.Status): List<RelationJoinedMetaAccountInfo>
|
||||
|
||||
@Query("SELECT * FROM meta_accounts")
|
||||
@Transaction
|
||||
suspend fun getFullMetaAccounts(): List<RelationJoinedMetaAccountInfo>
|
||||
|
||||
@Query("SELECT * FROM meta_accounts")
|
||||
fun getJoinedMetaAccountsInfoFlow(): Flow<List<RelationJoinedMetaAccountInfo>>
|
||||
|
||||
@Query("SELECT * FROM meta_accounts WHERE status = :status")
|
||||
fun getJoinedMetaAccountsInfoByStatusFlow(status: MetaAccountLocal.Status): Flow<List<RelationJoinedMetaAccountInfo>>
|
||||
|
||||
@Query("SELECT id FROM meta_accounts WHERE status = :status")
|
||||
fun getMetaAccountsIdsByStatus(status: MetaAccountLocal.Status): List<Long>
|
||||
|
||||
@Query(META_ACCOUNTS_WITH_BALANCE_QUERY)
|
||||
fun metaAccountsWithBalanceFlow(): Flow<List<MetaAccountWithBalanceLocal>>
|
||||
|
||||
@Query(META_ACCOUNT_WITH_BALANCE_QUERY)
|
||||
fun metaAccountWithBalanceFlow(metaId: Long): Flow<List<MetaAccountWithBalanceLocal>>
|
||||
|
||||
@Query("UPDATE meta_accounts SET isSelected = (id = :metaId)")
|
||||
suspend fun selectMetaAccount(metaId: Long)
|
||||
|
||||
@Update(entity = MetaAccountLocal::class)
|
||||
suspend fun updatePositions(updates: List<MetaAccountPositionUpdate>)
|
||||
|
||||
@Query("SELECT * FROM meta_accounts WHERE id = :metaId")
|
||||
@Transaction
|
||||
suspend fun getJoinedMetaAccountInfo(metaId: Long): RelationJoinedMetaAccountInfo
|
||||
|
||||
@Query("SELECT type FROM meta_accounts WHERE id = :metaId")
|
||||
suspend fun getMetaAccountType(metaId: Long): MetaAccountLocal.Type?
|
||||
|
||||
@Query("SELECT * FROM meta_accounts WHERE isSelected = 1")
|
||||
@Transaction
|
||||
fun selectedMetaAccountInfoFlow(): Flow<RelationJoinedMetaAccountInfo?>
|
||||
|
||||
@Query("SELECT * FROM meta_accounts WHERE id = :metaId")
|
||||
@Transaction
|
||||
fun metaAccountInfoFlow(metaId: Long): Flow<RelationJoinedMetaAccountInfo?>
|
||||
|
||||
@Query("SELECT EXISTS ($FIND_ACCOUNT_BY_ADDRESS_QUERY)")
|
||||
fun isMetaAccountExists(accountId: AccountId, chainId: String): Boolean
|
||||
|
||||
@Query(FIND_ACCOUNT_BY_ADDRESS_QUERY)
|
||||
@Transaction
|
||||
fun getMetaAccountInfo(accountId: AccountId, chainId: String): RelationJoinedMetaAccountInfo?
|
||||
|
||||
@Query(FIND_NAME_BY_ADDRESS_QUERY)
|
||||
fun metaAccountNameFor(accountId: AccountId, chainId: String): String?
|
||||
|
||||
@Query("UPDATE meta_accounts SET name = :newName WHERE id = :metaId")
|
||||
suspend fun updateName(metaId: Long, newName: String)
|
||||
|
||||
@Query(
|
||||
"""
|
||||
WITH RECURSIVE accounts_to_delete AS (
|
||||
SELECT id, parentMetaId, type FROM meta_accounts WHERE id IN (:metaIds)
|
||||
UNION ALL
|
||||
SELECT m.id, m.parentMetaId, m.type
|
||||
FROM meta_accounts m
|
||||
JOIN accounts_to_delete r ON m.parentMetaId = r.id
|
||||
)
|
||||
SELECT id, type FROM accounts_to_delete
|
||||
"""
|
||||
)
|
||||
suspend fun findAffectedMetaIdsOnDelete(metaIds: List<Long>): List<MetaAccountIdWithType>
|
||||
|
||||
@Query("DELETE FROM meta_accounts WHERE id IN (:ids)")
|
||||
suspend fun deleteByIds(ids: List<Long>)
|
||||
|
||||
@Transaction
|
||||
suspend fun delete(vararg metaId: Long): List<MetaAccountIdWithType> {
|
||||
val affectingMetaAccounts = findAffectedMetaIdsOnDelete(metaId.toList())
|
||||
if (affectingMetaAccounts.isNotEmpty()) {
|
||||
val ids = affectingMetaAccounts.map { it.id }
|
||||
deleteByIds(ids)
|
||||
}
|
||||
return affectingMetaAccounts
|
||||
}
|
||||
|
||||
@Transaction
|
||||
suspend fun delete(metaIds: List<Long>): List<MetaAccountIdWithType> {
|
||||
return delete(*metaIds.toLongArray())
|
||||
}
|
||||
|
||||
@Query("SELECT COALESCE(MAX(position), 0) + 1 FROM meta_accounts")
|
||||
suspend fun nextAccountPosition(): Int
|
||||
|
||||
@Query("SELECT * FROM meta_accounts WHERE isSelected = 1")
|
||||
suspend fun selectedMetaAccount(): RelationJoinedMetaAccountInfo?
|
||||
|
||||
@Query("SELECT EXISTS(SELECT id FROM meta_accounts WHERE type = :type)")
|
||||
fun hasMetaAccountsCountOfTypeFlow(type: MetaAccountLocal.Type): Flow<Boolean>
|
||||
|
||||
@Query("SELECT * FROM meta_accounts WHERE type = :type")
|
||||
fun observeMetaAccountsByTypeFlow(type: MetaAccountLocal.Type): Flow<List<RelationJoinedMetaAccountInfo>>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
DELETE FROM meta_accounts
|
||||
WHERE id IN (
|
||||
SELECT proxiedMetaId
|
||||
FROM proxy_accounts
|
||||
WHERE chainId = :chainId
|
||||
)
|
||||
"""
|
||||
)
|
||||
fun deleteProxiedMetaAccountsByChain(chainId: String)
|
||||
|
||||
@Transaction
|
||||
suspend fun insertMetaAndChainAccounts(
|
||||
metaAccount: MetaAccountLocal,
|
||||
createChainAccounts: suspend (metaId: Long) -> List<ChainAccountLocal>
|
||||
): Long {
|
||||
val metaId = insertMetaAccount(metaAccount)
|
||||
|
||||
insertChainAccounts(createChainAccounts(metaId))
|
||||
|
||||
return metaId
|
||||
}
|
||||
|
||||
@Query("SELECT EXISTS(SELECT * FROM meta_accounts WHERE status = :status)")
|
||||
suspend fun hasMetaAccountsByStatus(status: MetaAccountLocal.Status): Boolean
|
||||
|
||||
@Query("SELECT EXISTS(SELECT * FROM meta_accounts WHERE type = :type)")
|
||||
suspend fun hasMetaAccountsByType(type: MetaAccountLocal.Type): Boolean
|
||||
|
||||
@Query("SELECT EXISTS(SELECT * FROM meta_accounts WHERE id IN (:metaIds) AND type = :type)")
|
||||
suspend fun hasMetaAccountsByType(metaIds: Set<Long>, type: MetaAccountLocal.Type): Boolean
|
||||
|
||||
@Query("UPDATE meta_accounts SET status = :status WHERE id IN (:metaIds)")
|
||||
suspend fun changeAccountsStatus(metaIds: List<Long>, status: MetaAccountLocal.Status)
|
||||
|
||||
@Query("DELETE FROM meta_accounts WHERE status = :status ")
|
||||
fun removeMetaAccountsByStatus(status: MetaAccountLocal.Status)
|
||||
}
|
||||
|
||||
suspend inline fun <T : Any> MetaAccountDao.withTransaction(crossinline action: suspend () -> T): T {
|
||||
var result: T? = null
|
||||
|
||||
runInTransaction {
|
||||
result = action()
|
||||
}
|
||||
|
||||
return result!!
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
suspend fun MetaAccountDao.updateMetaAccount(metaId: Long, updateClosure: (MetaAccountLocal) -> MetaAccountLocal) {
|
||||
contract {
|
||||
callsInPlace(updateClosure, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
|
||||
val metaAccount = requireNotNull(getMetaAccount(metaId)) {
|
||||
"Meta account $metaId was not found"
|
||||
}
|
||||
|
||||
val updated = updateClosure(metaAccount)
|
||||
require(updated.id == metaId) {
|
||||
"Cannot modify metaId"
|
||||
}
|
||||
|
||||
updateMetaAccount(updated)
|
||||
}
|
||||
|
||||
class MetaAccountWithBalanceLocal(
|
||||
val id: Long,
|
||||
val freeInPlanks: BigInteger,
|
||||
val reservedInPlanks: BigInteger,
|
||||
val offChainBalance: BigInteger?,
|
||||
val precision: Int,
|
||||
val rate: BigDecimal?
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import io.novafoundation.nova.core_db.model.MultisigOperationCallLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
abstract class MultisigOperationsDao {
|
||||
|
||||
@Query("SELECT * FROM multisig_operation_call")
|
||||
abstract fun observeOperations(): Flow<List<MultisigOperationCallLocal>>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertOperation(operation: MultisigOperationCallLocal)
|
||||
|
||||
@Query("DELETE FROM multisig_operation_call WHERE metaId = :metaId AND chainId = :chainId AND callHash NOT IN (:excludedCallHashes)")
|
||||
abstract fun removeOperationsExclude(metaId: Long, chainId: String, excludedCallHashes: List<String>)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import io.novafoundation.nova.common.utils.CollectionDiffer
|
||||
import io.novafoundation.nova.core_db.model.NftLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
abstract class NftDao {
|
||||
|
||||
@Query("SELECT * FROM nfts WHERE metaId = :metaId")
|
||||
abstract fun nftsFlow(metaId: Long): Flow<List<NftLocal>>
|
||||
|
||||
@Query("SELECT * FROM nfts WHERE metaId = :metaId AND type = :type AND chainId = :chainId")
|
||||
abstract suspend fun getNfts(chainId: String, metaId: Long, type: NftLocal.Type): List<NftLocal>
|
||||
|
||||
@Delete
|
||||
protected abstract suspend fun deleteNfts(nfts: List<NftLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
protected abstract suspend fun insertNfts(nfts: List<NftLocal>)
|
||||
|
||||
@Update
|
||||
protected abstract suspend fun updateNft(nft: NftLocal)
|
||||
|
||||
@Query("SELECT * FROM nfts WHERE identifier = :nftIdentifier")
|
||||
abstract suspend fun getNft(nftIdentifier: String): NftLocal
|
||||
|
||||
@Query("SELECT type FROM nfts WHERE identifier = :nftIdentifier")
|
||||
abstract suspend fun getNftType(nftIdentifier: String): NftLocal.Type
|
||||
|
||||
@Query("UPDATE nfts SET wholeDetailsLoaded = 1 WHERE identifier = :nftIdentifier")
|
||||
abstract suspend fun markFullSynced(nftIdentifier: String)
|
||||
|
||||
@Transaction
|
||||
open suspend fun insertNftsDiff(
|
||||
nftType: NftLocal.Type,
|
||||
chainId: String,
|
||||
metaId: Long,
|
||||
newNfts: List<NftLocal>,
|
||||
forceOverwrite: Boolean
|
||||
) {
|
||||
val oldNfts = getNfts(chainId, metaId, nftType)
|
||||
|
||||
val diff = CollectionDiffer.findDiff(newNfts, oldNfts, forceUseNewItems = forceOverwrite)
|
||||
|
||||
deleteNfts(diff.removed)
|
||||
insertNfts(diff.newOrUpdated)
|
||||
}
|
||||
|
||||
@Transaction
|
||||
open suspend fun updateNft(nftIdentifier: String, update: (NftLocal) -> NftLocal) {
|
||||
val nft = getNft(nftIdentifier)
|
||||
|
||||
val updated = update(nft)
|
||||
|
||||
updateNft(updated)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import io.novafoundation.nova.core_db.model.NodeLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
abstract class NodeDao {
|
||||
|
||||
@Query("select * from nodes")
|
||||
abstract fun nodesFlow(): Flow<List<NodeLocal>>
|
||||
|
||||
@Query("select * from nodes")
|
||||
abstract suspend fun getNodes(): List<NodeLocal>
|
||||
|
||||
@Query("select * from nodes where link = :link")
|
||||
abstract suspend fun getNode(link: String): NodeLocal
|
||||
|
||||
@Query("select * from nodes where id = :id")
|
||||
abstract suspend fun getNodeById(id: Int): NodeLocal
|
||||
|
||||
@Query("select count(*) from nodes where link = :nodeHost")
|
||||
abstract suspend fun getNodesCountByHost(nodeHost: String): Int
|
||||
|
||||
@Query("select exists (select * from nodes where link = :nodeHost)")
|
||||
abstract suspend fun checkNodeExists(nodeHost: String): Boolean
|
||||
|
||||
@Query("DELETE FROM nodes where link = :link")
|
||||
abstract suspend fun remove(link: String)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insert(nodes: List<NodeLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insert(nodes: NodeLocal): Long
|
||||
|
||||
@Query("update nodes set name = :newName, link = :newHost, networkType = :networkType where id = :id")
|
||||
abstract suspend fun updateNode(id: Int, newName: String, newHost: String, networkType: Int)
|
||||
|
||||
@Query("SELECT * from nodes where isDefault = 1 AND networkType = :networkType")
|
||||
abstract suspend fun getDefaultNodeFor(networkType: Int): NodeLocal
|
||||
|
||||
@Query("select * from nodes limit 1")
|
||||
abstract suspend fun getFirstNode(): NodeLocal
|
||||
|
||||
@Query("delete from nodes where id = :nodeId")
|
||||
abstract suspend fun deleteNode(nodeId: Int)
|
||||
|
||||
@Query("UPDATE nodes SET isActive = 1 WHERE id = :newActiveNodeId")
|
||||
protected abstract suspend fun makeActive(newActiveNodeId: Int)
|
||||
|
||||
@Query("UPDATE nodes SET isActive = 0 WHERE isActive = 1")
|
||||
protected abstract suspend fun inactiveCurrentNode()
|
||||
|
||||
@Query("SELECT * FROM nodes WHERE isActive = 1")
|
||||
abstract fun activeNodeFlow(): Flow<NodeLocal?>
|
||||
|
||||
@Transaction
|
||||
open suspend fun switchActiveNode(newNodeId: Int) {
|
||||
inactiveCurrentNode()
|
||||
|
||||
makeActive(newNodeId)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import io.novafoundation.nova.common.utils.mapNotNullToSet
|
||||
import io.novafoundation.nova.core_db.model.operation.DirectRewardTypeLocal
|
||||
import io.novafoundation.nova.core_db.model.operation.ExtrinsicTypeLocal
|
||||
import io.novafoundation.nova.core_db.model.operation.OperationBaseLocal
|
||||
import io.novafoundation.nova.core_db.model.operation.OperationJoin
|
||||
import io.novafoundation.nova.core_db.model.operation.OperationLocal
|
||||
import io.novafoundation.nova.core_db.model.operation.OperationTypeLocal
|
||||
import io.novafoundation.nova.core_db.model.operation.PoolRewardTypeLocal
|
||||
import io.novafoundation.nova.core_db.model.operation.SwapTypeLocal
|
||||
import io.novafoundation.nova.core_db.model.operation.TransferTypeLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
private const val ID_FILTER = "address = :address AND chainId = :chainId AND assetId = :chainAssetId"
|
||||
|
||||
@Dao
|
||||
abstract class OperationDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertOperationBase(operation: OperationBaseLocal)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertTransferType(type: TransferTypeLocal)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertDirectRewardType(type: DirectRewardTypeLocal)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertPoolRewardType(type: PoolRewardTypeLocal)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertExtrinsicType(type: ExtrinsicTypeLocal)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertSwapType(type: SwapTypeLocal)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertOperationsBase(operations: List<OperationBaseLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertTransferTypes(types: List<TransferTypeLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertDirectRewardTypes(types: List<DirectRewardTypeLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertPoolRewardTypes(types: List<PoolRewardTypeLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertExtrinsicTypes(types: List<ExtrinsicTypeLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertSwapTypes(types: List<SwapTypeLocal>)
|
||||
|
||||
@Transaction
|
||||
open suspend fun insert(operation: OperationLocal) {
|
||||
insertOperationBase(operation.base)
|
||||
insertOperationType(operation.type)
|
||||
}
|
||||
|
||||
@Transaction
|
||||
open suspend fun insertAll(operations: List<OperationLocal>) {
|
||||
insertAllInternal(operations)
|
||||
}
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT
|
||||
o.assetId as o_assetId, o.chainId o_chainId, o.id as o_id, o.address as o_address, o.time o_time, o.status as o_status, o.source o_source, o.hash as o_hash,
|
||||
t.amount as t_amount, t.fee as t_fee, t.sender as t_sender, t.receiver as t_receiver,
|
||||
e.contentType as e_contentType, e.module as e_module, e.call as e_call, e.fee as e_fee,
|
||||
rd.isReward as rd_isReward, rd.amount as rd_amount, rd.validator as rd_validator, rd.era as rd_era, rd.eventId as rd_eventId,
|
||||
rp.isReward as rp_isReward, rp.amount as rp_amount, rp.poolId as rp_poolId, rp.eventId as rp_eventId,
|
||||
s.fee_chainId as s_fee_chainId, s.fee_assetId as s_fee_assetId, s.fee_amount as s_fee_amount,
|
||||
s.assetIn_chainId as s_assetIn_chainId, s.assetIn_assetId as s_assetIn_assetId, s.assetIn_amount as s_assetIn_amount,
|
||||
s.assetOut_chainId as s_assetOut_chainId, s.assetOut_assetId as s_assetOut_assetId, s.assetOut_amount as s_assetOut_amount
|
||||
FROM operations as o
|
||||
LEFT JOIN operation_transfers as t ON t.operationId = o.id AND t.assetId = o.assetId AND t.chainId = o.chainId AND t.address = o.address
|
||||
LEFT JOIN operation_extrinsics as e ON e.operationId = o.id AND e.assetId = o.assetId AND e.chainId = o.chainId AND e.address = o.address
|
||||
LEFT JOIN operation_rewards_direct as rd ON rd.operationId = o.id AND rd.assetId = o.assetId AND rd.chainId = o.chainId AND rd.address = o.address
|
||||
LEFT JOIN operation_rewards_pool as rp ON rp.operationId = o.id AND rp.assetId = o.assetId AND rp.chainId = o.chainId AND rp.address = o.address
|
||||
LEFT JOIN operation_swaps as s ON s.operationId = o.id AND s.assetId = o.assetId AND s.chainId = o.chainId AND s.address = o.address
|
||||
WHERE o.address = :address AND o.chainId = :chainId AND o.assetId = :chainAssetId
|
||||
ORDER BY (case when o.status = :statusUp then 0 else 1 end), o.time DESC
|
||||
"""
|
||||
)
|
||||
abstract fun observe(
|
||||
address: String,
|
||||
chainId: String,
|
||||
chainAssetId: Int,
|
||||
statusUp: OperationBaseLocal.Status = OperationBaseLocal.Status.PENDING
|
||||
): Flow<List<OperationJoin>>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT * FROM operation_transfers
|
||||
WHERE address = :address AND chainId = :chainId AND assetId = :chainAssetId AND operationId = :operationId
|
||||
"""
|
||||
)
|
||||
abstract suspend fun getTransferType(
|
||||
operationId: String,
|
||||
address: String,
|
||||
chainId: String,
|
||||
chainAssetId: Int
|
||||
): TransferTypeLocal?
|
||||
|
||||
@Transaction
|
||||
open suspend fun insertFromRemote(
|
||||
accountAddress: String,
|
||||
chainId: String,
|
||||
chainAssetId: Int,
|
||||
operations: List<OperationLocal>
|
||||
) {
|
||||
clearBySource(accountAddress, chainId, chainAssetId, OperationBaseLocal.Source.REMOTE)
|
||||
|
||||
val operationsWithHashes = operations.mapNotNullToSet { it.base.hash }
|
||||
if (operationsWithHashes.isNotEmpty()) {
|
||||
clearByHashes(accountAddress, chainId, chainAssetId, operationsWithHashes)
|
||||
}
|
||||
|
||||
val oldestTime = operations.minOfOrNull { it.base.time }
|
||||
oldestTime?.let {
|
||||
clearOld(accountAddress, chainId, chainAssetId, oldestTime)
|
||||
}
|
||||
|
||||
insertAllInternal(operations)
|
||||
}
|
||||
|
||||
@Query("DELETE FROM operations WHERE $ID_FILTER AND source = :source")
|
||||
protected abstract suspend fun clearBySource(
|
||||
address: String,
|
||||
chainId: String,
|
||||
chainAssetId: Int,
|
||||
source: OperationBaseLocal.Source
|
||||
): Int
|
||||
|
||||
@Query("DELETE FROM operations WHERE time < :minTime AND $ID_FILTER")
|
||||
protected abstract suspend fun clearOld(
|
||||
address: String,
|
||||
chainId: String,
|
||||
chainAssetId: Int,
|
||||
minTime: Long
|
||||
): Int
|
||||
|
||||
@Query("DELETE FROM operations WHERE $ID_FILTER AND hash in (:hashes)")
|
||||
protected abstract suspend fun clearByHashes(
|
||||
address: String,
|
||||
chainId: String,
|
||||
chainAssetId: Int,
|
||||
hashes: Set<String>
|
||||
): Int
|
||||
|
||||
private suspend fun insertOperationType(type: OperationTypeLocal) {
|
||||
when (type) {
|
||||
is ExtrinsicTypeLocal -> insertExtrinsicType(type)
|
||||
is DirectRewardTypeLocal -> insertDirectRewardType(type)
|
||||
is PoolRewardTypeLocal -> insertPoolRewardType(type)
|
||||
is SwapTypeLocal -> insertSwapType(type)
|
||||
is TransferTypeLocal -> insertTransferType(type)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun insertAllInternal(operations: List<OperationLocal>) {
|
||||
insertOperationsBase(operations.map { it.base })
|
||||
insertOperationTypes(operations.map { it.type })
|
||||
}
|
||||
|
||||
private suspend fun insertOperationTypes(types: List<OperationTypeLocal>) {
|
||||
val transfers = types.filterIsInstance<TransferTypeLocal>()
|
||||
val extrinsics = types.filterIsInstance<ExtrinsicTypeLocal>()
|
||||
val directRewards = types.filterIsInstance<DirectRewardTypeLocal>()
|
||||
val poolRewards = types.filterIsInstance<PoolRewardTypeLocal>()
|
||||
val swaps = types.filterIsInstance<SwapTypeLocal>()
|
||||
|
||||
insertTransferTypes(transfers)
|
||||
insertExtrinsicTypes(extrinsics)
|
||||
insertDirectRewardTypes(directRewards)
|
||||
insertPoolRewardTypes(poolRewards)
|
||||
insertSwapTypes(swaps)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import io.novafoundation.nova.core_db.model.PhishingAddressLocal
|
||||
|
||||
@Dao
|
||||
interface PhishingAddressDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insert(addresses: List<PhishingAddressLocal>)
|
||||
|
||||
@Query("delete from phishing_addresses")
|
||||
suspend fun clearTable()
|
||||
|
||||
@Query("select publicKey from phishing_addresses")
|
||||
suspend fun getAllAddresses(): List<String>
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import io.novafoundation.nova.core_db.model.PhishingSiteLocal
|
||||
|
||||
@Dao
|
||||
abstract class PhishingSitesDao {
|
||||
|
||||
@Query("SELECT EXISTS (SELECT * FROM phishing_sites WHERE host in (:hostSuffixes))")
|
||||
abstract suspend fun isPhishing(hostSuffixes: List<String>): Boolean
|
||||
|
||||
@Transaction
|
||||
open suspend fun updatePhishingSites(newSites: List<PhishingSiteLocal>) {
|
||||
clearPhishingSites()
|
||||
|
||||
insertPhishingSites(newSites)
|
||||
}
|
||||
|
||||
@Query("DELETE FROM phishing_sites")
|
||||
protected abstract suspend fun clearPhishingSites()
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
protected abstract suspend fun insertPhishingSites(sites: List<PhishingSiteLocal>)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import io.novafoundation.nova.core_db.model.StakingDashboardAccountsView
|
||||
import io.novafoundation.nova.core_db.model.StakingDashboardItemLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface StakingDashboardDao {
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT * FROM staking_dashboard_items WHERE
|
||||
metaId = :metaId
|
||||
AND chainId = :chainId
|
||||
AND chainAssetId = :chainAssetId
|
||||
AND stakingType = :stakingType
|
||||
"""
|
||||
)
|
||||
suspend fun getDashboardItem(
|
||||
chainId: String,
|
||||
chainAssetId: Int,
|
||||
stakingType: String,
|
||||
metaId: Long,
|
||||
): StakingDashboardItemLocal?
|
||||
|
||||
@Query("SELECT * FROM staking_dashboard_items WHERE metaId = :metaId")
|
||||
fun dashboardItemsFlow(metaId: Long): Flow<List<StakingDashboardItemLocal>>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT * FROM staking_dashboard_items
|
||||
WHERE metaId = :metaId AND chainId = :chainId AND chainAssetId = :assetId AND stakingType IN (:assetTypes)
|
||||
"""
|
||||
)
|
||||
fun dashboardItemsFlow(metaId: Long, chainId: String, assetId: Int, assetTypes: List<String>): Flow<List<StakingDashboardItemLocal>>
|
||||
|
||||
@Query("SELECT chainId, chainAssetId, stakingType, stakeStatusAccount, rewardsAccount FROM staking_dashboard_items WHERE metaId = :metaId")
|
||||
fun stakingAccountsViewFlow(metaId: Long): Flow<List<StakingDashboardAccountsView>>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertItem(dashboardItemLocal: StakingDashboardItemLocal)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import io.novafoundation.nova.core_db.model.StakingRewardPeriodLocal
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface StakingRewardPeriodDao {
|
||||
|
||||
@Query("SELECT * FROM staking_reward_period WHERE accountId = :accountId AND chainId = :chainId AND assetId = :assetId AND stakingType = :stakingType")
|
||||
suspend fun getStakingRewardPeriod(accountId: AccountId, chainId: String, assetId: Int, stakingType: String): StakingRewardPeriodLocal?
|
||||
|
||||
@Query("SELECT * FROM staking_reward_period WHERE accountId = :accountId AND chainId = :chainId AND assetId = :assetId AND stakingType = :stakingType")
|
||||
fun observeStakingRewardPeriod(accountId: AccountId, chainId: String, assetId: Int, stakingType: String): Flow<StakingRewardPeriodLocal?>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertStakingRewardPeriod(stakingRewardPeriodLocal: StakingRewardPeriodLocal)
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import io.novafoundation.nova.core_db.model.TotalRewardLocal
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
abstract class StakingTotalRewardDao {
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT * FROM total_reward
|
||||
WHERE accountId = :accountId AND chainId = :chainId AND chainAssetId = :chainAssetId and stakingType = :stakingType
|
||||
"""
|
||||
)
|
||||
abstract fun observeTotalRewards(accountId: AccountId, chainId: String, chainAssetId: Int, stakingType: String): Flow<TotalRewardLocal>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insert(totalRewardLocal: TotalRewardLocal)
|
||||
|
||||
@Query("DELETE FROM total_reward")
|
||||
abstract suspend fun deleteAll()
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import io.novafoundation.nova.core_db.model.StorageEntryLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
private const val SELECT_FULL_KEY_QUERY = "SELECT * from storage WHERE chainId = :chainId AND storageKey = :fullKey"
|
||||
private const val SELECT_PREFIX_KEY_QUERY = "SELECT * from storage WHERE chainId = :chainId AND storageKey LIKE :keyPrefix || '%'"
|
||||
|
||||
@Dao
|
||||
abstract class StorageDao {
|
||||
|
||||
@Query("SELECT EXISTS($SELECT_PREFIX_KEY_QUERY)")
|
||||
abstract suspend fun isPrefixInCache(chainId: String, keyPrefix: String): Boolean
|
||||
|
||||
@Query("SELECT EXISTS($SELECT_FULL_KEY_QUERY)")
|
||||
abstract suspend fun isFullKeyInCache(chainId: String, fullKey: String): Boolean
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insert(entry: StorageEntryLocal)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insert(entries: List<StorageEntryLocal>)
|
||||
|
||||
@Query("DELETE FROM storage WHERE chainId = :chainId AND storageKey LIKE :prefix || '%'")
|
||||
abstract suspend fun removeByPrefix(prefix: String, chainId: String)
|
||||
|
||||
@Query(
|
||||
"""
|
||||
DELETE FROM storage WHERE chainId = :chainId
|
||||
AND storageKey LIKE :prefix || '%'
|
||||
AND storageKey NOT IN (:exceptionFullKeys)
|
||||
"""
|
||||
)
|
||||
abstract suspend fun removeByPrefixExcept(prefix: String, exceptionFullKeys: List<String>, chainId: String)
|
||||
|
||||
@Query(SELECT_FULL_KEY_QUERY)
|
||||
abstract fun observeEntry(chainId: String, fullKey: String): Flow<StorageEntryLocal?>
|
||||
|
||||
@Query(SELECT_PREFIX_KEY_QUERY)
|
||||
abstract fun observeEntries(chainId: String, keyPrefix: String): Flow<List<StorageEntryLocal>>
|
||||
|
||||
@Query("SELECT storageKey from storage WHERE chainId = :chainId AND storageKey LIKE :keyPrefix || '%'")
|
||||
abstract suspend fun getKeys(chainId: String, keyPrefix: String): List<String>
|
||||
|
||||
@Query("SELECT * from storage WHERE chainId = :chainId AND storageKey in (:fullKeys)")
|
||||
abstract fun observeEntries(chainId: String, fullKeys: List<String>): Flow<List<StorageEntryLocal>>
|
||||
|
||||
@Query("SELECT storageKey from storage WHERE chainId = :chainId AND storageKey in (:keys)")
|
||||
abstract suspend fun filterKeysInCache(chainId: String, keys: List<String>): List<String>
|
||||
|
||||
@Transaction
|
||||
open suspend fun insertPrefixedEntries(entries: List<StorageEntryLocal>, prefix: String, chainId: String) {
|
||||
removeByPrefix(prefix, chainId)
|
||||
|
||||
insert(entries)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import io.novafoundation.nova.core_db.model.TinderGovBasketItemLocal
|
||||
import io.novafoundation.nova.core_db.model.TinderGovVotingPowerLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface TinderGovDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun setVotingPower(item: TinderGovVotingPowerLocal)
|
||||
|
||||
@Query("SELECT * FROM tinder_gov_voting_power WHERE metaId = :metaId AND chainId = :chainId")
|
||||
suspend fun getVotingPower(metaId: Long, chainId: String): TinderGovVotingPowerLocal?
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun addToBasket(item: TinderGovBasketItemLocal)
|
||||
|
||||
@Delete
|
||||
suspend fun removeFromBasket(item: TinderGovBasketItemLocal)
|
||||
|
||||
@Delete
|
||||
suspend fun removeFromBasket(items: List<TinderGovBasketItemLocal>)
|
||||
|
||||
@Query("SELECT * FROM tinder_gov_basket WHERE metaId = :metaId AND chainId == :chainId")
|
||||
suspend fun getBasket(metaId: Long, chainId: String): List<TinderGovBasketItemLocal>
|
||||
|
||||
@Query("SELECT * FROM tinder_gov_basket WHERE metaId = :metaId AND chainId == :chainId")
|
||||
fun observeBasket(metaId: Long, chainId: String): Flow<List<TinderGovBasketItemLocal>>
|
||||
|
||||
@Query("SELECT COUNT(*) FROM tinder_gov_basket WHERE metaId = :metaId AND chainId == :chainId")
|
||||
fun basketSize(metaId: Long, chainId: String): Int
|
||||
|
||||
@Query("DELETE FROM tinder_gov_basket WHERE metaId = :metaId AND chainId == :chainId")
|
||||
fun clearBasket(metaId: Long, chainId: String)
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import io.novafoundation.nova.common.utils.CollectionDiffer
|
||||
import io.novafoundation.nova.core_db.model.TokenLocal
|
||||
import io.novafoundation.nova.core_db.model.TokenWithCurrency
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
private const val RETRIEVE_TOKEN_WITH_CURRENCY = """
|
||||
SELECT * FROM currencies AS currency
|
||||
LEFT OUTER JOIN tokens AS token ON token.currencyId = currency.id AND token.tokenSymbol = :symbol
|
||||
WHERE currency.selected = 1
|
||||
"""
|
||||
|
||||
private const val RETRIEVE_TOKENS_WITH_CURRENCY = """
|
||||
SELECT * FROM currencies AS currency
|
||||
LEFT OUTER JOIN tokens AS token ON token.currencyId = currency.id AND token.tokenSymbol in (:symbols)
|
||||
WHERE currency.selected = 1
|
||||
"""
|
||||
|
||||
private const val INSERT_TOKEN_WITH_SELECTED_CURRENCY = """
|
||||
INSERT OR IGNORE INTO tokens (tokenSymbol, rate, currencyId, recentRateChange)
|
||||
VALUES(:symbol, NULL, (SELECT id FROM currencies WHERE selected = 1), NULL)
|
||||
"""
|
||||
|
||||
@Dao
|
||||
abstract class TokenDao {
|
||||
|
||||
@Transaction
|
||||
open suspend fun applyDiff(diff: CollectionDiffer.Diff<TokenLocal>) {
|
||||
deleteTokens(diff.removed)
|
||||
insertTokens(diff.added)
|
||||
updateTokens(diff.updated)
|
||||
}
|
||||
|
||||
@Query(RETRIEVE_TOKEN_WITH_CURRENCY)
|
||||
abstract suspend fun getTokenWithCurrency(symbol: String): TokenWithCurrency?
|
||||
|
||||
@Query(RETRIEVE_TOKENS_WITH_CURRENCY)
|
||||
abstract fun observeTokensWithCurrency(symbols: List<String>): Flow<List<TokenWithCurrency>>
|
||||
|
||||
@Query(RETRIEVE_TOKENS_WITH_CURRENCY)
|
||||
abstract fun getTokensWithCurrency(symbols: List<String>): List<TokenWithCurrency>
|
||||
|
||||
@Query(RETRIEVE_TOKEN_WITH_CURRENCY)
|
||||
abstract fun observeTokenWithCurrency(symbol: String): Flow<TokenWithCurrency>
|
||||
|
||||
@Query("SELECT * FROM tokens")
|
||||
abstract suspend fun getTokens(): List<TokenLocal>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertTokens(tokens: List<TokenLocal>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insertToken(token: TokenLocal)
|
||||
|
||||
@Query(INSERT_TOKEN_WITH_SELECTED_CURRENCY)
|
||||
abstract suspend fun insertTokenWithSelectedCurrency(symbol: String)
|
||||
|
||||
@Update
|
||||
abstract suspend fun updateTokens(chains: List<TokenLocal>)
|
||||
|
||||
@Delete
|
||||
abstract suspend fun deleteTokens(tokens: List<TokenLocal>)
|
||||
|
||||
@Query("DELETE FROM tokens")
|
||||
abstract suspend fun deleteAll()
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package io.novafoundation.nova.core_db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import io.novafoundation.nova.core_db.model.WalletConnectPairingLocal
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface WalletConnectSessionsDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertPairing(pairing: WalletConnectPairingLocal)
|
||||
|
||||
@Query("DELETE FROM wallet_connect_pairings WHERE pairingTopic = :pairingTopic")
|
||||
suspend fun deletePairing(pairingTopic: String)
|
||||
|
||||
@Query("SELECT * FROM wallet_connect_pairings WHERE pairingTopic = :pairingTopic")
|
||||
suspend fun getPairing(pairingTopic: String): WalletConnectPairingLocal?
|
||||
|
||||
@Query("SELECT * FROM wallet_connect_pairings WHERE pairingTopic = :pairingTopic")
|
||||
fun pairingFlow(pairingTopic: String): Flow<WalletConnectPairingLocal?>
|
||||
|
||||
@Query("DELETE FROM wallet_connect_pairings WHERE pairingTopic NOT IN (:pairingTopics)")
|
||||
suspend fun removeAllPairingsOtherThan(pairingTopics: List<String>)
|
||||
|
||||
@Query("SELECT * FROM wallet_connect_pairings")
|
||||
fun allPairingsFlow(): Flow<List<WalletConnectPairingLocal>>
|
||||
|
||||
@Query("SELECT * FROM wallet_connect_pairings WHERE metaId = :metaId")
|
||||
fun pairingsByMetaIdFlow(metaId: Long): Flow<List<WalletConnectPairingLocal>>
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package io.novafoundation.nova.core_db.di
|
||||
|
||||
import io.novafoundation.nova.core_db.AppDatabase
|
||||
import io.novafoundation.nova.core_db.dao.AccountDao
|
||||
import io.novafoundation.nova.core_db.dao.AccountStakingDao
|
||||
import io.novafoundation.nova.core_db.dao.AssetDao
|
||||
import io.novafoundation.nova.core_db.dao.BrowserHostSettingsDao
|
||||
import io.novafoundation.nova.core_db.dao.BrowserTabsDao
|
||||
import io.novafoundation.nova.core_db.dao.ChainAssetDao
|
||||
import io.novafoundation.nova.core_db.dao.ChainDao
|
||||
import io.novafoundation.nova.core_db.dao.CoinPriceDao
|
||||
import io.novafoundation.nova.core_db.dao.ContributionDao
|
||||
import io.novafoundation.nova.core_db.dao.CurrencyDao
|
||||
import io.novafoundation.nova.core_db.dao.DappAuthorizationDao
|
||||
import io.novafoundation.nova.core_db.dao.ExternalBalanceDao
|
||||
import io.novafoundation.nova.core_db.dao.FavouriteDAppsDao
|
||||
import io.novafoundation.nova.core_db.dao.GiftsDao
|
||||
import io.novafoundation.nova.core_db.dao.GovernanceDAppsDao
|
||||
import io.novafoundation.nova.core_db.dao.HoldsDao
|
||||
import io.novafoundation.nova.core_db.dao.LockDao
|
||||
import io.novafoundation.nova.core_db.dao.MetaAccountDao
|
||||
import io.novafoundation.nova.core_db.dao.MultisigOperationsDao
|
||||
import io.novafoundation.nova.core_db.dao.NftDao
|
||||
import io.novafoundation.nova.core_db.dao.NodeDao
|
||||
import io.novafoundation.nova.core_db.dao.OperationDao
|
||||
import io.novafoundation.nova.core_db.dao.PhishingAddressDao
|
||||
import io.novafoundation.nova.core_db.dao.PhishingSitesDao
|
||||
import io.novafoundation.nova.core_db.dao.StakingDashboardDao
|
||||
import io.novafoundation.nova.core_db.dao.StakingRewardPeriodDao
|
||||
import io.novafoundation.nova.core_db.dao.StakingTotalRewardDao
|
||||
import io.novafoundation.nova.core_db.dao.StorageDao
|
||||
import io.novafoundation.nova.core_db.dao.TinderGovDao
|
||||
import io.novafoundation.nova.core_db.dao.TokenDao
|
||||
import io.novafoundation.nova.core_db.dao.WalletConnectSessionsDao
|
||||
|
||||
interface DbApi {
|
||||
|
||||
val phishingSitesDao: PhishingSitesDao
|
||||
|
||||
val favouritesDAppsDao: FavouriteDAppsDao
|
||||
|
||||
val currencyDao: CurrencyDao
|
||||
|
||||
val walletConnectSessionsDao: WalletConnectSessionsDao
|
||||
|
||||
val stakingDashboardDao: StakingDashboardDao
|
||||
|
||||
val externalBalanceDao: ExternalBalanceDao
|
||||
|
||||
val holdsDao: HoldsDao
|
||||
|
||||
fun provideDatabase(): AppDatabase
|
||||
|
||||
fun provideLockDao(): LockDao
|
||||
|
||||
fun provideAccountDao(): AccountDao
|
||||
|
||||
fun contributionDao(): ContributionDao
|
||||
|
||||
fun provideNodeDao(): NodeDao
|
||||
|
||||
fun provideAssetDao(): AssetDao
|
||||
|
||||
fun provideOperationDao(): OperationDao
|
||||
|
||||
fun providePhishingAddressDao(): PhishingAddressDao
|
||||
|
||||
fun storageDao(): StorageDao
|
||||
|
||||
fun tokenDao(): TokenDao
|
||||
|
||||
fun accountStakingDao(): AccountStakingDao
|
||||
|
||||
fun stakingTotalRewardDao(): StakingTotalRewardDao
|
||||
|
||||
fun chainDao(): ChainDao
|
||||
|
||||
fun chainAssetDao(): ChainAssetDao
|
||||
|
||||
fun metaAccountDao(): MetaAccountDao
|
||||
|
||||
fun dappAuthorizationDao(): DappAuthorizationDao
|
||||
|
||||
fun nftDao(): NftDao
|
||||
|
||||
fun governanceDAppsDao(): GovernanceDAppsDao
|
||||
|
||||
fun browserHostSettingsDao(): BrowserHostSettingsDao
|
||||
|
||||
fun coinPriceDao(): CoinPriceDao
|
||||
|
||||
fun stakingRewardPeriodDao(): StakingRewardPeriodDao
|
||||
|
||||
fun tinderGovDao(): TinderGovDao
|
||||
|
||||
fun browserTabsDao(): BrowserTabsDao
|
||||
|
||||
fun multisigOperationsDao(): MultisigOperationsDao
|
||||
|
||||
fun giftsDao(): GiftsDao
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.novafoundation.nova.core_db.di
|
||||
|
||||
import dagger.Component
|
||||
import io.novafoundation.nova.common.di.CommonApi
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
|
||||
@Component(
|
||||
modules = [
|
||||
DbModule::class
|
||||
],
|
||||
dependencies = [
|
||||
DbDependencies::class
|
||||
]
|
||||
)
|
||||
@ApplicationScope
|
||||
abstract class DbComponent : DbApi {
|
||||
|
||||
@Component(
|
||||
dependencies = [
|
||||
CommonApi::class
|
||||
]
|
||||
)
|
||||
interface DbDependenciesComponent : DbDependencies
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package io.novafoundation.nova.core_db.di
|
||||
|
||||
import android.content.Context
|
||||
import com.google.gson.Gson
|
||||
import io.novafoundation.nova.common.data.secrets.v1.SecretStoreV1
|
||||
import io.novafoundation.nova.common.data.secrets.v2.SecretStoreV2
|
||||
import io.novafoundation.nova.common.data.storage.Preferences
|
||||
|
||||
interface DbDependencies {
|
||||
|
||||
fun gson(): Gson
|
||||
|
||||
fun preferences(): Preferences
|
||||
|
||||
fun context(): Context
|
||||
|
||||
fun secretStoreV1(): SecretStoreV1
|
||||
|
||||
fun secretStoreV2(): SecretStoreV2
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.novafoundation.nova.core_db.di
|
||||
|
||||
import io.novafoundation.nova.common.di.FeatureApiHolder
|
||||
import io.novafoundation.nova.common.di.FeatureContainer
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import javax.inject.Inject
|
||||
|
||||
@ApplicationScope
|
||||
class DbHolder @Inject constructor(
|
||||
featureContainer: FeatureContainer
|
||||
) : FeatureApiHolder(featureContainer) {
|
||||
|
||||
override fun initializeDependencies(): Any {
|
||||
val dbDependencies = DaggerDbComponent_DbDependenciesComponent.builder()
|
||||
.commonApi(commonApi())
|
||||
.build()
|
||||
return DaggerDbComponent.builder()
|
||||
.dbDependencies(dbDependencies)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
package io.novafoundation.nova.core_db.di
|
||||
|
||||
import android.content.Context
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.core_db.AppDatabase
|
||||
import io.novafoundation.nova.core_db.dao.AccountDao
|
||||
import io.novafoundation.nova.core_db.dao.AccountStakingDao
|
||||
import io.novafoundation.nova.core_db.dao.AssetDao
|
||||
import io.novafoundation.nova.core_db.dao.BrowserHostSettingsDao
|
||||
import io.novafoundation.nova.core_db.dao.BrowserTabsDao
|
||||
import io.novafoundation.nova.core_db.dao.ChainAssetDao
|
||||
import io.novafoundation.nova.core_db.dao.ChainDao
|
||||
import io.novafoundation.nova.core_db.dao.CoinPriceDao
|
||||
import io.novafoundation.nova.core_db.dao.ContributionDao
|
||||
import io.novafoundation.nova.core_db.dao.CurrencyDao
|
||||
import io.novafoundation.nova.core_db.dao.DappAuthorizationDao
|
||||
import io.novafoundation.nova.core_db.dao.ExternalBalanceDao
|
||||
import io.novafoundation.nova.core_db.dao.FavouriteDAppsDao
|
||||
import io.novafoundation.nova.core_db.dao.GiftsDao
|
||||
import io.novafoundation.nova.core_db.dao.GovernanceDAppsDao
|
||||
import io.novafoundation.nova.core_db.dao.HoldsDao
|
||||
import io.novafoundation.nova.core_db.dao.LockDao
|
||||
import io.novafoundation.nova.core_db.dao.MetaAccountDao
|
||||
import io.novafoundation.nova.core_db.dao.MultisigOperationsDao
|
||||
import io.novafoundation.nova.core_db.dao.NftDao
|
||||
import io.novafoundation.nova.core_db.dao.NodeDao
|
||||
import io.novafoundation.nova.core_db.dao.OperationDao
|
||||
import io.novafoundation.nova.core_db.dao.PhishingAddressDao
|
||||
import io.novafoundation.nova.core_db.dao.PhishingSitesDao
|
||||
import io.novafoundation.nova.core_db.dao.StakingDashboardDao
|
||||
import io.novafoundation.nova.core_db.dao.StakingRewardPeriodDao
|
||||
import io.novafoundation.nova.core_db.dao.StakingTotalRewardDao
|
||||
import io.novafoundation.nova.core_db.dao.StorageDao
|
||||
import io.novafoundation.nova.core_db.dao.TinderGovDao
|
||||
import io.novafoundation.nova.core_db.dao.TokenDao
|
||||
import io.novafoundation.nova.core_db.dao.WalletConnectSessionsDao
|
||||
|
||||
@Module
|
||||
class DbModule {
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideAppDatabase(
|
||||
context: Context
|
||||
): AppDatabase {
|
||||
return AppDatabase.get(context)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideUserDao(appDatabase: AppDatabase): AccountDao {
|
||||
return appDatabase.userDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideNodeDao(appDatabase: AppDatabase): NodeDao {
|
||||
return appDatabase.nodeDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideAssetDao(appDatabase: AppDatabase): AssetDao {
|
||||
return appDatabase.assetDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideLockDao(appDatabase: AppDatabase): LockDao {
|
||||
return appDatabase.lockDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideContributionDao(appDatabase: AppDatabase): ContributionDao {
|
||||
return appDatabase.contributionDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideOperationHistoryDao(appDatabase: AppDatabase): OperationDao {
|
||||
return appDatabase.operationDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun providePhishingAddressDao(appDatabase: AppDatabase): PhishingAddressDao {
|
||||
return appDatabase.phishingAddressesDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideStorageDao(appDatabase: AppDatabase): StorageDao {
|
||||
return appDatabase.storageDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideTokenDao(appDatabase: AppDatabase): TokenDao {
|
||||
return appDatabase.tokenDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideAccountStakingDao(appDatabase: AppDatabase): AccountStakingDao {
|
||||
return appDatabase.accountStakingDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideStakingTotalRewardDao(appDatabase: AppDatabase): StakingTotalRewardDao {
|
||||
return appDatabase.stakingTotalRewardDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideChainDao(appDatabase: AppDatabase): ChainDao {
|
||||
return appDatabase.chainDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideChainAssetDao(appDatabase: AppDatabase): ChainAssetDao {
|
||||
return appDatabase.chainAssetDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideMetaAccountDao(appDatabase: AppDatabase): MetaAccountDao {
|
||||
return appDatabase.metaAccountDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideDappAuthorizationDao(appDatabase: AppDatabase): DappAuthorizationDao {
|
||||
return appDatabase.dAppAuthorizationDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideNftDao(appDatabase: AppDatabase): NftDao {
|
||||
return appDatabase.nftDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun providePhishingSitesDao(appDatabase: AppDatabase): PhishingSitesDao {
|
||||
return appDatabase.phishingSitesDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideFavouriteDappsDao(appDatabase: AppDatabase): FavouriteDAppsDao {
|
||||
return appDatabase.favouriteDAppsDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideCurrencyDao(appDatabase: AppDatabase): CurrencyDao {
|
||||
return appDatabase.currencyDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideGovernanceDAppDao(appDatabase: AppDatabase): GovernanceDAppsDao {
|
||||
return appDatabase.governanceDAppsDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideBrowserHostSettingsDao(appDatabase: AppDatabase): BrowserHostSettingsDao {
|
||||
return appDatabase.browserHostSettingsDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideWalletConnectSessionsDao(appDatabase: AppDatabase): WalletConnectSessionsDao {
|
||||
return appDatabase.walletConnectSessionsDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideStakingDashboardDao(appDatabase: AppDatabase): StakingDashboardDao {
|
||||
return appDatabase.stakingDashboardDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideCoinPriceDao(appDatabase: AppDatabase): CoinPriceDao {
|
||||
return appDatabase.coinPriceDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideStakingRewardPeriodDao(appDatabase: AppDatabase): StakingRewardPeriodDao {
|
||||
return appDatabase.stakingRewardPeriodDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideExternalBalanceDao(appDatabase: AppDatabase): ExternalBalanceDao {
|
||||
return appDatabase.externalBalanceDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideHoldsDao(appDatabase: AppDatabase): HoldsDao {
|
||||
return appDatabase.holdsDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideTinderGovDao(appDatabase: AppDatabase): TinderGovDao {
|
||||
return appDatabase.tinderGovDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideBrowserTabsDao(appDatabase: AppDatabase): BrowserTabsDao {
|
||||
return appDatabase.browserTabsDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideMultisigOperationsDao(appDatabase: AppDatabase): MultisigOperationsDao {
|
||||
return appDatabase.multisigOperationsDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideGiftsDao(appDatabase: AppDatabase): GiftsDao {
|
||||
return appDatabase.giftsDao()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package io.novafoundation.nova.core_db.ext
|
||||
|
||||
import io.novafoundation.nova.core_db.dao.FullAssetIdLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainAssetLocal
|
||||
|
||||
fun ChainAssetLocal.fullId(): FullAssetIdLocal {
|
||||
return FullAssetIdLocal(this.chainId, this.id)
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val ChangeDAppAuthorization_10_11 = object : Migration(10, 11) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP TABLE dapp_authorizations")
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `dapp_authorizations` (
|
||||
`baseUrl` TEXT NOT NULL,
|
||||
`metaId` INTEGER NOT NULL,
|
||||
`dAppTitle` TEXT, `authorized` INTEGER,
|
||||
PRIMARY KEY(`baseUrl`, `metaId`)
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val RemoveChainForeignKeyFromChainAccount_11_12 = object : Migration(11, 12) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// rename
|
||||
database.execSQL("DROP INDEX `index_chain_accounts_chainId`")
|
||||
database.execSQL("DROP INDEX `index_chain_accounts_metaId`")
|
||||
database.execSQL("DROP INDEX `index_chain_accounts_accountId`")
|
||||
database.execSQL("ALTER TABLE chain_accounts RENAME TO chain_accounts_old")
|
||||
|
||||
// new table
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `chain_accounts` (
|
||||
`metaId` INTEGER NOT NULL,
|
||||
`chainId` TEXT NOT NULL,
|
||||
`publicKey` BLOB NOT NULL,
|
||||
`accountId` BLOB NOT NULL,
|
||||
`cryptoType` TEXT NOT NULL,
|
||||
PRIMARY KEY(`metaId`, `chainId`),
|
||||
FOREIGN KEY(`metaId`) REFERENCES `meta_accounts`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_chain_accounts_chainId` ON `chain_accounts` (`chainId`)")
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_chain_accounts_metaId` ON `chain_accounts` (`metaId`)")
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_chain_accounts_accountId` ON `chain_accounts` (`accountId`)")
|
||||
|
||||
// insert to new from old
|
||||
database.execSQL(
|
||||
"""
|
||||
INSERT INTO chain_accounts
|
||||
SELECT *
|
||||
FROM chain_accounts_old
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
// delete old
|
||||
database.execSQL("DROP TABLE chain_accounts_old")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
// used on master for Astar hotfix
|
||||
val AddAdditionalFieldToChains_12_13 = object : Migration(12, 13) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE chains ADD COLUMN additional TEXT DEFAULT null")
|
||||
}
|
||||
}
|
||||
|
||||
// used on develop for parachainStaking rewards
|
||||
val AddChainToTotalRewards_12_13 = object : Migration(12, 13) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP TABLE total_reward")
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `total_reward` (
|
||||
`accountAddress` TEXT NOT NULL,
|
||||
`chainId` TEXT NOT NULL,
|
||||
`chainAssetId` INTEGER NOT NULL,
|
||||
`totalReward` TEXT NOT NULL,
|
||||
PRIMARY KEY(`chainId`, `chainAssetId`, `accountAddress`)
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val FixMigrationConflicts_13_14 = object : Migration(13, 14) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
if (isMigratingFromMaster(database)) {
|
||||
// migrating from master -> execute missing develop migration
|
||||
AddChainToTotalRewards_12_13.migrate(database)
|
||||
} else {
|
||||
// migrating from develop -> execute missing master migration
|
||||
AddAdditionalFieldToChains_12_13.migrate(database)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isMigratingFromMaster(database: SupportSQLiteDatabase): Boolean {
|
||||
return runCatching {
|
||||
// check for column added in astar hotfix (master)
|
||||
database.query("SELECT additional FROM chains LIMIT 1")
|
||||
}.fold(
|
||||
onSuccess = { true },
|
||||
onFailure = { false }
|
||||
)
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import io.novafoundation.nova.core_db.converters.MetaAccountTypeConverters
|
||||
import io.novafoundation.nova.core_db.model.chain.account.MetaAccountLocal
|
||||
|
||||
val AddMetaAccountType_14_15 = object : Migration(14, 15) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
val converters = MetaAccountTypeConverters()
|
||||
|
||||
// all accounts that exist till now are added via secrets
|
||||
val defaultType = MetaAccountLocal.Type.SECRETS
|
||||
val typeRepresentationInDb = converters.fromEnum(defaultType)
|
||||
|
||||
database.execSQL("ALTER TABLE meta_accounts ADD COLUMN type TEXT NOT NULL DEFAULT '$typeRepresentationInDb'")
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val NullableSubstratePublicKey_15_16 = object : Migration(15, 16) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// rename
|
||||
database.execSQL("DROP INDEX `index_meta_accounts_substrateAccountId`")
|
||||
database.execSQL("DROP INDEX `index_meta_accounts_ethereumAddress`")
|
||||
database.execSQL("ALTER TABLE meta_accounts RENAME TO meta_accounts_old")
|
||||
|
||||
// new table
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `meta_accounts` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`substratePublicKey` BLOB,
|
||||
`substrateCryptoType` TEXT,
|
||||
`substrateAccountId` BLOB NOT NULL,
|
||||
`ethereumPublicKey` BLOB,
|
||||
`ethereumAddress` BLOB,
|
||||
`name` TEXT NOT NULL,
|
||||
`isSelected` INTEGER NOT NULL,
|
||||
`position` INTEGER NOT NULL,
|
||||
`type` TEXT NOT NULL
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_meta_accounts_substrateAccountId` ON `meta_accounts` (`substrateAccountId`)")
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_meta_accounts_ethereumAddress` ON `meta_accounts` (`ethereumAddress`)")
|
||||
|
||||
// insert to new from old
|
||||
database.execSQL(
|
||||
"""
|
||||
INSERT INTO meta_accounts
|
||||
SELECT *
|
||||
FROM meta_accounts_old
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
// delete old
|
||||
database.execSQL("DROP TABLE meta_accounts_old")
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val WatchOnlyChainAccounts_16_17 = object : Migration(16, 17) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// rename
|
||||
database.execSQL("DROP INDEX `index_chain_accounts_chainId`")
|
||||
database.execSQL("DROP INDEX `index_chain_accounts_metaId`")
|
||||
database.execSQL("DROP INDEX `index_chain_accounts_accountId`")
|
||||
database.execSQL("ALTER TABLE chain_accounts RENAME TO chain_accounts_old")
|
||||
|
||||
// new table
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `chain_accounts` (
|
||||
`metaId` INTEGER NOT NULL,
|
||||
`chainId` TEXT NOT NULL,
|
||||
`publicKey` BLOB,
|
||||
`accountId` BLOB NOT NULL,
|
||||
`cryptoType` TEXT,
|
||||
PRIMARY KEY(`metaId`, `chainId`),
|
||||
FOREIGN KEY(`metaId`) REFERENCES `meta_accounts`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_chain_accounts_chainId` ON `chain_accounts` (`chainId`)")
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_chain_accounts_metaId` ON `chain_accounts` (`metaId`)")
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_chain_accounts_accountId` ON `chain_accounts` (`accountId`)")
|
||||
|
||||
// insert to new from old
|
||||
database.execSQL(
|
||||
"""
|
||||
INSERT INTO chain_accounts
|
||||
SELECT *
|
||||
FROM chain_accounts_old
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
// delete old
|
||||
database.execSQL("DROP TABLE chain_accounts_old")
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val RemoveColorFromChains_17_18 = object : Migration(17, 18) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// rename
|
||||
database.execSQL("ALTER TABLE chains RENAME TO chains_old")
|
||||
|
||||
// new table
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `chains` (
|
||||
`id` TEXT NOT NULL,
|
||||
`parentId` TEXT,
|
||||
`name` TEXT NOT NULL,
|
||||
`icon` TEXT NOT NULL,
|
||||
`prefix` INTEGER NOT NULL,
|
||||
`isEthereumBased` INTEGER NOT NULL,
|
||||
`isTestNet` INTEGER NOT NULL,
|
||||
`hasCrowdloans` INTEGER NOT NULL,
|
||||
`additional` TEXT,
|
||||
`url` TEXT,
|
||||
`overridesCommon` INTEGER,
|
||||
`staking_url` TEXT,
|
||||
`staking_type` TEXT,
|
||||
`history_url` TEXT,
|
||||
`history_type` TEXT,
|
||||
`crowdloans_url` TEXT,
|
||||
`crowdloans_type` TEXT,
|
||||
PRIMARY KEY(`id`)
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
// insert to new from old
|
||||
database.execSQL(
|
||||
// select all but color
|
||||
"""
|
||||
INSERT INTO chains
|
||||
SELECT id, parentId, name, icon, prefix, isEthereumBased, isTestNet, hasCrowdloans, additional, url, overridesCommon,
|
||||
staking_url, staking_type, history_url, history_type, crowdloans_url, crowdloans_type
|
||||
FROM chains_old
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
// delete old
|
||||
database.execSQL("DROP TABLE chains_old")
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val AddCurrencies_18_19 = object : Migration(18, 19) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// new table
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `currencies` (
|
||||
`code` TEXT NOT NULL,
|
||||
`name` TEXT NOT NULL,
|
||||
`symbol` TEXT,
|
||||
`category` TEXT NOT NULL,
|
||||
`popular` INTEGER NOT NULL,
|
||||
`id` INTEGER NOT NULL,
|
||||
`coingeckoId` TEXT NOT NULL,
|
||||
`selected` INTEGER NOT NULL,
|
||||
PRIMARY KEY(`id`)
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val ChangeTokens_19_20 = object : Migration(19, 20) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// rename table
|
||||
database.execSQL("ALTER TABLE tokens RENAME TO tokens_old")
|
||||
|
||||
// new table
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `tokens` (
|
||||
`tokenSymbol` TEXT NOT NULL,
|
||||
`rate` TEXT,
|
||||
`recentRateChange` TEXT,
|
||||
`currencyId` INTEGER NOT NULL,
|
||||
PRIMARY KEY(`tokenSymbol`, `currencyId`)
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
// insert to new from old
|
||||
database.execSQL(
|
||||
"""
|
||||
INSERT INTO tokens (tokenSymbol, rate, recentRateChange, currencyId)
|
||||
SELECT symbol, dollarRate, recentRateChange, 0
|
||||
FROM tokens_old
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
// delete old
|
||||
database.execSQL("DROP TABLE tokens_old")
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val AddDAppAuthorizations_1_2 = object : Migration(1, 2) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `dapp_authorizations` (
|
||||
`baseUrl` TEXT NOT NULL,
|
||||
`authorized` INTEGER,
|
||||
PRIMARY KEY(`baseUrl`)
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val ChangeChainNodes_20_21 = object : Migration(20, 21) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE chain_nodes ADD `orderId` INTEGER NOT NULL DEFAULT 0")
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val NullableSubstrateAccountId_21_22 = object : Migration(21, 22) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// rename
|
||||
database.execSQL("DROP INDEX `index_meta_accounts_substrateAccountId`")
|
||||
database.execSQL("DROP INDEX `index_meta_accounts_ethereumAddress`")
|
||||
database.execSQL("ALTER TABLE meta_accounts RENAME TO meta_accounts_old")
|
||||
|
||||
// new table
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `meta_accounts` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`substratePublicKey` BLOB,
|
||||
`substrateCryptoType` TEXT,
|
||||
`substrateAccountId` BLOB,
|
||||
`ethereumPublicKey` BLOB,
|
||||
`ethereumAddress` BLOB,
|
||||
`name` TEXT NOT NULL,
|
||||
`isSelected` INTEGER NOT NULL,
|
||||
`position` INTEGER NOT NULL,
|
||||
`type` TEXT NOT NULL
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_meta_accounts_substrateAccountId` ON `meta_accounts` (`substrateAccountId`)")
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_meta_accounts_ethereumAddress` ON `meta_accounts` (`ethereumAddress`)")
|
||||
|
||||
// insert to new from old
|
||||
database.execSQL(
|
||||
"""
|
||||
INSERT INTO meta_accounts
|
||||
SELECT *
|
||||
FROM meta_accounts_old
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
// delete old
|
||||
database.execSQL("DROP TABLE meta_accounts_old")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val AddLocks_22_23 = object : Migration(22, 23) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// new table
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `locks` (
|
||||
`metaId` INTEGER NOT NULL,
|
||||
`chainId` TEXT NOT NULL,
|
||||
`assetId` INTEGER NOT NULL,
|
||||
`type` TEXT NOT NULL,
|
||||
`amount` TEXT NOT NULL,
|
||||
PRIMARY KEY(`metaId`, `chainId`, `assetId`, `type`),
|
||||
FOREIGN KEY(`metaId`) REFERENCES `meta_accounts`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE ,
|
||||
FOREIGN KEY(`chainId`) REFERENCES `chains`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE ,
|
||||
FOREIGN KEY(`assetId`, `chainId`) REFERENCES `chain_assets`(`id`, `chainId`) ON UPDATE NO ACTION ON DELETE CASCADE
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val AddContributions_23_24 = object : Migration(23, 24) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `contributions` (
|
||||
`metaId` INTEGER NOT NULL,
|
||||
`chainId` TEXT NOT NULL,
|
||||
`assetId` INTEGER NOT NULL,
|
||||
`paraId` TEXT NOT NULL,
|
||||
`amountInPlanks` TEXT NOT NULL,
|
||||
`sourceId` TEXT NOT NULL,
|
||||
PRIMARY KEY(`metaId`, `chainId`, `assetId`, `paraId`, `sourceId`)
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val AddGovernanceFlagToChains_24_25 = object : Migration(24, 25) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE chains ADD COLUMN hasGovernance INTEGER NOT NULL DEFAULT 0")
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val AddGovernanceDapps_25_26 = object : Migration(25, 26) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// new table
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `governance_dapps` (
|
||||
`chainId` TEXT NOT NULL,
|
||||
`name` TEXT NOT NULL,
|
||||
`referendumUrl` TEXT NOT NULL,
|
||||
`iconUrl` TEXT NOT NULL,
|
||||
`details` TEXT NOT NULL,
|
||||
PRIMARY KEY(`chainId`, `name`)
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val GovernanceFlagToEnum_26_27 = object : Migration(26, 27) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// rename
|
||||
database.execSQL("ALTER TABLE chains RENAME TO chains_old")
|
||||
|
||||
// new table
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `chains` (
|
||||
`id` TEXT NOT NULL,
|
||||
`parentId` TEXT,
|
||||
`name` TEXT NOT NULL,
|
||||
`icon` TEXT NOT NULL,
|
||||
`prefix` INTEGER NOT NULL,
|
||||
`isEthereumBased` INTEGER NOT NULL,
|
||||
`isTestNet` INTEGER NOT NULL,
|
||||
`hasCrowdloans` INTEGER NOT NULL,
|
||||
`governance` TEXT NOT NULL,
|
||||
`additional` TEXT,
|
||||
`url` TEXT,
|
||||
`overridesCommon` INTEGER,
|
||||
`staking_url` TEXT,
|
||||
`staking_type` TEXT,
|
||||
`history_url` TEXT,
|
||||
`history_type` TEXT,
|
||||
`crowdloans_url` TEXT,
|
||||
`crowdloans_type` TEXT,
|
||||
PRIMARY KEY(`id`)
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val governanceDefault = "NONE"
|
||||
|
||||
// insert to new from old
|
||||
database.execSQL(
|
||||
// select all but color
|
||||
"""
|
||||
INSERT INTO chains
|
||||
SELECT id, parentId, name, icon, prefix, isEthereumBased, isTestNet, hasCrowdloans, "$governanceDefault", additional, url, overridesCommon,
|
||||
staking_url, staking_type, history_url, history_type, crowdloans_url, crowdloans_type
|
||||
FROM chains_old
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
// delete old
|
||||
database.execSQL("DROP TABLE chains_old")
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val AddGovernanceExternalApiToChain_27_28 = object : Migration(27, 28) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// new columns
|
||||
database.execSQL("ALTER TABLE `chains` ADD COLUMN `governance_url` TEXT")
|
||||
database.execSQL("ALTER TABLE `chains` ADD COLUMN `governance_type` TEXT")
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainAssetLocal
|
||||
|
||||
val AddSourceToLocalAsset_28_29 = object : Migration(28, 29) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE `chain_assets` ADD COLUMN `source` TEXT NOT NULL DEFAULT '${ChainAssetLocal.SOURCE_DEFAULT}'")
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val AddTransferApisTable_29_30 = object : Migration(29, 30) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
removeTransferApiFieldsFromChains(database)
|
||||
|
||||
addTransferApiTable(database)
|
||||
|
||||
clearOperationsCache(database)
|
||||
}
|
||||
|
||||
private fun clearOperationsCache(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DELETE FROM operations")
|
||||
}
|
||||
|
||||
private fun addTransferApiTable(database: SupportSQLiteDatabase) {
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `chain_transfer_history_apis` (
|
||||
`chainId` TEXT NOT NULL,
|
||||
`assetType` TEXT NOT NULL,
|
||||
`apiType` TEXT NOT NULL,
|
||||
`url` TEXT NOT NULL,
|
||||
PRIMARY KEY(`chainId`, `url`),
|
||||
FOREIGN KEY(`chainId`) REFERENCES `chains`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS `index_chain_transfer_history_apis_chainId` ON `chain_transfer_history_apis` (`chainId`)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
private fun removeTransferApiFieldsFromChains(database: SupportSQLiteDatabase) {
|
||||
// rename
|
||||
database.execSQL("ALTER TABLE chains RENAME TO chains_old")
|
||||
|
||||
// new table
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `chains` (
|
||||
`id` TEXT NOT NULL,
|
||||
`parentId` TEXT,
|
||||
`name` TEXT NOT NULL,
|
||||
`icon` TEXT NOT NULL,
|
||||
`prefix` INTEGER NOT NULL,
|
||||
`isEthereumBased` INTEGER NOT NULL,
|
||||
`isTestNet` INTEGER NOT NULL,
|
||||
`hasCrowdloans` INTEGER NOT NULL,
|
||||
`governance` TEXT NOT NULL,
|
||||
`additional` TEXT,
|
||||
`url` TEXT,
|
||||
`overridesCommon` INTEGER,
|
||||
`staking_url` TEXT,
|
||||
`staking_type` TEXT,
|
||||
`crowdloans_url` TEXT,
|
||||
`crowdloans_type` TEXT,
|
||||
`governance_url` TEXT,
|
||||
`governance_type` TEXT,
|
||||
PRIMARY KEY(`id`)
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
// insert to new from old
|
||||
database.execSQL(
|
||||
// select all but color
|
||||
"""
|
||||
INSERT INTO chains
|
||||
SELECT id, parentId, name, icon, prefix, isEthereumBased, isTestNet, hasCrowdloans, governance,
|
||||
additional, url, overridesCommon, staking_url, staking_type, crowdloans_url, crowdloans_type, governance_url, governance_type
|
||||
FROM chains_old
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
// delete old
|
||||
database.execSQL("DROP TABLE chains_old")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val AssetTypes_2_3 = object : Migration(2, 3) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE chain_assets ADD COLUMN type TEXT DEFAULT NULL")
|
||||
database.execSQL("ALTER TABLE chain_assets ADD COLUMN typeExtras TEXT DEFAULT NULL")
|
||||
database.execSQL("ALTER TABLE chain_assets ADD COLUMN icon TEXT DEFAULT NULL")
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainAssetLocal
|
||||
|
||||
val AddEnabledColumnToChainAssets_30_31 = object : Migration(30, 31) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE `chain_assets` ADD COLUMN `enabled` INTEGER NOT NULL DEFAULT ${ChainAssetLocal.ENABLED_DEFAULT_STR}")
|
||||
}
|
||||
}
|
||||
+272
@@ -0,0 +1,272 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainAssetLocal
|
||||
|
||||
/**
|
||||
* Due to previous migration of chain & meta account tables by means of rename-create-insert-delete strategy
|
||||
* foreign keys to these tables got renamed and now points to wrong table which causes crashes for subset of users
|
||||
* This migration recreates all affected tables
|
||||
*/
|
||||
val FixBrokenForeignKeys_31_32 = object : Migration(31, 32) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// foreign key to ChainLocal
|
||||
recreateChainAssets(database)
|
||||
|
||||
// foreign key to ChainAssetLocal which was recreated above
|
||||
recreateAssets(database)
|
||||
|
||||
// foreign key to MetaAccountLocal
|
||||
recreateChainAccount(database)
|
||||
|
||||
// foreign key to ChainLocal
|
||||
recreateChainRuntimeInfo(database)
|
||||
|
||||
// foreign key to ChainLocal
|
||||
recreateChainExplorers(database)
|
||||
|
||||
// foreign key to ChainLocal
|
||||
recreateChainNodes(database)
|
||||
|
||||
// foreign key to ChainLocal, ChainAssetLocal, MetaAccount
|
||||
recreateBalanceLocks(database)
|
||||
}
|
||||
|
||||
private fun recreateChainAssets(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP INDEX IF EXISTS `index_chain_assets_chainId`")
|
||||
|
||||
database.execSQL("ALTER TABLE chain_assets RENAME TO chain_assets_old")
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `chain_assets` (
|
||||
`id` INTEGER NOT NULL,
|
||||
`chainId` TEXT NOT NULL,
|
||||
`name` TEXT NOT NULL,
|
||||
`symbol` TEXT NOT NULL,
|
||||
`priceId` TEXT,
|
||||
`staking` TEXT NOT NULL,
|
||||
`precision` INTEGER NOT NULL,
|
||||
`icon` TEXT,
|
||||
`type` TEXT,
|
||||
`source` TEXT NOT NULL DEFAULT '${ChainAssetLocal.SOURCE_DEFAULT}',
|
||||
`buyProviders` TEXT,
|
||||
`typeExtras` TEXT,
|
||||
`enabled` INTEGER NOT NULL DEFAULT ${ChainAssetLocal.ENABLED_DEFAULT_STR},
|
||||
PRIMARY KEY(`chainId`,`id`),
|
||||
FOREIGN KEY(`chainId`) REFERENCES `chains`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
INSERT INTO chain_assets
|
||||
SELECT
|
||||
id, chainId, name, symbol, priceId,
|
||||
staking, precision, icon, type, source,
|
||||
buyProviders, typeExtras, enabled
|
||||
FROM chain_assets_old
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_chain_assets_chainId` ON `chain_assets` (`chainId`)")
|
||||
|
||||
database.execSQL("DROP TABLE chain_assets_old")
|
||||
}
|
||||
|
||||
private fun recreateAssets(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP INDEX IF EXISTS `index_assets_metaId`")
|
||||
database.execSQL("ALTER TABLE assets RENAME TO assets_old")
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `assets` (
|
||||
`assetId` INTEGER NOT NULL,
|
||||
`chainId` TEXT NOT NULL,
|
||||
`metaId` INTEGER NOT NULL,
|
||||
`freeInPlanks` TEXT NOT NULL,
|
||||
`frozenInPlanks` TEXT NOT NULL,
|
||||
`reservedInPlanks` TEXT NOT NULL,
|
||||
`bondedInPlanks` TEXT NOT NULL,
|
||||
`redeemableInPlanks` TEXT NOT NULL,
|
||||
`unbondingInPlanks` TEXT NOT NULL,
|
||||
PRIMARY KEY(`assetId`,`chainId`,`metaId`),
|
||||
FOREIGN KEY(`assetId`,`chainId`) REFERENCES `chain_assets`(`id`, `chainId`) ON UPDATE NO ACTION ON DELETE CASCADE
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
INSERT INTO assets
|
||||
SELECT
|
||||
assetId, chainId, metaId,
|
||||
freeInPlanks, frozenInPlanks, reservedInPlanks,
|
||||
bondedInPlanks, redeemableInPlanks, unbondingInPlanks
|
||||
FROM assets_old
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_assets_metaId` ON `assets` (`metaId`)")
|
||||
|
||||
database.execSQL("DROP TABLE assets_old")
|
||||
}
|
||||
|
||||
private fun recreateChainAccount(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP INDEX IF EXISTS `index_chain_accounts_chainId`")
|
||||
database.execSQL("DROP INDEX IF EXISTS `index_chain_accounts_metaId`")
|
||||
database.execSQL("DROP INDEX IF EXISTS `index_chain_accounts_accountId`")
|
||||
|
||||
database.execSQL("ALTER TABLE chain_accounts RENAME TO chain_accounts_old")
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `chain_accounts` (
|
||||
`metaId` INTEGER NOT NULL,
|
||||
`chainId` TEXT NOT NULL,
|
||||
`publicKey` BLOB,
|
||||
`accountId` BLOB NOT NULL,
|
||||
`cryptoType` TEXT,
|
||||
PRIMARY KEY(`metaId`, `chainId`),
|
||||
FOREIGN KEY(`metaId`) REFERENCES `meta_accounts`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
INSERT INTO chain_accounts
|
||||
SELECT metaId, chainId, publicKey, accountId, cryptoType
|
||||
FROM chain_accounts_old
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_chain_accounts_chainId` ON `chain_accounts` (`chainId`)")
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_chain_accounts_metaId` ON `chain_accounts` (`metaId`)")
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_chain_accounts_accountId` ON `chain_accounts` (`accountId`)")
|
||||
|
||||
database.execSQL("DROP TABLE chain_accounts_old")
|
||||
}
|
||||
|
||||
private fun recreateChainRuntimeInfo(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP INDEX IF EXISTS `index_chain_runtimes_chainId`")
|
||||
|
||||
database.execSQL("ALTER TABLE chain_runtimes RENAME TO chain_runtimes_old")
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `chain_runtimes` (
|
||||
`chainId` TEXT NOT NULL,
|
||||
`syncedVersion` INTEGER NOT NULL,
|
||||
`remoteVersion` INTEGER NOT NULL,
|
||||
PRIMARY KEY(`chainId`),
|
||||
FOREIGN KEY(`chainId`) REFERENCES `chains`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
INSERT INTO chain_runtimes
|
||||
SELECT chainId, syncedVersion, remoteVersion FROM chain_runtimes_old
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_chain_runtimes_chainId` ON `chain_runtimes` (`chainId`)")
|
||||
|
||||
database.execSQL("DROP TABLE chain_runtimes_old")
|
||||
}
|
||||
|
||||
private fun recreateChainExplorers(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP INDEX IF EXISTS `index_chain_explorers_chainId`")
|
||||
|
||||
database.execSQL("ALTER TABLE chain_explorers RENAME TO chain_explorers_old")
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `chain_explorers` (
|
||||
`chainId` TEXT NOT NULL,
|
||||
`name` TEXT NOT NULL,
|
||||
`extrinsic` TEXT,
|
||||
`account` TEXT,
|
||||
`event` TEXT,
|
||||
PRIMARY KEY(`chainId`, `name`),
|
||||
FOREIGN KEY(`chainId`) REFERENCES `chains`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
INSERT INTO chain_explorers
|
||||
SELECT chainId, name, extrinsic, account, event FROM chain_explorers_old
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_chain_explorers_chainId` ON `chain_explorers` (`chainId`)")
|
||||
|
||||
database.execSQL("DROP TABLE chain_explorers_old")
|
||||
}
|
||||
|
||||
private fun recreateChainNodes(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP INDEX IF EXISTS `index_chain_nodes_chainId`")
|
||||
|
||||
database.execSQL("ALTER TABLE chain_nodes RENAME TO chain_nodes_old")
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `chain_nodes` (
|
||||
`chainId` TEXT NOT NULL,
|
||||
`url` TEXT NOT NULL,
|
||||
`name` TEXT NOT NULL,
|
||||
`orderId` INTEGER NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY(`chainId`, `url`),
|
||||
FOREIGN KEY(`chainId`) REFERENCES `chains`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
INSERT INTO chain_nodes
|
||||
SELECT chainId, url, name, orderId FROM chain_nodes_old
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_chain_nodes_chainId` ON `chain_nodes` (`chainId`)")
|
||||
|
||||
database.execSQL("DROP TABLE chain_nodes_old")
|
||||
}
|
||||
|
||||
private fun recreateBalanceLocks(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE locks RENAME TO locks_old")
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `locks` (
|
||||
`metaId` INTEGER NOT NULL,
|
||||
`chainId` TEXT NOT NULL,
|
||||
`assetId` INTEGER NOT NULL,
|
||||
`type` TEXT NOT NULL,
|
||||
`amount` TEXT NOT NULL,
|
||||
PRIMARY KEY(`metaId`, `chainId`, `assetId`, `type`),
|
||||
FOREIGN KEY(`metaId`) REFERENCES `meta_accounts`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE,
|
||||
FOREIGN KEY(`chainId`) REFERENCES `chains`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE ,
|
||||
FOREIGN KEY(`assetId`, `chainId`) REFERENCES `chain_assets`(`id`, `chainId`) ON UPDATE NO ACTION ON DELETE CASCADE
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
INSERT INTO locks
|
||||
SELECT metaId, chainId, assetId, type, amount FROM locks_old
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL("DROP TABLE locks_old")
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val AddVersioningToGovernanceDapps_32_33 = object : Migration(32, 33) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP TABLE `governance_dapps`")
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `governance_dapps` (
|
||||
`chainId` TEXT NOT NULL,
|
||||
`name` TEXT NOT NULL,
|
||||
`referendumUrlV1` TEXT,
|
||||
`referendumUrlV2` TEXT,
|
||||
`iconUrl` TEXT NOT NULL,
|
||||
`details` TEXT NOT NULL,
|
||||
PRIMARY KEY(`chainId`, `name`)
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val AddGovernanceNetworkToExternalApi_33_34 = object : Migration(33, 34) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE chains ADD COLUMN `governance_parameters` TEXT")
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val AddBrowserHostSettings_34_35 = object : Migration(34, 35) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `browser_host_settings` (
|
||||
`hostUrl` TEXT NOT NULL,
|
||||
`isDesktopModeEnabled` INTEGER NOT NULL,
|
||||
PRIMARY KEY(`hostUrl`)
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val ExtractExternalApiToSeparateTable_35_36 = object : Migration(35, 36) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
removeExternalApisColumnsFromChains(database)
|
||||
|
||||
migrateExternalApisTable(database)
|
||||
|
||||
// recreating chainId causes broken foreign keys to appear on some devices
|
||||
// so we run this migration again to fix it
|
||||
FixBrokenForeignKeys_31_32.migrate(database)
|
||||
}
|
||||
|
||||
private fun migrateExternalApisTable(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP TABLE chain_transfer_history_apis")
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `chain_external_apis` (
|
||||
`chainId` TEXT NOT NULL,
|
||||
`sourceType` TEXT NOT NULL,
|
||||
`apiType` TEXT NOT NULL,
|
||||
`parameters` TEXT,
|
||||
`url` TEXT NOT NULL,
|
||||
PRIMARY KEY(`chainId`, `url`, `apiType`),
|
||||
FOREIGN KEY(`chainId`) REFERENCES `chains`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_chain_external_apis_chainId` ON `chain_external_apis` (`chainId`)")
|
||||
}
|
||||
|
||||
private fun removeExternalApisColumnsFromChains(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE chains RENAME TO chains_old")
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `chains` (
|
||||
`id` TEXT NOT NULL,
|
||||
`parentId` TEXT,
|
||||
`name` TEXT NOT NULL,
|
||||
`icon` TEXT NOT NULL,
|
||||
`prefix` INTEGER NOT NULL,
|
||||
`isEthereumBased` INTEGER NOT NULL,
|
||||
`isTestNet` INTEGER NOT NULL,
|
||||
`hasCrowdloans` INTEGER NOT NULL,
|
||||
`governance` TEXT NOT NULL,
|
||||
`additional` TEXT,
|
||||
`url` TEXT,
|
||||
`overridesCommon` INTEGER,
|
||||
PRIMARY KEY(`id`)
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
INSERT INTO chains
|
||||
SELECT
|
||||
id, parentId, name, icon, prefix,
|
||||
isEthereumBased, isTestNet, hasCrowdloans, governance, additional,
|
||||
url, overridesCommon
|
||||
FROM chains_old
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL("DROP TABLE chains_old")
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainLocal
|
||||
|
||||
val AddRuntimeFlagToChains_36_37 = object : Migration(36, 37) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
val default = ChainLocal.Default.HAS_SUBSTRATE_RUNTIME
|
||||
|
||||
database.execSQL("ALTER TABLE chains ADD COLUMN `hasSubstrateRuntime` INTEGER NOT NULL DEFAULT $default")
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val AddExtrinsicContentField_37_38 = object : Migration(37, 38) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP TABLE operations")
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `operations` (
|
||||
`id` TEXT NOT NULL,
|
||||
`address` TEXT NOT NULL,
|
||||
`chainId` TEXT NOT NULL,
|
||||
`chainAssetId` INTEGER NOT NULL,
|
||||
`time` INTEGER NOT NULL,
|
||||
`status` INTEGER NOT NULL,
|
||||
`source` INTEGER NOT NULL,
|
||||
`operationType` INTEGER NOT NULL,
|
||||
`amount` TEXT,
|
||||
`sender` TEXT,
|
||||
`receiver` TEXT,
|
||||
`hash` TEXT,
|
||||
`fee` TEXT,
|
||||
`isReward` INTEGER,
|
||||
`era` INTEGER,
|
||||
`validator` TEXT,
|
||||
`extrinsicContent_type` TEXT,
|
||||
`extrinsicContent_module` TEXT,
|
||||
`extrinsicContent_call` TEXT,
|
||||
PRIMARY KEY(`id`, `address`, `chainId`, `chainAssetId`)
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainLocal
|
||||
|
||||
val AddNodeSelectionStrategyField_38_39 = object : Migration(38, 39) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
val default = ChainLocal.Default.NODE_SELECTION_STRATEGY_DEFAULT
|
||||
|
||||
database.execSQL("ALTER TABLE chains ADD COLUMN `nodeSelectionStrategy` TEXT NOT NULL DEFAULT '$default'")
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val AddWalletConnectSessions_39_40 = object : Migration(39, 40) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `wallet_connect_sessions` (
|
||||
`sessionTopic` TEXT NOT NULL,
|
||||
`metaId` INTEGER NOT NULL,
|
||||
PRIMARY KEY(`sessionTopic`),
|
||||
FOREIGN KEY(`metaId`) REFERENCES `meta_accounts`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_wallet_connect_sessions_metaId` ON `wallet_connect_sessions` (`metaId`)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val ChangeAsset_3_4 = object : Migration(3, 4) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP TABLE assets")
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `assets` (
|
||||
`tokenSymbol` TEXT NOT NULL,
|
||||
`chainId` TEXT NOT NULL,
|
||||
`metaId` INTEGER NOT NULL,
|
||||
`freeInPlanks` TEXT NOT NULL,
|
||||
`frozenInPlanks` TEXT NOT NULL,
|
||||
`reservedInPlanks` TEXT NOT NULL,
|
||||
`bondedInPlanks` TEXT NOT NULL,
|
||||
`redeemableInPlanks` TEXT NOT NULL,
|
||||
`unbondingInPlanks` TEXT NOT NULL,
|
||||
PRIMARY KEY(`tokenSymbol`, `chainId`, `metaId`)
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_assets_metaId` ON `assets` (`metaId`)")
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val AddStakingDashboardItems_41_42 = object : Migration(41, 42) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `staking_dashboard_items` (
|
||||
`chainId` TEXT NOT NULL,
|
||||
`chainAssetId` INTEGER NOT NULL,
|
||||
`stakingType` TEXT NOT NULL,
|
||||
`metaId` INTEGER NOT NULL,
|
||||
`hasStake` INTEGER NOT NULL,
|
||||
`stake` TEXT,
|
||||
`status` TEXT,
|
||||
`rewards` TEXT,
|
||||
`estimatedEarnings` REAL,
|
||||
`primaryStakingAccountId` BLOB,
|
||||
PRIMARY KEY(`chainId`, `chainAssetId`, `stakingType`, `metaId`),
|
||||
FOREIGN KEY(`chainId`) REFERENCES `chains`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE,
|
||||
FOREIGN KEY(`chainAssetId`, `chainId`) REFERENCES `chain_assets`(`id`, `chainId`) ON UPDATE NO ACTION ON DELETE CASCADE ,
|
||||
FOREIGN KEY(`metaId`) REFERENCES `meta_accounts`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_staking_dashboard_items_metaId` ON `staking_dashboard_items` (`metaId`)")
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val TransferFiatAmount_40_41 = object : Migration(40, 41) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
createCoinPriceTable(database)
|
||||
}
|
||||
|
||||
private fun createCoinPriceTable(database: SupportSQLiteDatabase) {
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `coin_prices` (
|
||||
`priceId` TEXT NOT NULL,
|
||||
`currencyId` TEXT NOT NULL,
|
||||
`timestamp` INTEGER NOT NULL,
|
||||
`rate` TEXT NOT NULL,
|
||||
PRIMARY KEY(`priceId`, `currencyId`, `timestamp`)
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val StakingRewardPeriods_42_43 = object : Migration(42, 43) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
createCoinPriceTable(database)
|
||||
}
|
||||
|
||||
private fun createCoinPriceTable(database: SupportSQLiteDatabase) {
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `staking_reward_period` (
|
||||
`accountId` BLOB NOT NULL,
|
||||
`chainId` TEXT NOT NULL,
|
||||
`assetId` INTEGER NOT NULL,
|
||||
`stakingType` TEXT NOT NULL,
|
||||
`periodType` TEXT NOT NULL,
|
||||
`customPeriodStart` INTEGER,
|
||||
`customPeriodEnd` INTEGER,
|
||||
PRIMARY KEY(`accountId`, `chainId`, `assetId`, `stakingType`))
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user