Skip to content

Commit ab1b304

Browse files
committed
Merge remote-tracking branch 'origin/master' into feature/user_address
2 parents 2e307a3 + a0e05d2 commit ab1b304

File tree

15 files changed

+128
-71
lines changed

15 files changed

+128
-71
lines changed

app/src/main/java/one/mixin/android/api/request/web3/Web3AddressRequest.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,11 @@ data class Web3AddressRequest(
1010
val chainId: String,
1111

1212
@SerializedName("path")
13-
val path: String?
13+
val path: String?,
14+
15+
@SerializedName("signature")
16+
val signature: String? = null,
17+
18+
@SerializedName("timestamp")
19+
val timestamp: String? = null
1420
)

app/src/main/java/one/mixin/android/api/service/TokenService.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,6 @@ interface TokenService {
8080
@GET("safe/deposits")
8181
suspend fun pendingDeposits(
8282
@Query("asset") asset: String,
83-
@Query("destination") key: String,
84-
@Query("tag") tag: String? = null,
8583
): MixinResponse<List<PendingDeposit>>
8684

8785
@GET("network/assets/search/{query}")

app/src/main/java/one/mixin/android/crypto/CryptoWalletHelper.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,4 +246,5 @@ object CryptoWalletHelper {
246246
fun clear(context: Context) {
247247
context.deleteSharedPreferences(ENCRYPTED_WEB3_KEY)
248248
}
249+
249250
}

app/src/main/java/one/mixin/android/extension/ContextExtension.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1065,7 +1065,8 @@ fun Fragment.getTipsByAsset(asset: TokenItem) =
10651065
Constants.ChainId.BITCOIN_CHAIN_ID -> getString(R.string.deposit_tip_btc)
10661066
Constants.ChainId.TRON_CHAIN_ID -> getString(R.string.deposit_tip_trx)
10671067
Constants.ChainId.ETHEREUM_CHAIN_ID -> getString(R.string.deposit_tip_eth)
1068-
Constants.ChainId.LIGHTNING_NETWORK_CHAIN_ID, Constants.ChainId.Litecoin, Constants.ChainId.RIPPLE_CHAIN_ID, Constants.ChainId.Dogecoin, Constants.ChainId.Monero, Constants.ChainId.MobileCoin, -> getString(R.string.deposit_tip_common, asset.symbol)
1068+
Constants.ChainId.LIGHTNING_NETWORK_CHAIN_ID -> getString(R.string.deposit_tip_lightning)
1069+
Constants.ChainId.Litecoin, Constants.ChainId.RIPPLE_CHAIN_ID, Constants.ChainId.Dogecoin, Constants.ChainId.Monero, Constants.ChainId.MobileCoin, -> getString(R.string.deposit_tip_common, asset.symbol)
10691070
else -> getString(R.string.deposit_tip_chain, asset.symbol, getChainName(asset.chainId, asset.chainName, asset.assetKey?:""))
10701071
}
10711072

app/src/main/java/one/mixin/android/repository/TokenRepository.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -564,10 +564,7 @@ class TokenRepository
564564

565565
suspend fun pendingDeposits(
566566
asset: String,
567-
destination: String,
568-
tag: String? = null,
569-
) =
570-
tokenService.pendingDeposits(asset, destination, tag)
567+
) = tokenService.pendingDeposits(asset)
571568

572569
suspend fun clearAllPendingDeposits() = safeSnapshotDao.clearAllPendingDeposits()
573570

app/src/main/java/one/mixin/android/ui/wallet/TransactionsFragment.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ class TransactionsFragment : BaseFragment(R.layout.fragment_transactions), OnSna
261261
lifecycleScope.launch {
262262
val depositEntry = walletViewModel.findAndSyncDepositEntry(asset.chainId, asset.assetId)
263263
if (depositEntry != null && depositEntry.destination.isNotBlank()) {
264-
refreshPendingDeposits(asset, depositEntry)
264+
refreshPendingDeposits(asset)
265265
}
266266
}
267267
}
@@ -275,13 +275,12 @@ class TransactionsFragment : BaseFragment(R.layout.fragment_transactions), OnSna
275275

