Updated on 2026-08-14
This commit is contained in:
commit
903fc32bc9
51 changed files with 944 additions and 309 deletions
|
|
@ -33,18 +33,18 @@ class BitcoinAddressValidator {
|
|||
if (testNet && firstLettersNonTestNet.contains(address.first())) return false
|
||||
if (address.length !in 26..35) return false
|
||||
val decoded = address.decodeBase58() ?: return false
|
||||
val hash = sha256(decoded, 0, 21, 2)
|
||||
val hash = recursiveSha256(decoded, 0, 21, 2)
|
||||
return hash.sliceArray(0..3).contentEquals(decoded.sliceArray(21..24))
|
||||
} else {
|
||||
return validateSegwitAddress(address, testNet)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sha256(data: ByteArray, start: Int, len: Int, recursion: Int): ByteArray {
|
||||
private fun recursiveSha256(data: ByteArray, start: Int, len: Int, recursion: Int): ByteArray {
|
||||
if (recursion == 0) return data
|
||||
val md = MessageDigest.getInstance("SHA-256")
|
||||
md.update(data.sliceArray(start until start + len))
|
||||
return sha256(md.digest(), 0, 32, recursion - 1)
|
||||
return recursiveSha256(md.digest(), 0, 32, recursion - 1)
|
||||
}
|
||||
|
||||
private fun String.decodeBase58(): ByteArray? {
|
||||
|
|
|
|||
|
|
@ -58,6 +58,18 @@ class BitcoinTransactionBuilder(private val testNet: Boolean) {
|
|||
val signature = TransactionSignature(r, canonicalS)
|
||||
return ScriptBuilder.createInputScript(signature, ECKey.fromPublicOnly(publicKey))
|
||||
}
|
||||
|
||||
fun getEstimateSize(transactionData: TransactionData, walletPublicKey: ByteArray): Result<Int> {
|
||||
val buildTransactionResult = buildToSign(transactionData)
|
||||
when (buildTransactionResult) {
|
||||
is Result.Failure -> return buildTransactionResult
|
||||
is Result.Success -> {
|
||||
val hashes = buildTransactionResult.data
|
||||
val finalTransaction = buildToSend(ByteArray(64 * hashes.size) { 1 }, walletPublicKey)
|
||||
return Result.Success(finalTransaction.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun TransactionData.toBitcoinJTransaction(networkParameters: NetworkParameters?,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ class BitcoinWalletManager(
|
|||
walletConfig: WalletConfig,
|
||||
isTestNet: Boolean = false
|
||||
) : WalletManager,
|
||||
TransactionEstimator,
|
||||
TransactionSender,
|
||||
FeeProvider {
|
||||
|
||||
|
|
@ -48,9 +47,9 @@ class BitcoinWalletManager(
|
|||
null,
|
||||
"unknown",
|
||||
currencyWallet.address))
|
||||
} else {
|
||||
currencyWallet.pendingTransactions.clear()
|
||||
}
|
||||
} else {
|
||||
currencyWallet.pendingTransactions.clear()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -58,18 +57,6 @@ class BitcoinWalletManager(
|
|||
Log.e(this::class.java.simpleName, error?.message ?: "")
|
||||
}
|
||||
|
||||
override suspend fun getEstimateSize(transactionData: TransactionData): Result<Int> {
|
||||
val buildTransactionResult = transactionBuilder.buildToSign(transactionData)
|
||||
when (buildTransactionResult) {
|
||||
is Result.Failure -> return buildTransactionResult
|
||||
is Result.Success -> {
|
||||
val hashes = buildTransactionResult.data
|
||||
val finalTransaction = transactionBuilder.buildToSend(ByteArray(64 * hashes.size) {1}, walletPublicKey)
|
||||
return Result.Success(finalTransaction.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun send(transactionData: TransactionData, signer: TransactionSigner): SimpleResult {
|
||||
val buildTransactionResult = transactionBuilder.buildToSign(transactionData)
|
||||
when (buildTransactionResult) {
|
||||
|
|
@ -90,10 +77,11 @@ class BitcoinWalletManager(
|
|||
when (val result = networkManager.getFee()) {
|
||||
is Result.Failure -> return result
|
||||
is Result.Success -> {
|
||||
val sizeResult = getEstimateSize(
|
||||
val sizeResult = transactionBuilder.getEstimateSize(
|
||||
TransactionData(amount,
|
||||
Amount(1.toBigDecimal().divide(SATOSHI_IN_BTC), blockchain),
|
||||
address, destination)
|
||||
address, destination),
|
||||
walletPublicKey
|
||||
)
|
||||
when (sizeResult) {
|
||||
is Result.Failure -> return sizeResult
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import co.nstant.`in`.cbor.CborDecoder
|
|||
import co.nstant.`in`.cbor.CborEncoder
|
||||
import co.nstant.`in`.cbor.model.Array
|
||||
import co.nstant.`in`.cbor.model.ByteString
|
||||
import co.nstant.`in`.cbor.model.DataItem
|
||||
import co.nstant.`in`.cbor.model.UnsignedInteger
|
||||
import com.tangem.blockchain.cardano.crypto.Blake2b
|
||||
import com.tangem.blockchain.common.extensions.decodeBase58
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.tangem.blockchain.common.TransactionData
|
|||
import com.tangem.blockchain.common.extensions.decodeBase58
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.math.BigDecimal
|
||||
|
||||
class CardanoTransactionBuilder() {
|
||||
var unspentOutputs: List<UnspentOutput> = listOf()
|
||||
|
|
@ -143,6 +144,21 @@ class CardanoTransactionBuilder() {
|
|||
return fullAmount - (amount + fee)
|
||||
}
|
||||
|
||||
fun getEstimateSize(transactionData: TransactionData, walletPublicKey: ByteArray): Int {
|
||||
val dummyFeeValue = BigDecimal.valueOf(0.1)
|
||||
|
||||
val dummyFee = transactionData.amount.copy(value = dummyFeeValue)
|
||||
val dummyAmount =
|
||||
transactionData.amount.copy(value = transactionData.amount.value!! - dummyFeeValue)
|
||||
|
||||
val dummyTransactionData = transactionData.copy(
|
||||
amount = dummyAmount,
|
||||
fee = dummyFee
|
||||
)
|
||||
buildToSign(dummyTransactionData)
|
||||
return buildToSend(ByteArray(64), walletPublicKey).size
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PROTOCOL_MAGIC: Long = 764824073
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ class CardanoWalletManager(
|
|||
private val walletPublicKey: ByteArray,
|
||||
walletConfig: WalletConfig
|
||||
) : WalletManager,
|
||||
TransactionEstimator,
|
||||
TransactionSender,
|
||||
FeeProvider {
|
||||
|
||||
|
|
@ -47,21 +46,6 @@ class CardanoWalletManager(
|
|||
Log.e(this::class.java.simpleName, error?.message ?: "")
|
||||
}
|
||||
|
||||
override suspend fun getEstimateSize(transactionData: TransactionData): Int {
|
||||
val dummyFeeValue = BigDecimal.valueOf(0.1)
|
||||
|
||||
val dummyFee = transactionData.amount.copy(value = dummyFeeValue)
|
||||
val dummyAmount =
|
||||
transactionData.amount.copy(value = transactionData.amount.value!! - dummyFeeValue)
|
||||
|
||||
val dummyTransactionData = transactionData.copy(
|
||||
amount = dummyAmount,
|
||||
fee = dummyFee
|
||||
)
|
||||
transactionBuilder.buildToSign(dummyTransactionData)
|
||||
return transactionBuilder.buildToSend(ByteArray(64), walletPublicKey).size
|
||||
}
|
||||
|
||||
override suspend fun send(transactionData: TransactionData, signer: TransactionSigner): SimpleResult {
|
||||
val transactionHash = transactionBuilder.buildToSign(transactionData)
|
||||
|
||||
|
|
@ -77,8 +61,9 @@ class CardanoWalletManager(
|
|||
override suspend fun getFee(amount: Amount, source: String, destination: String): Result<List<Amount>> {
|
||||
val a = 0.155381
|
||||
val b = 0.000043946
|
||||
val size = getEstimateSize(TransactionData(amount, null, source, destination))
|
||||
|
||||
val size = transactionBuilder.getEstimateSize(
|
||||
TransactionData(amount, null, source, destination), walletPublicKey
|
||||
)
|
||||
val fee = (a + b * size).toBigDecimal()
|
||||
return Result.Success(listOf(Amount(blockchain.currency, fee, source, blockchain.decimals)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,10 +13,6 @@ interface WalletManager {
|
|||
suspend fun update()
|
||||
}
|
||||
|
||||
interface TransactionEstimator {
|
||||
suspend fun getEstimateSize(transactionData: TransactionData): Result<Int>
|
||||
}
|
||||
|
||||
interface TransactionSender {
|
||||
suspend fun send(transactionData: TransactionData, signer: TransactionSigner) : SimpleResult
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,35 +15,40 @@ object WalletManagerFactory {
|
|||
val blockchainName: String = card.cardData?.blockchainName ?: return null
|
||||
|
||||
when {
|
||||
blockchainName.contains("btc") || blockchainName.contains("bitcoin") -> {
|
||||
blockchainName == Blockchain.Bitcoin.id -> {
|
||||
return BitcoinWalletManager(
|
||||
cardId = card.cardId,
|
||||
walletPublicKey = walletPublicKey,
|
||||
walletConfig = WalletConfig(true, true)
|
||||
)
|
||||
}
|
||||
blockchainName == Blockchain.BitcoinTestnet.id -> {
|
||||
return BitcoinWalletManager(
|
||||
cardId = card.cardId,
|
||||
walletPublicKey = walletPublicKey,
|
||||
walletConfig = WalletConfig(true, true),
|
||||
isTestNet = isTestNet(blockchainName))
|
||||
isTestNet = true
|
||||
)
|
||||
}
|
||||
blockchainName.contains("eth") -> {
|
||||
val chain = if (isTestNet(blockchainName)) {
|
||||
Chain.EthereumClassicTestnet
|
||||
} else {
|
||||
Chain.Mainnet
|
||||
}
|
||||
blockchainName == Blockchain.Ethereum.id -> {
|
||||
val chain = Chain.Mainnet
|
||||
return EthereumWalletManager(
|
||||
cardId = card.cardId,
|
||||
walletPublicKey = walletPublicKey,
|
||||
walletConfig = WalletConfig(true, true),
|
||||
chain = chain)
|
||||
chain = chain
|
||||
)
|
||||
}
|
||||
blockchainName.contains("xlm") -> {
|
||||
blockchainName == Blockchain.Stellar.id -> {
|
||||
val token = getToken(card)
|
||||
return StellarWalletManager(
|
||||
cardId = card.cardId,
|
||||
walletPublicKey = walletPublicKey,
|
||||
walletConfig = WalletConfig(true, token == null),
|
||||
token = token,
|
||||
isTestNet = isTestNet(blockchainName))
|
||||
token = token
|
||||
)
|
||||
}
|
||||
blockchainName.contains("cardano") -> {
|
||||
blockchainName == Blockchain.Cardano.id -> {
|
||||
return CardanoWalletManager(
|
||||
cardId = card.cardId,
|
||||
walletPublicKey = walletPublicKey,
|
||||
|
|
@ -61,8 +66,6 @@ object WalletManagerFactory {
|
|||
}
|
||||
}
|
||||
|
||||
private fun isTestNet(blockchainName: String) = blockchainName.contains("test")
|
||||
|
||||
private fun getToken(card: Card): Token? {
|
||||
val symbol = card.cardData?.tokenSymbol ?: return null
|
||||
val contractAddress = card.cardData?.tokenContractAddress ?: return null
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue