Add read-only Tron (TRC20) chain family support (#9)

* Add read-only Tron (TRC20) chain family support

Adds Tron as a third chain family alongside Substrate and EVM:
address derivation (BIP44 coin type 195, secp256k1 reused from the
existing Ethereum path, Base58Check encoding), TronGrid-backed TRX
and TRC20 USDT balance display, and a Room migration for the new
per-account Tron key/address columns. Read-only only — no send,
signing, or broadcast support yet.

* fix: exhaustive when branches for Trc20/TronNative asset types

ChainExt.kt's onChainAssetId and Common.kt's existentialDepositError
both switch exhaustively over Chain.Asset.Type and didn't have cases
for the new Trc20/TronNative variants, breaking compilation. Trc20
mirrors EvmErc20 (contract address as the on-chain id, dust burns
rather than transfers on removal); TronNative mirrors EvmNative.
This commit is contained in:
2026-07-07 06:03:20 -07:00
committed by GitHub
parent 85bde7e448
commit 46d2a91510
39 changed files with 864 additions and 41 deletions
@@ -57,6 +57,7 @@ fun chainOf(
legacyPrefix = null,
isTestNet = false,
isEthereumBased = false,
isTronBased = false,
hasCrowdloans = false,
additional = "",
governance = "governance",
@@ -94,6 +94,7 @@ import io.novafoundation.nova.core_db.migrations.AddStakingTypeToTotalRewards_44
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.AddTronSupport_73_74
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
@@ -166,7 +167,7 @@ import io.novafoundation.nova.core_db.model.operation.SwapTypeLocal
import io.novafoundation.nova.core_db.model.operation.TransferTypeLocal
@Database(
version = 73,
version = 74,
entities = [
AccountLocal::class,
NodeLocal::class,
@@ -271,6 +272,7 @@ abstract class AppDatabase : RoomDatabase() {
.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)
.addMigrations(AddTronSupport_73_74)
.build()
}
return instance!!
@@ -0,0 +1,14 @@
package io.novafoundation.nova.core_db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
val AddTronSupport_73_74 = object : Migration(73, 74) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE chains ADD COLUMN isTronBased INTEGER NOT NULL DEFAULT 0")
db.execSQL("ALTER TABLE meta_accounts ADD COLUMN tronPublicKey BLOB")
db.execSQL("ALTER TABLE meta_accounts ADD COLUMN tronAddress BLOB")
}
}
@@ -21,6 +21,8 @@ data class ChainLocal(
val prefix: Int,
val legacyPrefix: Int?,
val isEthereumBased: Boolean,
@ColumnInfo(defaultValue = "0")
val isTronBased: Boolean,
val isTestNet: Boolean,
@ColumnInfo(defaultValue = "1")
val hasSubstrateRuntime: Boolean,
@@ -36,7 +36,9 @@ class MetaAccountLocal(
@ColumnInfo(defaultValue = "ACTIVE")
val status: Status,
val globallyUniqueId: String,
val typeExtras: SerializedJson?
val typeExtras: SerializedJson?,
val tronPublicKey: ByteArray? = null,
val tronAddress: ByteArray? = null,
) {
enum class Status {
@@ -54,6 +56,9 @@ class MetaAccountLocal(
const val ETHEREUM_PUBKEY = "ethereumPublicKey"
const val ETHEREUM_ADDRESS = "ethereumAddress"
const val TRON_PUBKEY = "tronPublicKey"
const val TRON_ADDRESS = "tronAddress"
const val NAME = "name"
const val IS_SELECTED = "isSelected"
const val POSITION = "position"
@@ -83,7 +88,9 @@ class MetaAccountLocal(
type = type,
status = status,
globallyUniqueId = globallyUniqueId,
typeExtras = typeExtras
typeExtras = typeExtras,
tronPublicKey = tronPublicKey,
tronAddress = tronAddress
).also {
it.id = id
}
@@ -100,6 +107,8 @@ class MetaAccountLocal(
if (!substrateAccountId.contentEquals(other.substrateAccountId)) return false
if (!ethereumPublicKey.contentEquals(other.ethereumPublicKey)) return false
if (!ethereumAddress.contentEquals(other.ethereumAddress)) return false
if (!tronPublicKey.contentEquals(other.tronPublicKey)) return false
if (!tronAddress.contentEquals(other.tronAddress)) return false
if (name != other.name) return false
if (parentMetaId != other.parentMetaId) return false
if (isSelected != other.isSelected) return false
@@ -119,6 +128,8 @@ class MetaAccountLocal(
result = 31 * result + (substrateAccountId?.contentHashCode() ?: 0)
result = 31 * result + (ethereumPublicKey?.contentHashCode() ?: 0)
result = 31 * result + (ethereumAddress?.contentHashCode() ?: 0)
result = 31 * result + (tronPublicKey?.contentHashCode() ?: 0)
result = 31 * result + (tronAddress?.contentHashCode() ?: 0)
result = 31 * result + name.hashCode()
result = 31 * result + (parentMetaId?.hashCode() ?: 0)
result = 31 * result + isSelected.hashCode()