276276
private fun refreshPendingDeposits(
277277
asset: TokenItem,
278-
depositEntry: DepositEntry,
279278
) {
280279
if (viewDestroyed()) return
281280
lifecycleScope.launch {
282281
handleMixinResponse(
283282
invokeNetwork = {
284-
walletViewModel.refreshPendingDeposits(asset.assetId, depositEntry)
283+
walletViewModel.refreshPendingDeposits(asset.assetId)
285284
},
286285
exceptionBlock = { e ->
287286
reportException(e)

app/src/main/java/one/mixin/android/ui/wallet/WalletViewModel.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,7 @@ internal constructor(
181181

182182
suspend fun refreshPendingDeposits(
183183
assetId: String,
184-
depositEntry: DepositEntry,
185-
) = tokenRepository.pendingDeposits(assetId, requireNotNull(depositEntry.destination) { "refreshPendingDeposit required destination not null" }, depositEntry.tag)
184+
) = tokenRepository.pendingDeposits(assetId)
186185

187186
fun getPendingDisplays() = tokenRepository.getPendingDisplays()
188187

app/src/main/java/one/mixin/android/ui/wallet/viewmodel/FetchWalletViewModel.kt

Lines changed: 91 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ import one.mixin.android.job.MixinJobManager
2121
import one.mixin.android.job.RefreshSingleWalletJob
2222
import one.mixin.android.repository.UserRepository
2323
import one.mixin.android.repository.Web3Repository
24+
import one.mixin.android.session.Session
2425
import one.mixin.android.tip.Tip
2526
import one.mixin.android.tip.bip44.Bip44Path
2627
import one.mixin.android.tip.privateKeyToAddress
28+
import one.mixin.android.tip.tipPrivToPrivateKey
2729
import one.mixin.android.ui.wallet.WalletSecurityActivity
2830
import one.mixin.android.ui.wallet.components.FetchWalletState
2931
import one.mixin.android.ui.wallet.components.IndexedWallet
@@ -32,11 +34,14 @@ import one.mixin.android.util.ErrorHandler
3234
import one.mixin.android.util.encodeToBase58String
3335
import one.mixin.android.util.getMixinErrorStringByCode
3436
import one.mixin.android.vo.WalletCategory
37+
import one.mixin.android.web3.js.JsSignMessage
3538
import one.mixin.android.web3.js.Web3Signer
39+
import org.sol4k.Base58
3640
import org.sol4k.Keypair
3741
import org.web3j.utils.Numeric
3842
import timber.log.Timber
3943
import java.math.BigDecimal
44+
import java.time.Instant
4045
import javax.inject.Inject
4146

4247
@HiltViewModel
@@ -184,15 +189,19 @@ class FetchWalletViewModel @Inject constructor(
184189
val walletsToCreate = selectedWalletInfos.value.map {
185190
val category = WalletCategory.IMPORTED_MNEMONIC.value
186191
val addresses = listOf(
187-
Web3AddressRequest(
192+
createSignedWeb3AddressRequest(
188193
destination = it.ethereumWallet.address,
189194
chainId = Constants.ChainId.ETHEREUM_CHAIN_ID,
190-
path = it.ethereumWallet.path
195+
path = it.ethereumWallet.path,
196+
privateKey = it.ethereumWallet.privateKey,
197+
category = category
191198
),
192-
Web3AddressRequest(
199+
createSignedWeb3AddressRequest(
193200
destination = it.solanaWallet.address,
194201
chainId = Constants.ChainId.SOLANA_CHAIN_ID,
195-
path = it.solanaWallet.path
202+
path = it.solanaWallet.path,
203+
privateKey = it.solanaWallet.privateKey,
204+
category = category
196205
)
197206
)
198207
Pair(WalletRequest(it.name, category, addresses), it.solanaWallet.mnemonic.split(" "))
@@ -357,10 +366,18 @@ class FetchWalletViewModel @Inject constructor(
357366
regex.find(name)?.groupValues?.get(1)?.toIntOrNull()
358367
}.maxOrNull() ?: 0
359368
val name = "${MixinApplication.appContext.getString(if (mode == WalletSecurityActivity.Mode.ADD_WATCH_ADDRESS) R.string.Watch_Wallet else R.string.Common_Wallet)} ${maxIndex + 1}"
360-
val web3AddressRequest = Web3AddressRequest(
369+
val web3AddressRequest = createSignedWeb3AddressRequest(
361370
destination = address,
362371
chainId = chainId,
363-
path = null
372+
path = null,
373+
privateKey = key.let {
374+
if (chainId == Constants.ChainId.SOLANA_CHAIN_ID) {
375+
Base58.decode(key)
376+
} else {
377+
Numeric.hexStringToByteArray(key)
378+
}
379+
},
380+
category = category.value
364381
)
365382

366383
val walletRequest = WalletRequest(
@@ -472,15 +489,22 @@ class FetchWalletViewModel @Inject constructor(
472489
val walletRequest = WalletRequest(
473490
name = name,
474491
category = WalletCategory.CLASSIC.value,
475-
addresses = listOf(Web3AddressRequest(
476-
destination = evmAddress,
477-
chainId = Constants.ChainId.ETHEREUM_CHAIN_ID,
478-
path = Bip44Path.ethereumPathString(classicIndex)
479-
), Web3AddressRequest(
480-
destination = solAddress,
481-
chainId = Constants.ChainId.SOLANA_CHAIN_ID,
482-
path = Bip44Path.solanaPathString(classicIndex)
483-
))
492+
addresses = listOf(
493+
createSignedWeb3AddressRequest(
494+
destination = evmAddress,
495+
chainId = Constants.ChainId.ETHEREUM_CHAIN_ID,
496+
path = Bip44Path.ethereumPathString(classicIndex),
497+
privateKey = tipPrivToPrivateKey(currentSpendKey, Constants.ChainId.ETHEREUM_CHAIN_ID, classicIndex),
498+
category = WalletCategory.CLASSIC.value
499+
),
500+
createSignedWeb3AddressRequest(
501+
destination = solAddress,
502+
chainId = Constants.ChainId.SOLANA_CHAIN_ID,
503+
path = Bip44Path.solanaPathString(classicIndex),
504+
privateKey = tipPrivToPrivateKey(currentSpendKey, Constants.ChainId.SOLANA_CHAIN_ID, classicIndex),
505+
category = WalletCategory.CLASSIC.value
506+
)
507+
)
484508
)
485509
saveImportedWallet(walletRequest, null)
486510
} catch (e: Exception) {
@@ -489,4 +513,55 @@ class FetchWalletViewModel @Inject constructor(
489513
}
490514
}
491515
}
492-
}
516+
private fun createSignedWeb3AddressRequest(
517+
destination: String,
518+
chainId: String,
519+
path: String?,
520+
privateKey: String,
521+
category: String
522+
): Web3AddressRequest {
523+
return if (chainId == Constants.ChainId.SOLANA_CHAIN_ID) {
524+
val privateKeyBytes = Numeric.hexStringToByteArray(privateKey)
525+
createSignedWeb3AddressRequest(destination, chainId, path, privateKeyBytes, category)
526+
} else {
527+
val privateKeyBytes = Base58.decode(privateKey)
528+
createSignedWeb3AddressRequest(destination, chainId, path, privateKeyBytes, category)
529+
}
530+
}
531+
532+
private fun createSignedWeb3AddressRequest(
533+
destination: String,
534+
chainId: String,
535+
path: String?,
536+
privateKey: ByteArray?,
537+
category: String
538+
): Web3AddressRequest {
539+
val selfId = Session.getAccountId()
540+
if (category == WalletCategory.WATCH_ADDRESS.value) {
541+
return Web3AddressRequest(
542+
destination = destination,
543+
chainId = chainId,
544+
path = path,
545+
signature = null,
546+
timestamp = null
547+
)
548+
}
549+
val now = Instant.now()
550+
val signature = if (privateKey != null) {
551+
val message = "$destination\n$selfId\n${now.epochSecond}"
552+
if (chainId == Constants.ChainId.SOLANA_CHAIN_ID) {
553+
Numeric.prependHexPrefix(Web3Signer.signSolanaMessage(privateKey, message.toByteArray()))
554+
} else {
555+
Web3Signer.signEthMessage(privateKey, message.toByteArray().toHexString(), JsSignMessage.TYPE_PERSONAL_MESSAGE)
556+
}
557+
} else null
558+
559+
return Web3AddressRequest(
560+
destination = destination,
561+
chainId = chainId,
562+
path = path,
563+
signature = signature,
564+
timestamp = now.toString()
565+
)
566+
}
567+
}

app/src/main/java/one/mixin/android/web3/js/Web3Signer.kt

Lines changed: 11 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
package one.mixin.android.web3.js
22

3-
import android.util.LruCache
4-
import okhttp3.OkHttpClient
5-
import okhttp3.logging.HttpLoggingInterceptor
63
import okio.Buffer
7-
import one.mixin.android.BuildConfig
84
import one.mixin.android.Constants.Account.ChainAddress.EVM_ADDRESS
95
import one.mixin.android.Constants.Account.ChainAddress.SOLANA_ADDRESS
106
import one.mixin.android.Constants.ChainId.SOLANA_CHAIN_ID
@@ -40,13 +36,10 @@ import org.web3j.crypto.RawTransaction
4036
import org.web3j.crypto.Sign
4137
import org.web3j.crypto.StructuredDataEncoder
4238
import org.web3j.crypto.TransactionEncoder
43-
import org.web3j.protocol.Web3j
4439
import org.web3j.protocol.core.Response
45-
import org.web3j.protocol.http.HttpService
4640
import org.web3j.utils.Numeric
4741
import timber.log.Timber
4842
import java.math.BigInteger
49-
import java.util.concurrent.TimeUnit
5043
import org.sol4k.Constants as ConstantsSolana
5144

5245
object Web3Signer {
@@ -62,34 +55,6 @@ object Web3Signer {
6255
MixinApplication.appContext.defaultSharedPreferences
6356
}
6457

65-
private var web3jPool = LruCache<Chain, Web3j>(3)
66-
67-
private fun getWeb3j(chain: Chain): Web3j {
68-
val exists = web3jPool[chain]
69-
return if (exists == null) {
70-
val web3j = Web3j.build(HttpService(chain.rpcUrl, buildOkHttpClient()))
71-
web3jPool.put(chain, web3j)
72-
web3j
73-
} else {
74-
exists
75-
}
76-
}
77-
78-
private fun buildOkHttpClient(): OkHttpClient {
79-
val builder = OkHttpClient.Builder()
80-
builder.connectTimeout(15, TimeUnit.SECONDS)
81-
builder.writeTimeout(15, TimeUnit.SECONDS)
82-
builder.readTimeout(15, TimeUnit.SECONDS)
83-
if (BuildConfig.DEBUG) {
84-
builder.addInterceptor(
85-
HttpLoggingInterceptor().apply {
86-
level = HttpLoggingInterceptor.Level.BODY
87-
},
88-
)
89-
}
90-
return builder.build()
91-
}
92-
9358
private object Keys {
9459
const val ADDRESS = "signer_address"
9560
const val EVM_ADDRESS = "signer_evm_address"
@@ -377,7 +342,7 @@ object Web3Signer {
377342
}
378343
}
379344

380-
private fun signEthMessage(
345+
fun signEthMessage(
381346
priv: ByteArray,
382347
message: String,
383348
type: Int,
@@ -397,18 +362,25 @@ object Web3Signer {
397362
return Numeric.toHexString(b)
398363
}
399364

400-
private fun signSolanaMessage(
365+
fun signSolanaMessage(
401366
priv: ByteArray,
402367
message: String,
403368
): String {
404-
val keyPair = Keypair.fromSecretKey(priv)
405369
val m =
406370
try {
407371
message.decodeBase58()
408372
} catch (e: Exception) {
409373
message.removePrefix("0x").hexStringToByteArray()
410374
}
411-
val sig = keyPair.sign(m)
375+
return signSolanaMessage(priv, m)
376+
}
377+
378+
fun signSolanaMessage(
379+
priv: ByteArray,
380+
message: ByteArray,
381+
): String {
382+
val keyPair = Keypair.fromSecretKey(priv)
383+
val sig = keyPair.sign(message)
412384
return sig.toHex()
413385
}
414386

app/src/main/java/one/mixin/android/widget/ContentQRView.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import com.uber.autodispose.autoDispose
1414
import io.reactivex.Observable
1515
import io.reactivex.android.schedulers.AndroidSchedulers
1616
import io.reactivex.schedulers.Schedulers
17+
import one.mixin.android.Constants
1718
import one.mixin.android.R
1819
import one.mixin.android.databinding.ViewContentQrBinding
1920
import one.mixin.android.db.web3.vo.Web3TokenItem
@@ -93,7 +94,11 @@ class ContentQRView : ViewAnimator {
9394
if (isTag) {
9495
requireNotNull(depositEntry.tag)
9596
} else {
96-
destination
97+
if (asset.chainId == Constants.ChainId.LIGHTNING_NETWORK_CHAIN_ID) {
98+
destination.uppercase()
99+
} else {
100+
destination
101+
}
97102
}.generateQRCode(200.dp, innerPadding = 40.dp, padding = 0)
98103
e.onNext(r)
99104
}.subscribeOn(Schedulers.io())

0 commit comments

Comments
 (0)