Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Db/transaction #4594

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
65 changes: 65 additions & 0 deletions app/schemas/one.mixin.android.db.web3.Web3Database/1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "0c99118d4d50976b049b3a62a3d9b14f",
"entities": [
{
"tableName": "transactions",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`transaction_hash` TEXT NOT NULL, `chain_id` TEXT NOT NULL, `address` TEXT NOT NULL, `raw_transaction` TEXT NOT NULL, `nonce` INTEGER NOT NULL, `created_at` TEXT NOT NULL, PRIMARY KEY(`transaction_hash`, `chain_id`))",
"fields": [
{
"fieldPath": "transactionHash",
"columnName": "transaction_hash",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "chainId",
"columnName": "chain_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "address",
"columnName": "address",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "rawTransaction",
"columnName": "raw_transaction",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "nonce",
"columnName": "nonce",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "createdAt",
"columnName": "created_at",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"transaction_hash",
"chain_id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0c99118d4d50976b049b3a62a3d9b14f')"
]
}
}
1 change: 1 addition & 0 deletions app/src/main/java/one/mixin/android/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ object Constants {

const val FTS_DB_NAME = "fts.db"
const val PENDING_DB_NAME = "pending.db"
const val WEB3_DB_NAME = "web3.db"
}

object Storage {
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/one/mixin/android/MixinApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import one.mixin.android.crypto.MixinSignalProtocolLogger
import one.mixin.android.crypto.PrivacyPreference.clearPrivacyPreferences
import one.mixin.android.crypto.db.SignalDatabase
import one.mixin.android.db.MixinDatabase
import one.mixin.android.db.web3.Web3Database
import one.mixin.android.di.ApplicationScope
import one.mixin.android.extension.defaultSharedPreferences
import one.mixin.android.extension.getStackTraceInfo
Expand Down Expand Up @@ -129,6 +130,9 @@ open class MixinApplication :
@ApplicationScope
lateinit var applicationScope: CoroutineScope

@Inject
lateinit var web3Database: Web3Database

override fun onCreate() {
super.onCreate()
init()
Expand Down
12 changes: 12 additions & 0 deletions app/src/main/java/one/mixin/android/db/web3/TransactionDao.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package one.mixin.android.db.web3

import androidx.room.Dao
import androidx.room.Query
import one.mixin.android.db.BaseDao
import one.mixin.android.vo.web3.Transaction

@Dao
interface TransactionDao : BaseDao<Transaction> {
@Query("SELECT nonce FROM transactions WHERE chain_id = :chainId ORDER BY nonce DESC LIMIT 1")
fun lastNonce(chainId: String): Long?
}
41 changes: 41 additions & 0 deletions app/src/main/java/one/mixin/android/db/web3/Web3Database.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package one.mixin.android.db.web3

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import one.mixin.android.Constants.DataBase.WEB3_DB_NAME
import one.mixin.android.vo.web3.Transaction

@Database(
entities = [
Transaction::class
],
version = 1,
)
abstract class Web3Database : RoomDatabase() {
abstract fun transactionDao(): TransactionDao

companion object {
private var INSTANCE: Web3Database? = null

private val lock = Any()

fun getDatabase(
context: Context,
): Web3Database {
synchronized(lock) {
if (INSTANCE == null) {
val builder =
Room.databaseBuilder(
context,
Web3Database::class.java,
WEB3_DB_NAME,
).enableMultiInstanceInvalidation()
INSTANCE = builder.build()
}
}
return INSTANCE as Web3Database
}
}
}
9 changes: 9 additions & 0 deletions app/src/main/java/one/mixin/android/di/BaseDbModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import one.mixin.android.crypto.db.SignalDatabase
import one.mixin.android.db.MixinDatabase
import one.mixin.android.db.pending.PendingDatabase
import one.mixin.android.db.pending.PendingDatabaseImp
import one.mixin.android.db.web3.Web3Database
import one.mixin.android.fts.FtsDatabase
import javax.inject.Singleton

Expand Down Expand Up @@ -38,6 +39,14 @@ internal object BaseDbModule {
mixinDatabase: MixinDatabase,
): PendingDatabase = PendingDatabaseImp.getDatabase(app.applicationContext, mixinDatabase.floodMessageDao(), mixinDatabase.jobDao())

@Singleton
@Provides
fun provideWeb3Database(app: Application) = Web3Database.getDatabase(app)

@Singleton
@Provides
fun provideTransactionDao(db: Web3Database) = db.transactionDao()

@Singleton
@Provides
fun provideUserDao(db: MixinDatabase) = db.userDao()
Expand Down
15 changes: 14 additions & 1 deletion app/src/main/java/one/mixin/android/tip/wc/WalletConnectV2.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package one.mixin.android.tip.wc

import androidx.room.ColumnInfo
import com.github.salomonbrys.kotson.fromJson
import com.github.salomonbrys.kotson.registerTypeAdapter
import com.google.gson.GsonBuilder
Expand All @@ -14,6 +15,9 @@ import one.mixin.android.BuildConfig
import one.mixin.android.MixinApplication
import one.mixin.android.R
import one.mixin.android.RxBus
import one.mixin.android.db.web3.TransactionDao
import one.mixin.android.db.web3.Web3Database
import one.mixin.android.extension.nowInUtc
import one.mixin.android.tip.wc.internal.Chain
import one.mixin.android.tip.wc.internal.Method
import one.mixin.android.tip.wc.internal.WCEthereumSignMessage
Expand All @@ -25,9 +29,12 @@ import one.mixin.android.tip.wc.internal.WcSolanaTransaction
import one.mixin.android.tip.wc.internal.ethTransactionSerializer
import one.mixin.android.tip.wc.internal.getSupportedNamespaces
import one.mixin.android.tip.wc.internal.supportChainList
import one.mixin.android.tip.wc.internal.web3ChainId
import one.mixin.android.ui.tip.wc.WalletUnlockBottomSheetDialogFragment
import one.mixin.android.util.decodeBase58
import one.mixin.android.util.encodeToBase58String
import one.mixin.android.web3.js.JsSigner
import one.mixin.android.webrtc.CallDebugLiveData
import org.sol4k.Connection
import org.sol4k.Keypair
import org.sol4k.RpcUrl
Expand All @@ -48,10 +55,15 @@ import timber.log.Timber
import java.math.BigInteger
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import javax.inject.Inject

object WalletConnectV2 : WalletConnect() {
const val TAG = "WalletConnectV2"

private val transactionDao by lazy {
MixinApplication.get().web3Database.transactionDao()
}

private val gson =
GsonBuilder()
.serializeNulls()
Expand Down Expand Up @@ -592,7 +604,7 @@ object WalletConnectV2 : WalletConnect() {
if (transactionCount.hasError()) {
throwError(transactionCount.error)
}
val nonce = transactionCount.transactionCount
val nonce = transactionDao.lastNonce(chain.web3ChainId())?.let { BigInteger.valueOf(it + 1) } ?: transactionCount.transactionCount
val v = Numeric.toBigInt(value)
val tipGas = signData.tipGas
if (tipGas == null) {
Expand Down Expand Up @@ -623,6 +635,7 @@ object WalletConnectV2 : WalletConnect() {
if (approve) {
approveRequestInternal(hexMessage, sessionRequest)
}
transactionDao.insert(one.mixin.android.vo.web3.Transaction(hexMessage, chain.web3ChainId(), credential.address, hexMessage, nonce.toLong(), nowInUtc()))
return hexMessage
}

Expand Down
14 changes: 14 additions & 0 deletions app/src/main/java/one/mixin/android/tip/wc/internal/Chain.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,20 @@ sealed class Chain(
}
}

fun Chain.web3ChainId():String {
return when {
this is Chain.Ethereum-> "ethereum"
this is Chain.Arbitrum-> "arbitrum"
this is Chain.Optimism-> "optimism"
this is Chain.Base-> "base"
this is Chain.BinanceSmartChain-> "binance-smart-chain"
this is Chain.Polygon-> "polygon"
this is Chain.Avalanche-> "avalanche"
this is Chain.Solana-> "solana"
else -> throw IllegalArgumentException("Not support chain")
}
}

internal val supportChainList = listOf(Chain.Ethereum, Chain.Base, Chain.Arbitrum, Chain.Optimism, Chain.BinanceSmartChain, Chain.Polygon, Chain.Avalanche, Chain.Solana)
internal val evmChainList = listOf(Chain.Ethereum, Chain.Base, Chain.Arbitrum, Chain.Optimism, Chain.BinanceSmartChain, Chain.BinanceSmartChain, Chain.Polygon, Chain.Avalanche)

Expand Down
23 changes: 23 additions & 0 deletions app/src/main/java/one/mixin/android/vo/web3/Transaction.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package one.mixin.android.vo.web3

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "transactions",
primaryKeys = ["transaction_hash", "chain_id"]
)
class Transaction(
@ColumnInfo("transaction_hash")
val transactionHash: String,
@ColumnInfo("chain_id")
val chainId: String,
@ColumnInfo("address")
val address: String,
@ColumnInfo("raw_transaction")
val rawTransaction: String,
@ColumnInfo("nonce")
val nonce: Long,
@ColumnInfo("created_at")
val createdAt: String,
)
10 changes: 9 additions & 1 deletion app/src/main/java/one/mixin/android/web3/js/JsSigner.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ import okhttp3.logging.HttpLoggingInterceptor
import one.mixin.android.BuildConfig
import one.mixin.android.Constants.Account.ChainAddress.EVM_ADDRESS
import one.mixin.android.Constants.Account.ChainAddress.SOLANA_ADDRESS
import one.mixin.android.MixinApplication
import one.mixin.android.db.property.PropertyHelper
import one.mixin.android.extension.hexStringToByteArray
import one.mixin.android.extension.nowInUtc
import one.mixin.android.extension.toHex
import one.mixin.android.tip.wc.WalletConnect
import one.mixin.android.tip.wc.internal.Chain
import one.mixin.android.tip.wc.internal.TipGas
import one.mixin.android.tip.wc.internal.WCEthereumTransaction
import one.mixin.android.tip.wc.internal.WalletConnectException
import one.mixin.android.tip.wc.internal.web3ChainId
import one.mixin.android.tip.wc.internal.evmChainList
import one.mixin.android.util.GsonHelper
import one.mixin.android.util.decodeBase58
Expand Down Expand Up @@ -47,6 +50,10 @@ object JsSigner {
data object Solana : JsSignerNetwork("solana")
}

private val transactionDao by lazy {
MixinApplication.get().web3Database.transactionDao()
}

private const val TAG = "JsSigner"

private var web3jPool = LruCache<Chain, Web3j>(3)
Expand Down Expand Up @@ -192,7 +199,7 @@ object JsSigner {
if (transactionCount.hasError()) {
throwError(transactionCount.error)
}
val nonce = transactionCount.transactionCount
val nonce = transactionDao.lastNonce((chain ?: currentChain).web3ChainId())?.let { BigInteger.valueOf(it + 1) } ?: transactionCount.transactionCount
val v = Numeric.toBigInt(value)

val maxPriorityFeePerGas = tipGas.ethMaxPriorityFeePerGas
Expand Down Expand Up @@ -222,6 +229,7 @@ object JsSigner {

val signedMessage = TransactionEncoder.signMessage(rawTransaction, (chain ?: currentChain).chainReference.toLong(), credential)
val hexMessage = Numeric.toHexString(signedMessage)
transactionDao.insert(one.mixin.android.vo.web3.Transaction(hexMessage, (chain ?: currentChain).web3ChainId(), address, hexMessage, nonce.toLong(), nowInUtc()))
Timber.d("$TAG signTransaction $hexMessage")
return hexMessage
}
Expand Down
Loading