Updated on 2026-08-14
This commit is contained in:
commit
794e24c69c
16 changed files with 2677 additions and 6 deletions
|
|
@ -60,6 +60,8 @@ dependencies {
|
|||
implementation "com.github.walleth.kethereum:crypto_api:$kethereum_version"
|
||||
implementation "com.github.walleth.kethereum:model:$kethereum_version"
|
||||
|
||||
implementation 'co.nstant.in:cbor:0.8'
|
||||
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2'
|
||||
testImplementation "com.google.truth:truth:1.0"
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
|
|
|
|||
|
|
@ -90,7 +90,8 @@ class BitcoinNetworkManager(private val isTestNet: Boolean) : BitcoinProvider {
|
|||
data class BitcoinAddressResponse(
|
||||
val balance: Long,
|
||||
val hasUnconfirmed: Boolean,
|
||||
val unspentTransactions: List<UnspentTransaction>)
|
||||
val unspentTransactions: List<UnspentTransaction>
|
||||
)
|
||||
|
||||
data class BitcoinFee(
|
||||
val minimalPerKb: BigDecimal,
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class BlockchainInfoProvider(
|
|||
|
||||
val addressData = addressDeferred.await()
|
||||
val unspents = unspentsDeferred.await()
|
||||
val unconfinedTransactions = addressData.transactions?.find { it.blockHeight == 0L } != null
|
||||
val unconfirmedTransactions = addressData.transactions?.find { it.blockHeight == 0L } != null
|
||||
|
||||
val bitcoinUnspents = unspents.unspentOutputs.map {
|
||||
UnspentTransaction(
|
||||
|
|
@ -36,7 +36,7 @@ class BlockchainInfoProvider(
|
|||
Result.Success(
|
||||
BitcoinAddressResponse(
|
||||
addressData.finalBalance
|
||||
?: 0L, unconfinedTransactions, bitcoinUnspents))
|
||||
?: 0L, unconfirmedTransactions, bitcoinUnspents))
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
Result.Failure(exception)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
package com.tangem.blockchain.cardano
|
||||
|
||||
import co.nstant.`in`.cbor.CborBuilder
|
||||
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
|
||||
import com.tangem.blockchain.common.extensions.encodeBase58
|
||||
import org.spongycastle.crypto.util.DigestFactory
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.zip.CRC32
|
||||
|
||||
class CardanoAddressFactory {
|
||||
companion object {
|
||||
fun makeAddress(cardPublicKey: ByteArray, testNet: Boolean = false): String {
|
||||
val extendedPublicKey = extendPublicKey(cardPublicKey)
|
||||
|
||||
val pubKeyWithAttributesBaos = ByteArrayOutputStream()
|
||||
CborEncoder(pubKeyWithAttributesBaos).encode(CborBuilder()
|
||||
.addArray()
|
||||
.add(0)
|
||||
.addArray()
|
||||
.add(0)
|
||||
.add(extendedPublicKey)
|
||||
.end()
|
||||
.addMap()
|
||||
.end()
|
||||
.end()
|
||||
.build())
|
||||
val pubKeyWithAttributes = pubKeyWithAttributesBaos.toByteArray()
|
||||
|
||||
val sha3Digest = DigestFactory.createSHA3_256()
|
||||
sha3Digest.update(pubKeyWithAttributes, 0, pubKeyWithAttributes.size)
|
||||
val sha3Hash = ByteArray(32)
|
||||
sha3Digest.doFinal(sha3Hash, 0)
|
||||
|
||||
val blake2b = Blake2b.Digest.newInstance(28)
|
||||
val blakeHash = blake2b.digest(sha3Hash)
|
||||
|
||||
val hashWithAttributesBaos = ByteArrayOutputStream()
|
||||
CborEncoder(hashWithAttributesBaos).encode(CborBuilder()
|
||||
.addArray()
|
||||
.add(blakeHash)
|
||||
.addMap() //additional attributes
|
||||
.end()
|
||||
.add(0) //address type
|
||||
.end()
|
||||
.build())
|
||||
val hashWithAttributes = hashWithAttributesBaos.toByteArray()
|
||||
|
||||
val crc32 = CRC32()
|
||||
crc32.update(hashWithAttributes)
|
||||
val checksum = crc32.value
|
||||
|
||||
val addressItem = CborBuilder().add(hashWithAttributes).build().get(0)
|
||||
addressItem.setTag(24)
|
||||
|
||||
//addr + checksum
|
||||
val addressBaos = ByteArrayOutputStream()
|
||||
CborEncoder(addressBaos).encode(CborBuilder()
|
||||
.addArray()
|
||||
.add(addressItem)
|
||||
.add(checksum)
|
||||
.end()
|
||||
.build())
|
||||
|
||||
val hexAddress = addressBaos.toByteArray()
|
||||
return hexAddress.encodeBase58()
|
||||
}
|
||||
|
||||
fun extendPublicKey(publicKey: ByteArray): ByteArray {
|
||||
val zeroBytes = ByteArray(32)
|
||||
zeroBytes.fill(0)
|
||||
return publicKey + zeroBytes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CardanoAddressValidator {
|
||||
companion object {
|
||||
fun validate(address: String): Boolean {
|
||||
val decoded = address.decodeBase58() ?: return false
|
||||
|
||||
return try {
|
||||
val bais = ByteArrayInputStream(decoded)
|
||||
val addressList =
|
||||
(CborDecoder(bais).decode()[0] as Array).dataItems
|
||||
val addressItemBytes = (addressList[0] as ByteString).bytes
|
||||
val checksum = (addressList[1] as UnsignedInteger).value.toLong()
|
||||
|
||||
val crc32 = CRC32()
|
||||
crc32.update(addressItemBytes)
|
||||
val calculatedChecksum = crc32.value
|
||||
|
||||
checksum == calculatedChecksum
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
package com.tangem.blockchain.cardano
|
||||
|
||||
import co.nstant.`in`.cbor.CborBuilder
|
||||
import co.nstant.`in`.cbor.CborDecoder
|
||||
import co.nstant.`in`.cbor.CborEncoder
|
||||
import co.nstant.`in`.cbor.builder.ArrayBuilder
|
||||
import com.tangem.blockchain.cardano.crypto.Blake2b
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.extensions.decodeBase58
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
class CardanoTransactionBuilder() {
|
||||
var unspentOutputs: List<UnspentOutput> = listOf()
|
||||
var transactionBody: ByteArray = ByteArray(0)
|
||||
|
||||
fun buildToSign(transactionData: TransactionData): ByteArray {
|
||||
val transactionBuilder = CborBuilder()
|
||||
|
||||
val transactionArray = transactionBuilder.addArray()
|
||||
transactionArray.addInputArray()
|
||||
transactionArray.addOutputArray(transactionData)
|
||||
transactionArray.addMap().end()
|
||||
|
||||
val transactionBaos = ByteArrayOutputStream()
|
||||
CborEncoder(transactionBaos).encode(transactionBuilder.build())
|
||||
transactionBody = transactionBaos.toByteArray()
|
||||
|
||||
val blake2b = Blake2b.Digest.newInstance(32)
|
||||
val transactionBodyHash = blake2b.digest(transactionBody)
|
||||
|
||||
val magicBaos = ByteArrayOutputStream()
|
||||
CborEncoder(magicBaos).encode(CborBuilder().add(PROTOCOL_MAGIC).build())
|
||||
val magic = magicBaos.toByteArray()
|
||||
|
||||
//dataToSign prefix
|
||||
val prefixedHashBaos = ByteArrayOutputStream()
|
||||
prefixedHashBaos.write(byteArrayOf(0x01.toByte()))
|
||||
prefixedHashBaos.write(magic)
|
||||
prefixedHashBaos.write(byteArrayOf(0x58.toByte(), 0x20.toByte()))
|
||||
prefixedHashBaos.write(transactionBodyHash)
|
||||
return prefixedHashBaos.toByteArray()
|
||||
}
|
||||
|
||||
fun buildToSend(signature: ByteArray, publicKey: ByteArray): ByteArray {
|
||||
val extendedPublicKey = CardanoAddressFactory.extendPublicKey(publicKey)
|
||||
|
||||
//pubkey + signature
|
||||
val witnessBodyBaos = ByteArrayOutputStream()
|
||||
CborEncoder(witnessBodyBaos).encode(CborBuilder()
|
||||
.addArray()
|
||||
.add(extendedPublicKey)
|
||||
.add(signature)
|
||||
.end()
|
||||
.build())
|
||||
val witnessBody = witnessBodyBaos.toByteArray()
|
||||
val witnessBodyItem = CborBuilder().add(witnessBody).build()[0]
|
||||
witnessBodyItem.setTag(24)
|
||||
|
||||
val witnessBuilder = CborBuilder()
|
||||
val witnessArrayBuilder = witnessBuilder.addArray()
|
||||
|
||||
//witness type + witness body
|
||||
for (utxo in unspentOutputs) {
|
||||
witnessArrayBuilder
|
||||
.addArray()
|
||||
.add(0)
|
||||
.add(witnessBodyItem)
|
||||
.end()
|
||||
}
|
||||
|
||||
val witnessBaos = ByteArrayOutputStream()
|
||||
CborEncoder(witnessBaos).encode(witnessBuilder.build())
|
||||
val witness = witnessBaos.toByteArray()
|
||||
|
||||
val transactionBaos = ByteArrayOutputStream()
|
||||
transactionBaos.write(byteArrayOf(0x82.toByte()))
|
||||
transactionBaos.write(transactionBody)
|
||||
transactionBaos.write(witness)
|
||||
return transactionBaos.toByteArray()
|
||||
}
|
||||
|
||||
private fun ArrayBuilder<CborBuilder>.addInputArray() {
|
||||
val inputArray = this.startArray()
|
||||
|
||||
for (utxo in unspentOutputs) {
|
||||
val inputBaos = ByteArrayOutputStream()
|
||||
CborEncoder(inputBaos).encode(CborBuilder()
|
||||
.addArray()
|
||||
.add(utxo.hash)
|
||||
.add(utxo.outputIndex)
|
||||
.end()
|
||||
.build())
|
||||
val input = inputBaos.toByteArray()
|
||||
|
||||
val inputItem = CborBuilder().add(input).build().get(0)
|
||||
inputItem.setTag(24)
|
||||
//input type + input
|
||||
inputArray
|
||||
.addArray()
|
||||
.add(0)
|
||||
.add(inputItem)
|
||||
.end()
|
||||
}
|
||||
inputArray.end()
|
||||
}
|
||||
|
||||
private fun ArrayBuilder<CborBuilder>.addOutputArray(transactionData: TransactionData) {
|
||||
val amount = transactionData.amount.value!!
|
||||
.movePointRight(transactionData.amount.decimals.toInt()).toLong()
|
||||
val fee = transactionData.fee!!.value!!
|
||||
.movePointRight(transactionData.fee.decimals.toInt()).toLong()
|
||||
val change = calculateChange(amount, fee)
|
||||
|
||||
val outputArray = this.startArray()
|
||||
|
||||
//1st output
|
||||
val targetAddressItem =
|
||||
CborDecoder(ByteArrayInputStream(transactionData.destinationAddress.decodeBase58()))
|
||||
.decode()[0]
|
||||
outputArray
|
||||
.addArray()
|
||||
.add(targetAddressItem)
|
||||
.add(amount)
|
||||
.end()
|
||||
|
||||
//2nd output (optional)
|
||||
if (change > 0) {
|
||||
val myAddressItem =
|
||||
CborDecoder(ByteArrayInputStream(transactionData.sourceAddress.decodeBase58()))
|
||||
.decode()[0]
|
||||
outputArray
|
||||
.addArray()
|
||||
.add(myAddressItem)
|
||||
.add(change)
|
||||
.end()
|
||||
}
|
||||
outputArray.end()
|
||||
}
|
||||
|
||||
private fun calculateChange(amount: Long, fee: Long): Long {
|
||||
val fullAmount = unspentOutputs.map { it.amount }.sum()
|
||||
return fullAmount - (amount + fee)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PROTOCOL_MAGIC: Long = 764824073
|
||||
}
|
||||
}
|
||||
|
||||
class UnspentOutput(
|
||||
val amount: Long,
|
||||
val outputIndex: Long,
|
||||
val hash: ByteArray
|
||||
)
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
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
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.extensions.Result
|
||||
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
|
||||
) : WalletManager,
|
||||
TransactionEstimator,
|
||||
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()
|
||||
|
||||
override suspend fun update() {
|
||||
val response = networkManager.getInfo(address)
|
||||
when (response) {
|
||||
is Result.Success -> updateWallet(response.data)
|
||||
is Result.Failure -> updateError(response.error)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateWallet(response: CardanoAddressResponse) {
|
||||
Log.d(this::class.java.simpleName, "Balance is ${response.balance.toString()}")
|
||||
currencyWallet.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 ?: "")
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
when (val signerResponse = signer.sign(arrayOf(transactionHash), cardId)) {
|
||||
is TaskEvent.Event -> {
|
||||
val transactionToSend = transactionBuilder.buildToSend(signerResponse.data.signature, walletPublicKey)
|
||||
return networkManager.sendTransaction(transactionToSend.encodeBase64NoWrap())
|
||||
}
|
||||
is TaskEvent.Completion -> return SimpleResult.Failure(signerResponse.error)
|
||||
}
|
||||
}
|
||||
|
||||
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 fee = (a + b * size).toBigDecimal()
|
||||
return Result.Success(listOf(Amount(blockchain.currency, fee, source, blockchain.decimals)))
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,67 @@
|
|||
package com.tangem.blockchain.cardano.network
|
||||
|
||||
import com.tangem.blockchain.cardano.UnspentOutput
|
||||
import com.tangem.blockchain.cardano.network.adalite.AdaliteProvider
|
||||
import com.tangem.blockchain.cardano.network.api.AdaliteApi
|
||||
import com.tangem.blockchain.common.extensions.Result
|
||||
import com.tangem.blockchain.common.extensions.SimpleResult
|
||||
import com.tangem.blockchain.common.network.API_ADALITE
|
||||
import com.tangem.blockchain.common.network.API_ADALITE_RESERVE
|
||||
import com.tangem.blockchain.common.network.createRetrofitInstance
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class CardanoNetworkManager {
|
||||
private val adaliteProvider by lazy {
|
||||
val api = createRetrofitInstance(API_ADALITE)
|
||||
.create(AdaliteApi::class.java)
|
||||
AdaliteProvider(api)
|
||||
}
|
||||
|
||||
private val adaliteReserveProvider by lazy {
|
||||
val api = createRetrofitInstance(API_ADALITE_RESERVE)
|
||||
.create(AdaliteApi::class.java)
|
||||
AdaliteProvider(api)
|
||||
}
|
||||
|
||||
private var provider = adaliteProvider
|
||||
|
||||
private fun changeProvider() {
|
||||
provider = if (provider == adaliteProvider) adaliteReserveProvider else adaliteProvider
|
||||
}
|
||||
|
||||
suspend fun getInfo(address: String): Result<CardanoAddressResponse> {
|
||||
val result = provider.getInfo(address)
|
||||
when (result) {
|
||||
is Result.Success -> return result
|
||||
is Result.Failure -> {
|
||||
if (result.error is IOException || result.error is HttpException) {
|
||||
changeProvider()
|
||||
return provider.getInfo(address)
|
||||
} else {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendTransaction(transaction: String): SimpleResult {
|
||||
val result = provider.sendTransaction(transaction)
|
||||
when (result) {
|
||||
is SimpleResult.Success -> return result
|
||||
is SimpleResult.Failure -> {
|
||||
if (result.error is IOException || result.error is HttpException) {
|
||||
changeProvider()
|
||||
return provider.sendTransaction(transaction)
|
||||
} else {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class CardanoAddressResponse(
|
||||
val balance: Long,
|
||||
val unspentOutputs: List<UnspentOutput>
|
||||
)
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.blockchain.cardano.network.api
|
||||
|
||||
import com.tangem.blockchain.cardano.network.adalite.AdaliteAddress
|
||||
import com.tangem.blockchain.cardano.network.adalite.AdaliteSendBody
|
||||
import com.tangem.blockchain.cardano.network.adalite.AdaliteUnspents
|
||||
import retrofit2.http.*
|
||||
import shadow.okhttp3.ResponseBody
|
||||
|
||||
interface AdaliteApi {
|
||||
@GET("/api/addresses/summary/{address}")
|
||||
suspend fun getAddress(@Path("address") address: String): AdaliteAddress
|
||||
|
||||
@Headers("Content-Type: application/json")
|
||||
@POST("/api/bulk/addresses/utxo")
|
||||
suspend fun getUnspents(@Body address: String): AdaliteUnspents
|
||||
|
||||
@Headers("Content-Type: application/json")
|
||||
@POST("/api/v2/txs/signed")
|
||||
suspend fun sendTransaction(@Body adaliteBody: AdaliteSendBody): ResponseBody // List<Any>?
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package com.tangem.blockchain.cardano.network.adalite
|
||||
|
||||
import com.tangem.blockchain.cardano.network.CardanoAddressResponse
|
||||
import com.tangem.blockchain.cardano.UnspentOutput
|
||||
import com.tangem.blockchain.cardano.network.api.AdaliteApi
|
||||
import com.tangem.blockchain.common.extensions.Result
|
||||
import com.tangem.blockchain.common.extensions.SimpleResult
|
||||
import com.tangem.blockchain.common.extensions.retryIO
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
|
||||
class AdaliteProvider(private val api: AdaliteApi) {
|
||||
|
||||
suspend fun getInfo(address: String): Result<CardanoAddressResponse> {
|
||||
return try {
|
||||
coroutineScope {
|
||||
val addressDeferred = retryIO { async { api.getAddress(address) } }
|
||||
val unspentsDeferred = retryIO { async { api.getUnspents(address) } }
|
||||
|
||||
val addressData = addressDeferred.await()
|
||||
val unspents = unspentsDeferred.await()
|
||||
|
||||
val cardanoUnspents = unspents.data.map {
|
||||
UnspentOutput(
|
||||
it.amountData!!.amount!!,
|
||||
it.outputIndex!!.toLong(),
|
||||
it.hash!!.toByteArray()
|
||||
)
|
||||
}
|
||||
|
||||
Result.Success(
|
||||
CardanoAddressResponse(
|
||||
addressData.data!!.balanceData!!.amount!!,
|
||||
cardanoUnspents
|
||||
)
|
||||
)
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
Result.Failure(exception)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendTransaction(transaction: String): SimpleResult {
|
||||
return try {
|
||||
retryIO { api.sendTransaction(AdaliteSendBody(transaction)) }
|
||||
SimpleResult.Success
|
||||
} catch (exception: Exception) {
|
||||
SimpleResult.Failure(exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class AdaliteSendBody(val signedTransaction: String)
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package com.tangem.blockchain.cardano.network.adalite
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class AdaliteAddress(
|
||||
@Json(name = "final_balance")
|
||||
var data: AdaliteAddressData? = null
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class AdaliteAddressData(
|
||||
@Json(name = "caBalance")
|
||||
var balanceData: BalanceData? = null
|
||||
|
||||
// @Json(name = "caTxList")
|
||||
// var transactions: List<AdaliteTransaction>
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class BalanceData(
|
||||
@Json(name = "getCoin")
|
||||
var amount: Long? = null
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class AdaliteUnspents(
|
||||
@Json(name = "Right")
|
||||
var data: List<AdaliteUtxo>
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class AdaliteUtxo(
|
||||
@Json(name = "cuId")
|
||||
var hash: String? = null,
|
||||
|
||||
@Json(name = "cuOutIndex")
|
||||
var outputIndex: Int? = null,
|
||||
|
||||
@Json(name = "cuCoins")
|
||||
var amountData: BalanceData? = null
|
||||
)
|
||||
|
||||
//@JsonClass(generateAdapter = true)
|
||||
//data class AdaliteTransaction(
|
||||
// @Json(name = "ctbId")
|
||||
// var hash: String? = null
|
||||
//)
|
||||
|
|
@ -2,6 +2,8 @@ package com.tangem.blockchain.common
|
|||
|
||||
import com.tangem.blockchain.bitcoin.BitcoinAddressFactory
|
||||
import com.tangem.blockchain.bitcoin.BitcoinAddressValidator
|
||||
import com.tangem.blockchain.cardano.CardanoAddressFactory
|
||||
import com.tangem.blockchain.cardano.CardanoAddressValidator
|
||||
import com.tangem.blockchain.eth.EthereumAddressFactory
|
||||
import com.tangem.blockchain.eth.EthereumAddressValidator
|
||||
import com.tangem.blockchain.stellar.StellarAddressFactory
|
||||
|
|
@ -37,7 +39,7 @@ enum class Blockchain(
|
|||
BitcoinTestnet -> BitcoinAddressFactory.makeAddress(cardPublicKey, testNet = true)
|
||||
Ethereum -> EthereumAddressFactory.makeAddress(cardPublicKey)
|
||||
// Rootstock -> RootstockAddressFactory.makeAddress(cardPublicKey)
|
||||
// Cardano -> CardanoAddressFactory.makeAddress(cardPublicKey)
|
||||
Cardano -> CardanoAddressFactory.makeAddress(cardPublicKey)
|
||||
// Ripple -> RippleAddressFactory.makeAddress(cardPublicKey)
|
||||
// Binance -> BinanceAddressFactory.makeAddress(cardPublicKey)
|
||||
Stellar -> StellarAddressFactory.makeAddress(cardPublicKey)
|
||||
|
|
@ -52,7 +54,7 @@ enum class Blockchain(
|
|||
BitcoinTestnet -> BitcoinAddressValidator.validate(address, testNet = true)
|
||||
Ethereum -> EthereumAddressValidator.validate(address)
|
||||
// Rootstock -> RootstockAddressValidator.validate(address)
|
||||
// Cardano -> CardanoAddressValidator.validate(address)
|
||||
Cardano -> CardanoAddressValidator.validate(address)
|
||||
// Ripple -> RippleAddressValidator.validate(address)
|
||||
// Binance -> BinanceAddressValidator.validate(address)
|
||||
// Stellar -> StellarAddressValidator.validate(address)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.blockchain.common
|
||||
|
||||
import com.tangem.blockchain.bitcoin.BitcoinWalletManager
|
||||
import com.tangem.blockchain.cardano.CardanoWalletManager
|
||||
import com.tangem.blockchain.eth.Chain
|
||||
import com.tangem.blockchain.eth.EthereumWalletManager
|
||||
import com.tangem.blockchain.stellar.StellarWalletManager
|
||||
|
|
@ -41,6 +42,13 @@ object WalletManagerFactory {
|
|||
token = token,
|
||||
isTestNet = isTestNet(blockchainName))
|
||||
}
|
||||
blockchainName.contains("cardano") -> {
|
||||
return CardanoWalletManager(
|
||||
cardId = card.cardId,
|
||||
walletPublicKey = walletPublicKey,
|
||||
walletConfig = WalletConfig(false, true)
|
||||
)
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.blockchain.common.extensions
|
||||
|
||||
import android.util.Base64
|
||||
import org.bitcoinj.core.Base58
|
||||
|
||||
fun ByteArray.encodeBase58(): String {
|
||||
return Base58.encode(this)
|
||||
}
|
||||
|
||||
fun ByteArray.encodeBase64NoWrap(): String {
|
||||
return Base64.encodeToString(this, Base64.NO_WRAP)
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.blockchain.common.extensions
|
||||
|
||||
import org.bitcoinj.core.AddressFormatException
|
||||
import org.bitcoinj.core.Base58
|
||||
|
||||
fun String.decodeBase58(): ByteArray? {
|
||||
return try {
|
||||
Base58.decode(this)
|
||||
} catch (exception: AddressFormatException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
@ -41,4 +41,6 @@ const val API_MATIC_TESTNET = "https://testnet2.matic.network/"
|
|||
const val API_STELLAR = "https://horizon.stellar.org/"
|
||||
const val API_STELLAR_RESERVE = "https://horizon.sui.li/"
|
||||
const val API_STELLAR_TESTNET = "https://horizon-testnet.stellar.org/"
|
||||
const val API_BLOCKCHAIN_INFO = "https://blockchain.info/"
|
||||
const val API_BLOCKCHAIN_INFO = "https://blockchain.info/"
|
||||
const val API_ADALITE = "https://explorer2.adalite.io"
|
||||
const val API_ADALITE_RESERVE = "https://nodes.southeastasia.cloudapp.azure.com"
|
||||
Loading…
Add table
Add a link
Reference in a new issue