Updated on 2026-08-14
This commit is contained in:
commit
11ea0b26ef
19 changed files with 187 additions and 114 deletions
|
|
@ -2,6 +2,7 @@ apply plugin: 'com.android.library'
|
|||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "29.0.2"
|
||||
|
|
@ -24,6 +25,13 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
@ -33,11 +41,12 @@ dependencies {
|
|||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin"
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.core:core-ktx:1.1.0'
|
||||
implementation 'androidx.core:core-ktx:1.2.0'
|
||||
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.7.0'
|
||||
implementation 'com.squareup.retrofit2:converter-moshi:2.6.0'
|
||||
implementation 'com.squareup.moshi:moshi:1.9.2'
|
||||
implementation "com.squareup.moshi:moshi-kotlin:1.9.2"
|
||||
kapt("com.squareup.moshi:moshi-kotlin-codegen:1.9.2")
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor:4.2.2'
|
||||
|
||||
|
|
@ -45,21 +54,21 @@ dependencies {
|
|||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.3.3"
|
||||
|
||||
implementation 'org.bitcoinj:bitcoinj-core:0.15.2'
|
||||
implementation 'com.github.stellar:java-stellar-sdk:0.13.0'
|
||||
implementation 'com.github.stellar:java-stellar-sdk:0.14.0'
|
||||
|
||||
implementation "com.madgag.spongycastle:core:1.58.0.0"
|
||||
implementation "com.madgag.spongycastle:prov:1.58.0.0"
|
||||
|
||||
ext.kethereum_version = '0.79.5'
|
||||
implementation "com.github.walleth.kethereum:functions:$kethereum_version"
|
||||
ext.kethereum_version = '0.81.2'
|
||||
implementation "com.github.walleth.kethereum:extensions_kotlin:$kethereum_version"
|
||||
implementation "com.github.walleth.kethereum:extensions_transactions:$kethereum_version"
|
||||
implementation "com.github.walleth.kethereum:erc55:$kethereum_version"
|
||||
implementation "com.github.walleth.kethereum:keccak_shortcut:$kethereum_version"
|
||||
implementation "com.github.walleth.kethereum:wallet:$kethereum_version"
|
||||
implementation "com.github.walleth.kethereum:crypto_impl_spongycastle:$kethereum_version"
|
||||
implementation "com.github.walleth.kethereum:crypto:$kethereum_version"
|
||||
implementation "com.github.walleth.kethereum:crypto_api:$kethereum_version"
|
||||
implementation "com.github.walleth.kethereum:model:$kethereum_version"
|
||||
implementation 'com.github.komputing.khex:core:1.0.0-RC6'
|
||||
implementation 'com.github.komputing.khex:extensions:1.0.0-RC6'
|
||||
|
||||
implementation 'co.nstant.in:cbor:0.8'
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.blockchain.bitcoin
|
|||
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.extensions.Result
|
||||
import com.tangem.blockchain.common.extensions.toCanonicalised
|
||||
import org.bitcoinj.core.*
|
||||
import org.bitcoinj.crypto.TransactionSignature
|
||||
import org.bitcoinj.script.Script
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import java.math.BigDecimal
|
|||
class BitcoinWalletManager(
|
||||
private val cardId: String,
|
||||
private val walletPublicKey: ByteArray,
|
||||
walletConfig: WalletConfig,
|
||||
override var wallet: CurrencyWallet,
|
||||
isTestNet: Boolean = false
|
||||
) : WalletManager,
|
||||
TransactionSender,
|
||||
|
|
@ -23,11 +23,13 @@ class BitcoinWalletManager(
|
|||
|
||||
override val blockchain = if (isTestNet) Blockchain.BitcoinTestnet else Blockchain.Bitcoin
|
||||
private val address = blockchain.makeAddress(walletPublicKey)
|
||||
private val currencyWallet = CurrencyWallet(walletConfig, address)
|
||||
override var wallet: Wallet = currencyWallet
|
||||
private val transactionBuilder = BitcoinTransactionBuilder(isTestNet)
|
||||
private val networkManager = BitcoinNetworkManager(isTestNet)
|
||||
|
||||
init {
|
||||
wallet.balances[AmountType.Coin] = Amount(null, blockchain)
|
||||
}
|
||||
|
||||
override suspend fun update() {
|
||||
val response = networkManager.getInfo(address)
|
||||
when (response) {
|
||||
|
|
@ -38,23 +40,24 @@ class BitcoinWalletManager(
|
|||
|
||||
private fun updateWallet(response: BitcoinAddressResponse) {
|
||||
Log.d(this::class.java.simpleName, "Balance is ${response.balance}")
|
||||
currencyWallet.balances[AmountType.Coin]?.value = response.balance
|
||||
wallet.balances[AmountType.Coin]?.value = response.balance
|
||||
transactionBuilder.unspentOutputs = response.unspentTransactions
|
||||
if (response.hasUnconfirmed) {
|
||||
if (currencyWallet.pendingTransactions.isEmpty()) {
|
||||
currencyWallet.pendingTransactions.add(TransactionData(
|
||||
if (wallet.pendingTransactions.isEmpty()) {
|
||||
wallet.pendingTransactions.add(TransactionData(
|
||||
Amount(blockchain.currency, decimals = blockchain.decimals),
|
||||
null,
|
||||
"unknown",
|
||||
currencyWallet.address))
|
||||
wallet.address))
|
||||
}
|
||||
} else {
|
||||
currencyWallet.pendingTransactions.clear()
|
||||
wallet.pendingTransactions.clear()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateError(error: Throwable?) {
|
||||
Log.e(this::class.java.simpleName, error?.message ?: "")
|
||||
if (error != null) throw error
|
||||
}
|
||||
|
||||
override suspend fun send(transactionData: TransactionData, signer: TransactionSigner): SimpleResult {
|
||||
|
|
@ -73,7 +76,7 @@ class BitcoinWalletManager(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getFee(amount: Amount, source: String, destination: String): Result<List<Amount>> {
|
||||
override suspend fun getFee(amount: Amount, destination: String): Result<List<Amount>> {
|
||||
when (val result = networkManager.getFee()) {
|
||||
is Result.Failure -> return result
|
||||
is Result.Success -> {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.blockchain.cardano
|
||||
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import com.tangem.blockchain.cardano.network.CardanoAddressResponse
|
||||
import com.tangem.blockchain.cardano.network.CardanoNetworkManager
|
||||
|
|
@ -10,20 +9,17 @@ import com.tangem.blockchain.common.extensions.SimpleResult
|
|||
import com.tangem.blockchain.common.extensions.encodeBase64NoWrap
|
||||
import com.tangem.blockchain.wallets.CurrencyWallet
|
||||
import com.tangem.tasks.TaskEvent
|
||||
import java.math.BigDecimal
|
||||
|
||||
class CardanoWalletManager(
|
||||
private val cardId: String,
|
||||
private val walletPublicKey: ByteArray,
|
||||
walletConfig: WalletConfig
|
||||
override var wallet: CurrencyWallet
|
||||
) : WalletManager,
|
||||
TransactionSender,
|
||||
FeeProvider {
|
||||
|
||||
override val blockchain = Blockchain.Cardano
|
||||
private val address = blockchain.makeAddress(walletPublicKey)
|
||||
private val currencyWallet = CurrencyWallet(walletConfig, address)
|
||||
override var wallet: Wallet = currencyWallet
|
||||
private val transactionBuilder = CardanoTransactionBuilder()
|
||||
private val networkManager = CardanoNetworkManager()
|
||||
|
||||
|
|
@ -37,13 +33,14 @@ class CardanoWalletManager(
|
|||
|
||||
private fun updateWallet(response: CardanoAddressResponse) {
|
||||
Log.d(this::class.java.simpleName, "Balance is ${response.balance.toString()}")
|
||||
currencyWallet.balances[AmountType.Coin]?.value =
|
||||
wallet.balances[AmountType.Coin]?.value =
|
||||
response.balance.toBigDecimal().movePointLeft(blockchain.decimals.toInt())
|
||||
transactionBuilder.unspentOutputs = response.unspentOutputs
|
||||
}
|
||||
|
||||
private fun updateError(error: Throwable?) {
|
||||
Log.e(this::class.java.simpleName, error?.message ?: "")
|
||||
if (error != null) throw error
|
||||
}
|
||||
|
||||
override suspend fun send(transactionData: TransactionData, signer: TransactionSigner): SimpleResult {
|
||||
|
|
@ -58,13 +55,13 @@ class CardanoWalletManager(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getFee(amount: Amount, source: String, destination: String): Result<List<Amount>> {
|
||||
override suspend fun getFee(amount: Amount, destination: String): Result<List<Amount>> {
|
||||
val a = 0.155381
|
||||
val b = 0.000043946
|
||||
val size = transactionBuilder.getEstimateSize(
|
||||
TransactionData(amount, null, source, destination), walletPublicKey
|
||||
TransactionData(amount, null, address, destination), walletPublicKey
|
||||
)
|
||||
val fee = (a + b * size).toBigDecimal()
|
||||
return Result.Success(listOf(Amount(blockchain.currency, fee, source, blockchain.decimals)))
|
||||
return Result.Success(listOf(Amount(blockchain.currency, fee, address, blockchain.decimals)))
|
||||
}
|
||||
}
|
||||
|
|
@ -2,13 +2,15 @@ package com.tangem.blockchain.common
|
|||
|
||||
import com.tangem.blockchain.bitcoin.BitcoinAddressFactory
|
||||
import com.tangem.blockchain.bitcoin.BitcoinAddressValidator
|
||||
import com.tangem.blockchain.ethereum.EthereumAddressFactory
|
||||
import com.tangem.blockchain.ethereum.EthereumAddressValidator
|
||||
import com.tangem.blockchain.cardano.CardanoAddressFactory
|
||||
import com.tangem.blockchain.cardano.CardanoAddressValidator
|
||||
import com.tangem.blockchain.ethereum.EthereumAddressFactory
|
||||
import com.tangem.blockchain.ethereum.EthereumAddressValidator
|
||||
|
||||
import com.tangem.blockchain.stellar.StellarAddressFactory
|
||||
import com.tangem.blockchain.xrp.XrpAddressFactory
|
||||
import com.tangem.blockchain.xrp.XrpAddressValidator
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
enum class Blockchain(
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ data class Amount(
|
|||
address: String? = null,
|
||||
type: AmountType = AmountType.Coin
|
||||
) : this(blockchain.currency, value, address, blockchain.decimals, type)
|
||||
|
||||
constructor(token: Token, value: BigDecimal? = null) :
|
||||
this(token.symbol, value, token.contractAddress, token.decimals, AmountType.Token)
|
||||
}
|
||||
|
||||
data class TransactionData(
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ package com.tangem.blockchain.common
|
|||
|
||||
import com.tangem.blockchain.common.extensions.Result
|
||||
import com.tangem.blockchain.common.extensions.SimpleResult
|
||||
import com.tangem.blockchain.wallets.CurrencyWallet
|
||||
import com.tangem.commands.SignResponse
|
||||
import com.tangem.tasks.TaskEvent
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface WalletManager {
|
||||
var wallet: Wallet
|
||||
var wallet: CurrencyWallet
|
||||
val blockchain: Blockchain
|
||||
|
||||
suspend fun update()
|
||||
|
|
@ -22,5 +22,5 @@ interface TransactionSigner {
|
|||
}
|
||||
|
||||
interface FeeProvider {
|
||||
suspend fun getFee(amount: Amount, source: String, destination: String): Result<List<Amount>>
|
||||
suspend fun getFee(amount: Amount, destination: String): Result<List<Amount>>
|
||||
}
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
package com.tangem.blockchain.common
|
||||
|
||||
import com.tangem.blockchain.bitcoin.BitcoinWalletManager
|
||||
import com.tangem.blockchain.cardano.CardanoWalletManager
|
||||
import com.tangem.blockchain.ethereum.Chain
|
||||
import com.tangem.blockchain.ethereum.EthereumWalletManager
|
||||
import com.tangem.blockchain.cardano.CardanoWalletManager
|
||||
|
||||
import com.tangem.blockchain.stellar.StellarWalletManager
|
||||
import com.tangem.blockchain.wallets.CurrencyWallet
|
||||
import com.tangem.blockchain.xrp.XrpWalletManager
|
||||
import com.tangem.commands.Card
|
||||
|
||||
|
|
@ -13,53 +15,56 @@ object WalletManagerFactory {
|
|||
fun makeWalletManager(card: Card): WalletManager? {
|
||||
val walletPublicKey: ByteArray = card.walletPublicKey ?: return null
|
||||
val blockchainName: String = card.cardData?.blockchainName ?: return null
|
||||
val blockchain = Blockchain.fromId(blockchainName)
|
||||
|
||||
when {
|
||||
blockchainName == Blockchain.Bitcoin.id -> {
|
||||
val token = getToken(card)
|
||||
val wallet = CurrencyWallet.newInstance(blockchain, blockchain.makeAddress(walletPublicKey), token)
|
||||
|
||||
|
||||
when (blockchain) {
|
||||
Blockchain.Bitcoin -> {
|
||||
return BitcoinWalletManager(
|
||||
cardId = card.cardId,
|
||||
walletPublicKey = walletPublicKey,
|
||||
walletConfig = WalletConfig(true, true)
|
||||
wallet = wallet
|
||||
)
|
||||
}
|
||||
blockchainName == Blockchain.BitcoinTestnet.id -> {
|
||||
Blockchain.BitcoinTestnet -> {
|
||||
return BitcoinWalletManager(
|
||||
cardId = card.cardId,
|
||||
walletPublicKey = walletPublicKey,
|
||||
walletConfig = WalletConfig(true, true),
|
||||
wallet = wallet,
|
||||
isTestNet = true
|
||||
)
|
||||
}
|
||||
blockchainName == Blockchain.Ethereum.id -> {
|
||||
Blockchain.Ethereum -> {
|
||||
val chain = Chain.Mainnet
|
||||
return EthereumWalletManager(
|
||||
cardId = card.cardId,
|
||||
walletPublicKey = walletPublicKey,
|
||||
walletConfig = WalletConfig(true, true),
|
||||
wallet = wallet,
|
||||
chain = chain
|
||||
)
|
||||
}
|
||||
blockchainName == Blockchain.Stellar.id -> {
|
||||
val token = getToken(card)
|
||||
Blockchain.Stellar -> {
|
||||
return StellarWalletManager(
|
||||
cardId = card.cardId,
|
||||
walletPublicKey = walletPublicKey,
|
||||
walletConfig = WalletConfig(true, token == null),
|
||||
token = token
|
||||
wallet = wallet
|
||||
)
|
||||
}
|
||||
blockchainName == Blockchain.Cardano.id -> {
|
||||
Blockchain.Cardano -> {
|
||||
return CardanoWalletManager(
|
||||
cardId = card.cardId,
|
||||
walletPublicKey = walletPublicKey,
|
||||
walletConfig = WalletConfig(false, true)
|
||||
wallet = wallet
|
||||
)
|
||||
}
|
||||
blockchainName == Blockchain.XRP.id -> {
|
||||
Blockchain.XRP -> {
|
||||
return XrpWalletManager(
|
||||
cardId = card.cardId,
|
||||
walletPublicKey = walletPublicKey,
|
||||
walletConfig = WalletConfig(true, true)
|
||||
wallet = wallet
|
||||
)
|
||||
}
|
||||
else -> return null
|
||||
|
|
|
|||
|
|
@ -4,12 +4,11 @@ import com.tangem.CardManager
|
|||
import com.tangem.blockchain.common.TransactionSigner
|
||||
import com.tangem.commands.SignResponse
|
||||
import com.tangem.tasks.TaskEvent
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import java.io.IOException
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
|
||||
suspend fun <T> retryIO(
|
||||
times: Int = 3,
|
||||
|
|
@ -56,11 +55,11 @@ sealed class SimpleResult {
|
|||
}
|
||||
|
||||
class Signer(private val cardManager: CardManager) : TransactionSigner {
|
||||
override suspend fun sign(hashes: Array<ByteArray>, cardId: String): TaskEvent<SignResponse> = coroutineScope {
|
||||
async {
|
||||
suspendCancellableCoroutine<TaskEvent<SignResponse>> { continuation ->
|
||||
cardManager.sign(hashes, cardId) { if (continuation.isActive) continuation.resume(it) }
|
||||
override suspend fun sign(hashes: Array<ByteArray>, cardId: String): TaskEvent<SignResponse> =
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
cardManager.sign(hashes, cardId) { result ->
|
||||
if (continuation.isActive) continuation.resume(result)
|
||||
}
|
||||
}
|
||||
}.await()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package com.tangem.blockchain.common.network
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import com.tangem.blockchain.BuildConfig
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
|
|
@ -18,11 +20,16 @@ private fun createHttpLoggingInterceptor(): HttpLoggingInterceptor {
|
|||
return logging
|
||||
}
|
||||
|
||||
private val moshi: Moshi by lazy {
|
||||
Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
}
|
||||
|
||||
fun createRetrofitInstance(baseUrl: String): Retrofit =
|
||||
Retrofit.Builder()
|
||||
.baseUrl(baseUrl)
|
||||
.addConverterFactory(MoshiConverterFactory.create())
|
||||
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||
.client(okHttpClient)
|
||||
.build()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.blockchain.ethereum
|
||||
|
||||
import org.kethereum.crypto.toAddress
|
||||
import org.kethereum.functions.isValid
|
||||
import org.kethereum.erc55.isValid
|
||||
import org.kethereum.model.Address
|
||||
import org.kethereum.model.PublicKey
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.blockchain.ethereum
|
||||
|
||||
import android.util.Log
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchain.common.extensions.Result
|
||||
import com.tangem.blockchain.common.extensions.SimpleResult
|
||||
import com.tangem.blockchain.ethereum.network.EthereumNetworkManager
|
||||
|
|
@ -12,7 +14,7 @@ import org.kethereum.DEFAULT_GAS_LIMIT
|
|||
import org.kethereum.crypto.api.ec.ECDSASignature
|
||||
import org.kethereum.crypto.determineRecId
|
||||
import org.kethereum.crypto.impl.ec.canonicalise
|
||||
import org.kethereum.functions.encodeRLP
|
||||
import org.kethereum.extensions.transactions.encodeRLP
|
||||
import org.kethereum.keccakshortcut.keccak
|
||||
import org.kethereum.model.*
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -22,22 +24,32 @@ class EthereumWalletManager(
|
|||
private val cardId: String,
|
||||
private val walletPublicKey: ByteArray,
|
||||
chain: Chain,
|
||||
walletConfig: WalletConfig
|
||||
token: Token? = null,
|
||||
override var wallet: CurrencyWallet
|
||||
) : WalletManager,
|
||||
TransactionSender,
|
||||
FeeProvider {
|
||||
|
||||
override val blockchain: Blockchain = Blockchain.Ethereum
|
||||
private val address = blockchain.makeAddress(walletPublicKey)
|
||||
private val currencyWallet = CurrencyWallet(walletConfig, address)
|
||||
override var wallet: Wallet = currencyWallet
|
||||
private val builder = EthereumTransactionBuilder(chain)
|
||||
private val networkManager = EthereumNetworkManager()
|
||||
private var pendingTxCount = -1L
|
||||
private var txCount = -1L
|
||||
|
||||
init {
|
||||
if (token != null) wallet.balances[AmountType.Token] =
|
||||
Amount(
|
||||
token.symbol,
|
||||
null,
|
||||
token.contractAddress,
|
||||
token.decimals,
|
||||
AmountType.Token)
|
||||
wallet.balances[AmountType.Coin] = Amount(null, blockchain)
|
||||
}
|
||||
|
||||
override suspend fun update() {
|
||||
val result = networkManager.getInfo(address, currencyWallet.balances[AmountType.Token]?.address)
|
||||
val result = networkManager.getInfo(address, wallet.balances[AmountType.Token]?.address)
|
||||
when (result) {
|
||||
is Result.Failure -> updateError(result.error)
|
||||
is Result.Success -> updateWallet(result.data)
|
||||
|
|
@ -45,23 +57,24 @@ class EthereumWalletManager(
|
|||
}
|
||||
|
||||
private fun updateWallet(data: EthereumResponse) {
|
||||
currencyWallet.balances[AmountType.Coin]?.value = data.balance
|
||||
currencyWallet.balances[AmountType.Token]?.value = data.tokenBalance
|
||||
wallet.balances[AmountType.Coin]?.value = data.balance
|
||||
wallet.balances[AmountType.Token]?.value = data.tokenBalance
|
||||
txCount = data.txCount
|
||||
pendingTxCount = data.pendingTxCount
|
||||
if (txCount == pendingTxCount) {
|
||||
currencyWallet.pendingTransactions.forEach { it.status = TransactionStatus.Confirmed }
|
||||
} else if (currencyWallet.pendingTransactions.isEmpty()) {
|
||||
currencyWallet.pendingTransactions.add(TransactionData(
|
||||
wallet.pendingTransactions.forEach { it.status = TransactionStatus.Confirmed }
|
||||
} else if (wallet.pendingTransactions.isEmpty()) {
|
||||
wallet.pendingTransactions.add(TransactionData(
|
||||
Amount(blockchain.currency, decimals = blockchain.decimals),
|
||||
null,
|
||||
"unknown",
|
||||
currencyWallet.address))
|
||||
wallet.address))
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateError(error: Throwable?) {
|
||||
|
||||
Log.e(this::class.java.simpleName, error?.message ?: "")
|
||||
if (error != null) throw error
|
||||
}
|
||||
|
||||
override suspend fun send(transactionData: TransactionData, signer: TransactionSigner): SimpleResult {
|
||||
|
|
@ -76,7 +89,7 @@ class EthereumWalletManager(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getFee(amount: Amount, source: String, destination: String): Result<List<Amount>> {
|
||||
override suspend fun getFee(amount: Amount, destination: String): Result<List<Amount>> {
|
||||
val result = networkManager.getFee(getGasLimit(amount).value)
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import kotlinx.coroutines.coroutineScope
|
|||
import org.kethereum.ETH_IN_WEI
|
||||
import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
import java.math.RoundingMode
|
||||
|
||||
|
||||
class EthereumNetworkManager {
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ class StellarNetworkManager(isTestNet: Boolean) {
|
|||
coroutineScope {
|
||||
val accountResponseDefered = async(Dispatchers.IO) { stellarServer.accounts().account(accountId) }
|
||||
val ledgerResponseDeferred = async(Dispatchers.IO) {
|
||||
val latestLedger: Int = stellarServer.root().coreLatestLedger
|
||||
val latestLedger: Int = stellarServer.root().historyLatestLedger
|
||||
stellarServer.ledgers().ledger(latestLedger.toLong())
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ class StellarNetworkManager(isTestNet: Boolean) {
|
|||
null
|
||||
} else {
|
||||
accountResponse.balances
|
||||
.find { it.assetType != "native" && it.assetCode == assetCode }
|
||||
.find { it.assetType != "native" && it.assetIssuer == assetCode }
|
||||
?.balance?.toBigDecimal()
|
||||
?: return@coroutineScope Result.Failure(Exception("Stellar Balance not found"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.blockchain.stellar
|
||||
|
||||
import android.util.Log
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.extensions.Result
|
||||
import com.tangem.blockchain.common.extensions.SimpleResult
|
||||
|
|
@ -11,8 +12,7 @@ import java.util.*
|
|||
class StellarWalletManager(
|
||||
private val cardId: String,
|
||||
walletPublicKey: ByteArray,
|
||||
walletConfig: WalletConfig,
|
||||
token: Token? = null,
|
||||
override var wallet: CurrencyWallet,
|
||||
isTestNet: Boolean = false
|
||||
) : WalletManager,
|
||||
TransactionSender,
|
||||
|
|
@ -20,26 +20,14 @@ class StellarWalletManager(
|
|||
|
||||
override val blockchain: Blockchain = Blockchain.Stellar
|
||||
private val address = blockchain.makeAddress(walletPublicKey)
|
||||
private val currencyWallet = CurrencyWallet(walletConfig, address)
|
||||
override var wallet: Wallet = currencyWallet
|
||||
private val networkManager = StellarNetworkManager(isTestNet)
|
||||
private val builder = StellarTransactionBuilder(networkManager, walletPublicKey)
|
||||
private var baseFee = BASE_FEE
|
||||
private var baseReserve = BASE_RESERVE
|
||||
private var sequence = 0L
|
||||
|
||||
init {
|
||||
if (token != null) currencyWallet.balances[AmountType.Token] =
|
||||
Amount(
|
||||
token.symbol,
|
||||
null,
|
||||
token.contractAddress,
|
||||
token.decimals,
|
||||
AmountType.Token)
|
||||
}
|
||||
|
||||
override suspend fun update() {
|
||||
val result = networkManager.getInfo(address, currencyWallet.balances[AmountType.Token]?.address)
|
||||
val result = networkManager.getInfo(address, wallet.balances[AmountType.Token]?.address)
|
||||
when (result) {
|
||||
is Result.Failure -> updateError(result.error)
|
||||
is Result.Success -> updateWallet(result.data)
|
||||
|
|
@ -47,15 +35,15 @@ class StellarWalletManager(
|
|||
}
|
||||
|
||||
private fun updateWallet(data: StellarResponse) {
|
||||
currencyWallet.balances[AmountType.Coin]?.value = data.balance
|
||||
currencyWallet.balances[AmountType.Token]?.value = data.assetBalance
|
||||
currencyWallet.balances[AmountType.Reserve]?.value = data.baseReserve
|
||||
wallet.balances[AmountType.Coin]?.value = data.balance
|
||||
wallet.balances[AmountType.Token]?.value = data.assetBalance
|
||||
wallet.balances[AmountType.Reserve]?.value = data.baseReserve
|
||||
sequence = data.sequence
|
||||
baseFee = data.baseFee
|
||||
baseReserve = data.baseReserve
|
||||
|
||||
val currentTime = Calendar.getInstance().timeInMillis
|
||||
currencyWallet.pendingTransactions.forEach { transaction ->
|
||||
wallet.pendingTransactions.forEach { transaction ->
|
||||
if (transaction.date?.timeInMillis ?: 0 - currentTime > 10) {
|
||||
transaction.status = TransactionStatus.Confirmed
|
||||
}
|
||||
|
|
@ -63,7 +51,8 @@ class StellarWalletManager(
|
|||
}
|
||||
|
||||
private fun updateError(error: Throwable?) {
|
||||
|
||||
Log.e(this::class.java.simpleName, error?.message ?: "")
|
||||
if (error != null) throw error
|
||||
}
|
||||
|
||||
override suspend fun send(transactionData: TransactionData, signer: TransactionSigner): SimpleResult {
|
||||
|
|
@ -77,7 +66,7 @@ class StellarWalletManager(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getFee(amount: Amount, source: String, destination: String): Result<List<Amount>> {
|
||||
override suspend fun getFee(amount: Amount, destination: String): Result<List<Amount>> {
|
||||
return Result.Success(listOf(
|
||||
Amount(baseFee, blockchain)
|
||||
))
|
||||
|
|
|
|||
|
|
@ -4,17 +4,63 @@ import com.tangem.blockchain.common.*
|
|||
import java.util.*
|
||||
|
||||
class CurrencyWallet(
|
||||
override val config: WalletConfig,
|
||||
override val address: String,
|
||||
override val exploreUrl: String? = null,
|
||||
override val shareUrl: String? = null,
|
||||
val pendingTransactions: MutableList<TransactionData> = mutableListOf(),
|
||||
val balances: MutableMap<AmountType, Amount> = mutableMapOf(),
|
||||
val isTestnet: Boolean = false
|
||||
val blockchain: Blockchain,
|
||||
address: String,
|
||||
override val config: WalletConfig
|
||||
) : Wallet, TransactionValidator {
|
||||
|
||||
override val exploreUrl: String? = null
|
||||
override val shareUrl: String? = null
|
||||
override val address: String
|
||||
get() = balances[AmountType.Coin]!!.address!!
|
||||
|
||||
val pendingTransactions: MutableList<TransactionData> = mutableListOf()
|
||||
val balances: MutableMap<AmountType, Amount> = mutableMapOf()
|
||||
|
||||
init {
|
||||
addAmount(Amount(null, blockchain, address))
|
||||
}
|
||||
|
||||
override fun validateTransaction(amount: Amount, fee: Amount?): EnumSet<ValidationError> {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
fun addAmount(amount: Amount) {
|
||||
balances[amount.type] = amount
|
||||
}
|
||||
|
||||
fun addPendingTransaction(transaction: TransactionData) {
|
||||
pendingTransactions.add(transaction.copy(date = Calendar.getInstance()))
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newInstance(blockchain: Blockchain, address: String, token: Token?): CurrencyWallet {
|
||||
|
||||
return when (blockchain) {
|
||||
Blockchain.Bitcoin, Blockchain.XRP -> {
|
||||
val config = WalletConfig(true, true)
|
||||
CurrencyWallet(blockchain, address, config)
|
||||
}
|
||||
Blockchain.Cardano -> {
|
||||
val config = WalletConfig(false, true)
|
||||
CurrencyWallet(blockchain, address, config)
|
||||
}
|
||||
Blockchain.Ethereum -> {
|
||||
val config = WalletConfig(true, token == null)
|
||||
val wallet = CurrencyWallet(blockchain, address, config)
|
||||
if (token != null) wallet.addAmount(Amount(token))
|
||||
wallet
|
||||
}
|
||||
Blockchain.Stellar -> {
|
||||
val config = WalletConfig(false, token == null)
|
||||
val wallet = CurrencyWallet(blockchain, address, config)
|
||||
if (token != null) wallet.addAmount(Amount(token))
|
||||
wallet.addAmount(Amount(null, blockchain, address, AmountType.Reserve))
|
||||
wallet
|
||||
}
|
||||
else -> throw Exception("Unsupported blockchain")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,18 +12,21 @@ import com.tangem.tasks.TaskEvent
|
|||
class XrpWalletManager(
|
||||
private val cardId: String,
|
||||
private val walletPublicKey: ByteArray,
|
||||
walletConfig: WalletConfig
|
||||
override var wallet: CurrencyWallet
|
||||
) : WalletManager,
|
||||
TransactionSender,
|
||||
FeeProvider {
|
||||
|
||||
override val blockchain = Blockchain.XRP
|
||||
private val address = blockchain.makeAddress(walletPublicKey)
|
||||
private val currencyWallet = CurrencyWallet(walletConfig, address)
|
||||
override var wallet: Wallet = currencyWallet
|
||||
private val transactionBuilder = XrpTransactionBuilder(walletPublicKey)
|
||||
private val networkManager = XrpNetworkManager()
|
||||
|
||||
init {
|
||||
wallet.balances[AmountType.Coin] = Amount(null, blockchain)
|
||||
wallet.balances[AmountType.Reserve] = Amount(null, blockchain, type = AmountType.Reserve)
|
||||
}
|
||||
|
||||
override suspend fun update() {
|
||||
val result = networkManager.getInfo(address)
|
||||
when (result) {
|
||||
|
|
@ -34,30 +37,31 @@ class XrpWalletManager(
|
|||
|
||||
private fun updateWallet(response: XrpInfoResponse) {
|
||||
Log.d(this::class.java.simpleName, "Balance is ${response.balance}")
|
||||
currencyWallet.balances[AmountType.Reserve]?.value = response.reserveBase
|
||||
wallet.balances[AmountType.Reserve]?.value = response.reserveBase
|
||||
|
||||
if (!response.accountFound) {
|
||||
updateError(Exception("Account not found")) //TODO rework, add reserve
|
||||
return
|
||||
}
|
||||
currencyWallet.balances[AmountType.Coin]?.value = response.balance - response.reserveBase
|
||||
wallet.balances[AmountType.Coin]?.value = response.balance - response.reserveBase
|
||||
transactionBuilder.sequence = response.sequence
|
||||
|
||||
if (response.hasUnconfirmed) {
|
||||
if (currencyWallet.pendingTransactions.isEmpty()) {
|
||||
currencyWallet.pendingTransactions.add(TransactionData(
|
||||
if (wallet.pendingTransactions.isEmpty()) {
|
||||
wallet.pendingTransactions.add(TransactionData(
|
||||
Amount(blockchain.currency, decimals = blockchain.decimals),
|
||||
null,
|
||||
"unknown",
|
||||
currencyWallet.address))
|
||||
wallet.address))
|
||||
}
|
||||
} else {
|
||||
currencyWallet.pendingTransactions.clear()
|
||||
wallet.pendingTransactions.clear()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateError(error: Throwable?) {
|
||||
Log.e(this::class.java.simpleName, error?.message ?: "")
|
||||
if (error != null) throw error
|
||||
}
|
||||
|
||||
override suspend fun send(transactionData: TransactionData, signer: TransactionSigner): SimpleResult {
|
||||
|
|
@ -72,7 +76,7 @@ class XrpWalletManager(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getFee(amount: Amount, source: String, destination: String): Result<List<Amount>> {
|
||||
override suspend fun getFee(amount: Amount, destination: String): Result<List<Amount>> {
|
||||
val result = networkManager.getFee()
|
||||
when (result) {
|
||||
is Result.Failure -> return result
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.blockchain.xrp.network
|
||||
|
||||
import com.tangem.blockchain.bitcoin.network.BitcoinFee
|
||||
import com.tangem.blockchain.common.extensions.Result
|
||||
import com.tangem.blockchain.common.extensions.SimpleResult
|
||||
import com.tangem.blockchain.common.network.API_RIPPLED
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import com.tangem.blockchain.xrp.network.XrpFeeResponse
|
|||
import com.tangem.blockchain.xrp.network.XrpInfoResponse
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import java.math.BigDecimal
|
||||
|
||||
class RippledProvider(private val api: RippledApi) {
|
||||
private val decimals = Blockchain.XRP.decimals.toInt()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue