Updated on 2026-08-14
This commit is contained in:
parent
ea92fac837
commit
da726c3f5a
11 changed files with 537 additions and 6 deletions
|
|
@ -0,0 +1,37 @@
|
|||
package com.tangem.blockchain.blockchains.tezos
|
||||
|
||||
import com.tangem.blockchain.common.AddressService
|
||||
import com.tangem.common.extensions.calculateSha256
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import org.bitcoinj.core.Base58
|
||||
import org.spongycastle.jcajce.provider.digest.Blake2b
|
||||
|
||||
class TezosAddressService : AddressService {
|
||||
override fun makeAddress(walletPublicKey: ByteArray): String {
|
||||
val publicKeyHash = Blake2b.Blake2b160().digest(walletPublicKey)
|
||||
|
||||
val tz1Prefix = "06A19F".hexToBytes()
|
||||
val prefixedHash = tz1Prefix + publicKeyHash
|
||||
|
||||
val checksum = prefixedHash.calculateTezosChecksum()
|
||||
val prefixedHashWithChecksum = prefixedHash + checksum
|
||||
|
||||
return Base58.encode(prefixedHashWithChecksum)
|
||||
}
|
||||
|
||||
override fun validate(address: String): Boolean {
|
||||
val prefixedHashWithChecksum = Base58.decode(address)
|
||||
if (prefixedHashWithChecksum == null || prefixedHashWithChecksum.size != 27) return false
|
||||
|
||||
val prefixedHash = prefixedHashWithChecksum.copyOf(23)
|
||||
val checksum = prefixedHashWithChecksum.copyOfRange(23, 27)
|
||||
|
||||
val calculatedChecksum = prefixedHash.calculateTezosChecksum()
|
||||
|
||||
return calculatedChecksum.contentEquals(checksum)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun ByteArray.calculateTezosChecksum() = this.calculateSha256().calculateSha256().copyOfRange(0, 4)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package com.tangem.blockchain.blockchains.tezos
|
||||
|
||||
import com.tangem.blockchain.blockchains.tezos.TezosAddressService.Companion.calculateTezosChecksum
|
||||
import com.tangem.blockchain.blockchains.tezos.network.TezosOperationContent
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.blockchain.extensions.bigIntegerValue
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import org.bitcoinj.core.Base58
|
||||
import org.spongycastle.jcajce.provider.digest.Blake2b
|
||||
|
||||
class TezosTransactionBuilder(private val walletPublicKey: ByteArray) {
|
||||
var counter: Long? = null
|
||||
|
||||
fun buildContents(transactionData: TransactionData,
|
||||
publicKeyRevealed: Boolean
|
||||
): Result<List<TezosOperationContent>> {
|
||||
if (counter == null) return Result.Failure(Exception("counter is null"))
|
||||
var counter = counter!!
|
||||
|
||||
val contents = arrayListOf<TezosOperationContent>()
|
||||
|
||||
if (!publicKeyRevealed) {
|
||||
counter++
|
||||
val revealOp = TezosOperationContent(
|
||||
kind = "reveal",
|
||||
source = transactionData.sourceAddress,
|
||||
fee = "1300",
|
||||
counter = counter.toString(),
|
||||
gas_limit = "10000",
|
||||
storage_limit = "0",
|
||||
public_key = encodePublicKey(walletPublicKey)
|
||||
)
|
||||
contents.add(revealOp)
|
||||
}
|
||||
|
||||
counter++
|
||||
val transactionOp = TezosOperationContent(
|
||||
kind = "transaction",
|
||||
source = transactionData.sourceAddress,
|
||||
fee = "1350",
|
||||
counter = counter.toString(),
|
||||
gas_limit = "10600",
|
||||
storage_limit = "277",
|
||||
destination = transactionData.destinationAddress,
|
||||
amount = transactionData.amount.bigIntegerValue().toString()
|
||||
)
|
||||
contents.add(transactionOp)
|
||||
|
||||
return Result.Success(contents)
|
||||
}
|
||||
|
||||
fun buildToSign(forgedContents: String): ByteArray {
|
||||
val genericOperationWatermark = "03"
|
||||
return Blake2b.Blake2b256().digest((genericOperationWatermark + forgedContents).hexToBytes())
|
||||
}
|
||||
|
||||
fun buildToSend(signature: ByteArray, forgedContents: String) = forgedContents + signature.toHexString()
|
||||
|
||||
private fun encodePublicKey(pkUncompressed: ByteArray): String {
|
||||
val edpkPrefix = "0D0F25D9".hexToBytes()
|
||||
val prefixedPubKey = edpkPrefix + pkUncompressed
|
||||
|
||||
val checksum = prefixedPubKey.calculateTezosChecksum()
|
||||
val prefixedHashWithChecksum = prefixedPubKey + checksum
|
||||
|
||||
return Base58.encode(prefixedHashWithChecksum)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
package com.tangem.blockchain.blockchains.tezos
|
||||
|
||||
import android.util.Log
|
||||
import com.tangem.blockchain.blockchains.tezos.network.TezosInfoResponse
|
||||
import com.tangem.blockchain.blockchains.tezos.network.TezosNetworkManager
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.blockchain.extensions.SimpleResult
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import java.math.BigDecimal
|
||||
|
||||
class TezosWalletManager(
|
||||
cardId: String,
|
||||
wallet: Wallet,
|
||||
private val transactionBuilder: TezosTransactionBuilder,
|
||||
private val networkManager: TezosNetworkManager
|
||||
) : WalletManager(cardId, wallet), TransactionSender {
|
||||
|
||||
private val blockchain = wallet.blockchain
|
||||
private var publicKeyRevealed: Boolean? = null
|
||||
|
||||
override suspend fun update() {
|
||||
val response = networkManager.getInfo(wallet.address)
|
||||
when (response) {
|
||||
is Result.Success -> updateWallet(response.data)
|
||||
is Result.Failure -> updateError(response.error)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateWallet(response: TezosInfoResponse) {
|
||||
Log.d(this::class.java.simpleName, "Balance is ${response.balance}")
|
||||
wallet.amounts[AmountType.Coin]?.value = response.balance
|
||||
transactionBuilder.counter = response.counter
|
||||
}
|
||||
|
||||
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 {
|
||||
if (publicKeyRevealed == null) return SimpleResult.Failure(Exception("publicKeyRevealed is null"))
|
||||
|
||||
val contents =
|
||||
when (val response = transactionBuilder.buildContents(transactionData, publicKeyRevealed!!)) {
|
||||
is Result.Failure -> return SimpleResult.Failure(response.error)
|
||||
is Result.Success -> response.data
|
||||
}
|
||||
val header =
|
||||
when (val response = networkManager.getHeader()) {
|
||||
is Result.Failure -> return SimpleResult.Failure(response.error)
|
||||
is Result.Success -> response.data
|
||||
}
|
||||
val forgedContents = //TODO: CHANGE FOR PRODUCTION, this is potential security vulnerability, transaction should be forged locally
|
||||
when (val response = networkManager.forgeContents(header.hash, contents)) {
|
||||
is Result.Failure -> return SimpleResult.Failure(response.error)
|
||||
is Result.Success -> response.data
|
||||
}
|
||||
val dataToSign = transactionBuilder.buildToSign(forgedContents)
|
||||
|
||||
val signature = when (val signerResponse = signer.sign(arrayOf(dataToSign), cardId)) {
|
||||
is CompletionResult.Failure -> return SimpleResult.Failure(signerResponse.error)
|
||||
is CompletionResult.Success -> signerResponse.data.signature
|
||||
}
|
||||
|
||||
when (val response = networkManager.checkTransaction(header, contents, signature)) {
|
||||
is SimpleResult.Failure -> return response
|
||||
is SimpleResult.Success -> {
|
||||
val transactionToSend = transactionBuilder.buildToSend(signature,forgedContents)
|
||||
return networkManager.sendTransaction(transactionToSend)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getFee(amount: Amount, destination: String): Result<List<Amount>> {
|
||||
var fee: BigDecimal = BigDecimal.valueOf(0.00135)
|
||||
var error: Result.Failure? = null
|
||||
|
||||
coroutineScope {
|
||||
val publicKeyRevealedDeferred = async { networkManager.isPublicKeyRevealed(wallet.address) }
|
||||
val destinationInfoDeferred = async { networkManager.getInfo(destination) }
|
||||
|
||||
when (val result = publicKeyRevealedDeferred.await()) {
|
||||
is Result.Failure -> error = result
|
||||
is Result.Success -> {
|
||||
publicKeyRevealed = result.data
|
||||
if (!publicKeyRevealed!!) {
|
||||
fee += BigDecimal.valueOf(0.0013)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when (val result = destinationInfoDeferred.await()) {
|
||||
is Result.Failure -> error = result
|
||||
is Result.Success -> {
|
||||
if (result.data.balance.isZero()) {
|
||||
fee += BigDecimal.valueOf(0.257)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return if (error == null) Result.Success(listOf(Amount(fee, blockchain))) else error!!
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package com.tangem.blockchain.blockchains.tezos.network
|
||||
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Path
|
||||
|
||||
interface TezosApi {
|
||||
@GET("chains/main/blocks/head/context/contracts/{address}")
|
||||
suspend fun getAddressData(@Path("address") address: String): TezosAddressResponse
|
||||
|
||||
@GET("chains/main/blocks/head/header")
|
||||
suspend fun getHeader(): TezosHeaderResponse
|
||||
|
||||
@GET("chains/main/blocks/head/context/contracts/{address}/manager_key")
|
||||
suspend fun getManagerKey(@Path("address") address: String): String
|
||||
|
||||
@POST("chains/main/blocks/head/helpers/forge/operations")
|
||||
suspend fun forgeOperations(@Body tezosForgeBody: TezosForgeBody): String
|
||||
|
||||
@POST("chains/main/blocks/head/helpers/preapply/operations")
|
||||
suspend fun preapplyOperations(@Body tezosPreapplyBodyList: List<TezosPreapplyBody>)
|
||||
|
||||
@POST("injection/operation")
|
||||
suspend fun sendTransaction(@Body transaction: String)
|
||||
}
|
||||
|
||||
data class TezosForgeBody(
|
||||
val branch: String,
|
||||
val contents: List<TezosOperationContent>
|
||||
)
|
||||
|
||||
data class TezosOperationContent(
|
||||
val kind: String,
|
||||
val source: String,
|
||||
val fee: String,
|
||||
val counter: String,
|
||||
val gas_limit: String,
|
||||
val storage_limit: String,
|
||||
val public_key: String? = null,
|
||||
val destination: String? = null,
|
||||
val amount: String? = null
|
||||
)
|
||||
|
||||
data class TezosPreapplyBody(
|
||||
val protocol: String,
|
||||
val branch: String,
|
||||
val contents: List<TezosOperationContent>,
|
||||
val signature: String
|
||||
)
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
package com.tangem.blockchain.blockchains.tezos.network
|
||||
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.blockchain.extensions.SimpleResult
|
||||
import com.tangem.blockchain.network.API_TEZOS
|
||||
import com.tangem.blockchain.network.API_TEZOS_RESERVE
|
||||
import com.tangem.blockchain.network.createRetrofitInstance
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
import java.math.BigDecimal
|
||||
|
||||
class TezosNetworkManager {
|
||||
private val tezosProvider by lazy {
|
||||
val api = createRetrofitInstance(API_TEZOS)
|
||||
.create(TezosApi::class.java)
|
||||
TezosProvider(api)
|
||||
}
|
||||
|
||||
private val tezosReserveProvider by lazy {
|
||||
val api = createRetrofitInstance(API_TEZOS_RESERVE)
|
||||
.create(TezosApi::class.java)
|
||||
TezosProvider(api)
|
||||
}
|
||||
|
||||
var provider = tezosProvider
|
||||
|
||||
private fun changeProvider() {
|
||||
provider = if (provider == tezosProvider) tezosReserveProvider else tezosProvider
|
||||
}
|
||||
|
||||
suspend fun getInfo(address: String): Result<TezosInfoResponse> {
|
||||
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 isPublicKeyRevealed(address: String): Result<Boolean> {
|
||||
val result = provider.isPublicKeyRevealed(address)
|
||||
when (result) {
|
||||
is Result.Success -> return result
|
||||
is Result.Failure -> {
|
||||
if (result.error is IOException || result.error is HttpException) {
|
||||
changeProvider()
|
||||
return provider.isPublicKeyRevealed(address)
|
||||
} else {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getHeader(): Result<TezosHeader> {
|
||||
val result = provider.getHeader()
|
||||
when (result) {
|
||||
is Result.Success -> return result
|
||||
is Result.Failure -> {
|
||||
if (result.error is IOException || result.error is HttpException) {
|
||||
changeProvider()
|
||||
return provider.getHeader()
|
||||
} else {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun forgeContents(headerHash: String, contents: List<TezosOperationContent>): Result<String> {
|
||||
val result = provider.forgeContents(headerHash, contents)
|
||||
when (result) {
|
||||
is Result.Success -> return result
|
||||
is Result.Failure -> {
|
||||
if (result.error is IOException || result.error is HttpException) {
|
||||
changeProvider()
|
||||
return provider.forgeContents(headerHash, contents)
|
||||
} else {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun checkTransaction(
|
||||
header: TezosHeader,
|
||||
contents: List<TezosOperationContent>,
|
||||
signature: ByteArray
|
||||
): SimpleResult {
|
||||
val result = provider.checkTransaction(header, contents, signature)
|
||||
when (result) {
|
||||
is SimpleResult.Success -> return result
|
||||
is SimpleResult.Failure -> {
|
||||
if (result.error is IOException || result.error is HttpException) {
|
||||
changeProvider()
|
||||
return provider.checkTransaction(header, contents, signature)
|
||||
} 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 TezosInfoResponse(
|
||||
val balance: BigDecimal,
|
||||
val counter: Long
|
||||
)
|
||||
|
||||
data class TezosHeader(
|
||||
val hash: String,
|
||||
val protocol: String
|
||||
)
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package com.tangem.blockchain.blockchains.tezos.network
|
||||
|
||||
import com.tangem.blockchain.blockchains.tezos.TezosAddressService.Companion.calculateTezosChecksum
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.blockchain.extensions.SimpleResult
|
||||
import com.tangem.blockchain.extensions.retryIO
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import org.bitcoinj.core.Base58
|
||||
|
||||
class TezosProvider(private val api: TezosApi) {
|
||||
private val decimals = Blockchain.Tezos.decimals()
|
||||
|
||||
suspend fun getInfo(address: String): Result<TezosInfoResponse> {
|
||||
return try {
|
||||
val addressData = retryIO { api.getAddressData(address) }
|
||||
Result.Success(TezosInfoResponse(
|
||||
balance = addressData.balance!!.toBigDecimal().movePointLeft(decimals),
|
||||
counter = addressData.counter!!
|
||||
))
|
||||
} catch (exception: Exception) {
|
||||
Result.Failure(exception)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun isPublicKeyRevealed(address: String): Result<Boolean> {
|
||||
return try {
|
||||
retryIO { api.getManagerKey(address) }
|
||||
Result.Success(true)
|
||||
} catch (exception: Exception) { //TODO: check exception
|
||||
Result.Success(false)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getHeader(): Result<TezosHeader> {
|
||||
return try {
|
||||
val headerResponse = retryIO { api.getHeader() }
|
||||
Result.Success(TezosHeader(
|
||||
hash = headerResponse.hash!!,
|
||||
protocol = headerResponse.protocol!!
|
||||
))
|
||||
} catch (exception: Exception) {
|
||||
Result.Failure(exception)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun forgeContents(headerHash: String, contents: List<TezosOperationContent>): Result<String> {
|
||||
return try {
|
||||
val forgedContents = retryIO { api.forgeOperations(TezosForgeBody(headerHash, contents)) }
|
||||
Result.Success(forgedContents)
|
||||
} catch (exception: Exception) {
|
||||
Result.Failure(exception)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun checkTransaction(
|
||||
header: TezosHeader,
|
||||
contents: List<TezosOperationContent>,
|
||||
signature: ByteArray
|
||||
): SimpleResult {
|
||||
return try {
|
||||
val tezosPreapplyBody = TezosPreapplyBody(
|
||||
protocol = header.protocol,
|
||||
branch = header.hash,
|
||||
contents = contents,
|
||||
signature = encodeSignature(signature)
|
||||
)
|
||||
retryIO { api.preapplyOperations(listOf(tezosPreapplyBody)) }
|
||||
SimpleResult.Success
|
||||
} catch (exception: Exception) {
|
||||
SimpleResult.Failure(exception)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendTransaction(transaction: String): SimpleResult {
|
||||
return try {
|
||||
retryIO { api.sendTransaction(transaction) }
|
||||
SimpleResult.Success
|
||||
} catch (exception: Exception) {
|
||||
SimpleResult.Failure(exception)
|
||||
}
|
||||
}
|
||||
|
||||
private fun encodeSignature(signature: ByteArray): String {
|
||||
val edsigPrefix = "09F5CD8612".hexToBytes()
|
||||
val prefixedSignature = edsigPrefix + signature
|
||||
val checksum = prefixedSignature.calculateTezosChecksum()
|
||||
val prefixedSignatureWithChecksum = prefixedSignature + checksum
|
||||
|
||||
return Base58.encode(prefixedSignatureWithChecksum)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.blockchain.blockchains.tezos.network
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TezosAddressResponse(
|
||||
@Json(name = "balance")
|
||||
var balance: Long? = null,
|
||||
|
||||
@Json(name = "counter")
|
||||
var counter: Long? = null
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TezosHeaderResponse(
|
||||
@Json(name = "protocol")
|
||||
var protocol: String? = null,
|
||||
|
||||
@Json(name = "hash")
|
||||
var hash: String? = null
|
||||
)
|
||||
|
|
@ -6,6 +6,7 @@ import com.tangem.blockchain.blockchains.bitcoincash.BitcoinCashAddressService
|
|||
import com.tangem.blockchain.blockchains.cardano.CardanoAddressService
|
||||
import com.tangem.blockchain.blockchains.ethereum.EthereumAddressService
|
||||
import com.tangem.blockchain.blockchains.stellar.StellarAddressService
|
||||
import com.tangem.blockchain.blockchains.tezos.TezosAddressService
|
||||
import com.tangem.blockchain.blockchains.xrp.XrpAddressService
|
||||
|
||||
enum class Blockchain(
|
||||
|
|
@ -24,11 +25,12 @@ enum class Blockchain(
|
|||
XRP("XRP", "XRP", "XRP Ledger"),
|
||||
Binance("BINANCE", "BNB", "Binance"),
|
||||
BinanceTestnet("BINANCE/test", "BNBt", "Binance"),
|
||||
Stellar("XLM", "XLM", "Stellar");
|
||||
Stellar("XLM", "XLM", "Stellar"),
|
||||
Tezos("TEZOS", "XTZ", "Tezos");
|
||||
|
||||
fun decimals(): Int = when (this) {
|
||||
Bitcoin, BitcoinTestnet, BitcoinCash, Binance, BinanceTestnet, Litecoin -> 8
|
||||
Cardano, XRP -> 6
|
||||
Cardano, XRP, Tezos -> 6
|
||||
Ethereum, RSK -> 18
|
||||
Stellar -> 7
|
||||
Unknown -> 0
|
||||
|
|
@ -52,6 +54,7 @@ enum class Blockchain(
|
|||
Binance -> BinanceAddressService()
|
||||
BinanceTestnet -> BinanceAddressService(true)
|
||||
Stellar -> StellarAddressService()
|
||||
Tezos -> TezosAddressService()
|
||||
}
|
||||
|
||||
fun getShareUri(address: String): String = when (this) {
|
||||
|
|
@ -63,6 +66,7 @@ enum class Blockchain(
|
|||
|
||||
fun getExploreUrl(address: String, token: Token? = null): String = when (this) {
|
||||
Binance -> "https://explorer.binance.org/address/$address"
|
||||
BinanceTestnet -> "https://testnet-explorer.binance.org/address/$address"
|
||||
Bitcoin -> "https://blockchain.info/address/$address"
|
||||
BitcoinTestnet -> "https://live.blockcypher.com/btc-testnet/address/$address"
|
||||
BitcoinCash -> "https://blockchair.com/bitcoin-cash/address/$address"
|
||||
|
|
@ -82,7 +86,8 @@ enum class Blockchain(
|
|||
}
|
||||
Stellar -> "https://stellar.expert/explorer/public/account/$address"
|
||||
XRP -> "https://xrpscan.com/account/$address"
|
||||
else -> throw Exception("Explore URL not defined!")
|
||||
Tezos -> "https://tezblock.io/account/$address"
|
||||
Unknown -> throw Exception("unsupported blockchain")
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ import com.tangem.blockchain.blockchains.stellar.StellarNetworkManager
|
|||
import com.tangem.blockchain.blockchains.stellar.StellarTransactionBuilder
|
||||
|
||||
import com.tangem.blockchain.blockchains.stellar.StellarWalletManager
|
||||
import com.tangem.blockchain.blockchains.tezos.TezosTransactionBuilder
|
||||
import com.tangem.blockchain.blockchains.tezos.TezosWalletManager
|
||||
import com.tangem.blockchain.blockchains.tezos.network.TezosNetworkManager
|
||||
import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder
|
||||
import com.tangem.blockchain.blockchains.xrp.XrpWalletManager
|
||||
import com.tangem.blockchain.blockchains.xrp.network.XrpNetworkManager
|
||||
|
|
@ -113,7 +116,14 @@ object WalletManagerFactory {
|
|||
BinanceNetworkManager(true)
|
||||
)
|
||||
}
|
||||
else -> return null
|
||||
Blockchain.Tezos -> {
|
||||
return TezosWalletManager(
|
||||
cardId, wallet,
|
||||
TezosTransactionBuilder(walletPublicKey),
|
||||
TezosNetworkManager()
|
||||
)
|
||||
}
|
||||
Blockchain.Unknown -> throw Exception("unsupported blockchain")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import com.tangem.blockchain.common.Amount
|
|||
import java.math.BigInteger
|
||||
|
||||
fun Amount.bigIntegerValue(): BigInteger? {
|
||||
return this.value?.movePointRight(this.decimals.toInt())?.toBigInteger()
|
||||
return this.value?.movePointRight(this.decimals)?.toBigInteger()
|
||||
}
|
||||
|
||||
fun Amount.isAboveZero(): Boolean {
|
||||
|
|
|
|||
|
|
@ -53,4 +53,6 @@ const val API_ADALITE = "https://explorer3.adalite.io/"
|
|||
const val API_ADALITE_RESERVE = "https://nodes.southeastasia.cloudapp.azure.com/"
|
||||
const val API_RIPPLED = "https://s1.ripple.com:51234/"
|
||||
const val API_RIPPLED_RESERVE = "https://s2.ripple.com:51234/"
|
||||
const val API_BLOCKCHAIR = "https://api.blockchair.com/"
|
||||
const val API_BLOCKCHAIR = "https://api.blockchair.com/"
|
||||
const val API_TEZOS = "https://teznode.letzbake.com"
|
||||
const val API_TEZOS_RESERVE = "https://mainnet.tezrpc.me"
|
||||
Loading…
Add table
Add a link
Reference in a new issue