Updated on 2026-08-14
This commit is contained in:
commit
0f4f44a8c6
13 changed files with 583 additions and 14 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.cardano.CardanoAddressService
|
||||||
import com.tangem.blockchain.blockchains.ethereum.EthereumAddressService
|
import com.tangem.blockchain.blockchains.ethereum.EthereumAddressService
|
||||||
import com.tangem.blockchain.blockchains.stellar.StellarAddressService
|
import com.tangem.blockchain.blockchains.stellar.StellarAddressService
|
||||||
|
import com.tangem.blockchain.blockchains.tezos.TezosAddressService
|
||||||
import com.tangem.blockchain.blockchains.xrp.XrpAddressService
|
import com.tangem.blockchain.blockchains.xrp.XrpAddressService
|
||||||
|
|
||||||
enum class Blockchain(
|
enum class Blockchain(
|
||||||
|
|
@ -24,11 +25,12 @@ enum class Blockchain(
|
||||||
XRP("XRP", "XRP", "XRP Ledger"),
|
XRP("XRP", "XRP", "XRP Ledger"),
|
||||||
Binance("BINANCE", "BNB", "Binance"),
|
Binance("BINANCE", "BNB", "Binance"),
|
||||||
BinanceTestnet("BINANCE/test", "BNBt", "Binance"),
|
BinanceTestnet("BINANCE/test", "BNBt", "Binance"),
|
||||||
Stellar("XLM", "XLM", "Stellar");
|
Stellar("XLM", "XLM", "Stellar"),
|
||||||
|
Tezos("TEZOS", "XTZ", "Tezos");
|
||||||
|
|
||||||
fun decimals(): Int = when (this) {
|
fun decimals(): Int = when (this) {
|
||||||
Bitcoin, BitcoinTestnet, BitcoinCash, Binance, BinanceTestnet, Litecoin -> 8
|
Bitcoin, BitcoinTestnet, BitcoinCash, Binance, BinanceTestnet, Litecoin -> 8
|
||||||
Cardano, XRP -> 6
|
Cardano, XRP, Tezos -> 6
|
||||||
Ethereum, RSK -> 18
|
Ethereum, RSK -> 18
|
||||||
Stellar -> 7
|
Stellar -> 7
|
||||||
Unknown -> 0
|
Unknown -> 0
|
||||||
|
|
@ -52,6 +54,7 @@ enum class Blockchain(
|
||||||
Binance -> BinanceAddressService()
|
Binance -> BinanceAddressService()
|
||||||
BinanceTestnet -> BinanceAddressService(true)
|
BinanceTestnet -> BinanceAddressService(true)
|
||||||
Stellar -> StellarAddressService()
|
Stellar -> StellarAddressService()
|
||||||
|
Tezos -> TezosAddressService()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getShareUri(address: String): String = when (this) {
|
fun getShareUri(address: String): String = when (this) {
|
||||||
|
|
@ -63,6 +66,7 @@ enum class Blockchain(
|
||||||
|
|
||||||
fun getExploreUrl(address: String, token: Token? = null): String = when (this) {
|
fun getExploreUrl(address: String, token: Token? = null): String = when (this) {
|
||||||
Binance -> "https://explorer.binance.org/address/$address"
|
Binance -> "https://explorer.binance.org/address/$address"
|
||||||
|
BinanceTestnet -> "https://testnet-explorer.binance.org/address/$address"
|
||||||
Bitcoin -> "https://blockchain.info/address/$address"
|
Bitcoin -> "https://blockchain.info/address/$address"
|
||||||
BitcoinTestnet -> "https://live.blockcypher.com/btc-testnet/address/$address"
|
BitcoinTestnet -> "https://live.blockcypher.com/btc-testnet/address/$address"
|
||||||
BitcoinCash -> "https://blockchair.com/bitcoin-cash/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"
|
Stellar -> "https://stellar.expert/explorer/public/account/$address"
|
||||||
XRP -> "https://xrpscan.com/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 {
|
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.StellarTransactionBuilder
|
||||||
|
|
||||||
import com.tangem.blockchain.blockchains.stellar.StellarWalletManager
|
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.XrpTransactionBuilder
|
||||||
import com.tangem.blockchain.blockchains.xrp.XrpWalletManager
|
import com.tangem.blockchain.blockchains.xrp.XrpWalletManager
|
||||||
import com.tangem.blockchain.blockchains.xrp.network.XrpNetworkManager
|
import com.tangem.blockchain.blockchains.xrp.network.XrpNetworkManager
|
||||||
|
|
@ -113,7 +116,14 @@ object WalletManagerFactory {
|
||||||
BinanceNetworkManager(true)
|
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
|
import java.math.BigInteger
|
||||||
|
|
||||||
fun Amount.bigIntegerValue(): BigInteger? {
|
fun Amount.bigIntegerValue(): BigInteger? {
|
||||||
return this.value?.movePointRight(this.decimals.toInt())?.toBigInteger()
|
return this.value?.movePointRight(this.decimals)?.toBigInteger()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Amount.isAboveZero(): Boolean {
|
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_ADALITE_RESERVE = "https://nodes.southeastasia.cloudapp.azure.com/"
|
||||||
const val API_RIPPLED = "https://s1.ripple.com:51234/"
|
const val API_RIPPLED = "https://s1.ripple.com:51234/"
|
||||||
const val API_RIPPLED_RESERVE = "https://s2.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"
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.tangem.blockchain.blockchains.tezos
|
||||||
|
|
||||||
|
import com.google.common.truth.Truth
|
||||||
|
import com.tangem.common.extensions.hexToBytes
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class TezosAddressTest {
|
||||||
|
|
||||||
|
private val addressService = TezosAddressService()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun makeAddressFromCorrectPublicKey() {
|
||||||
|
val walletPublicKey = "98E0E504F3A5FDE704400302ABB0A2EFB0DF0F95C166C91D7F207DEDCE10CBA3".hexToBytes()
|
||||||
|
val expected = "tz1hhRdWDAvGsgEioZ9GAp4bUVQkd9ng2MMR"
|
||||||
|
|
||||||
|
Truth.assertThat(addressService.makeAddress(walletPublicKey))
|
||||||
|
.isEqualTo(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun validateCorrectAddress() {
|
||||||
|
val address = "tz1hhRdWDAvGsgEioZ9GAp4bUVQkd9ng2MMR"
|
||||||
|
Truth.assertThat(addressService.validate(address))
|
||||||
|
.isTrue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ import com.tangem.blockchain.blockchains.cardano.CardanoWalletManager
|
||||||
import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager
|
import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager
|
||||||
import com.tangem.blockchain.blockchains.litecoin.LitecoinWalletManager
|
import com.tangem.blockchain.blockchains.litecoin.LitecoinWalletManager
|
||||||
import com.tangem.blockchain.blockchains.stellar.StellarWalletManager
|
import com.tangem.blockchain.blockchains.stellar.StellarWalletManager
|
||||||
|
import com.tangem.blockchain.blockchains.tezos.TezosWalletManager
|
||||||
import com.tangem.blockchain.blockchains.xrp.XrpWalletManager
|
import com.tangem.blockchain.blockchains.xrp.XrpWalletManager
|
||||||
import com.tangem.commands.ReadCommand
|
import com.tangem.commands.ReadCommand
|
||||||
import com.tangem.common.apdu.ResponseApdu
|
import com.tangem.common.apdu.ResponseApdu
|
||||||
|
|
@ -22,7 +23,7 @@ internal class WalletManagerFactoryTest {
|
||||||
val data = "0108bb00000000000304200754414e47454d00020102800a322e3432642053444b000341040876bdec26b89bd2159a668b9af3d9fe86370f318717c92b8d6c1186fb3648c32a5f9321998cc2d042901c91d40601e79a641e1cbcebe7a2358be6054e1b6e5d0a04041e76310c618102ffff8a0101820407e30b0d830b54414e47454d2053444b0084034254438640e17ceec48c5be36240c98019f95ad8b6e56acfebe60d11979c6279f715d607d76a860a137da8d109e805753f3f56b0130709f4bbf4cb9974b4c57b8469bf4b873041045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd26050a736563703235366b310008040000006407010009020bb8604104752a727e14bba5bd73b6714d72500f61ffd11026ad1196d2e1c54577cbeeac3d11fc68a64700f8d533f4e311964ea8fb3aa26c588295f2133868d69c3e62869362040000005c6304000000090f01009000"
|
val data = "0108bb00000000000304200754414e47454d00020102800a322e3432642053444b000341040876bdec26b89bd2159a668b9af3d9fe86370f318717c92b8d6c1186fb3648c32a5f9321998cc2d042901c91d40601e79a641e1cbcebe7a2358be6054e1b6e5d0a04041e76310c618102ffff8a0101820407e30b0d830b54414e47454d2053444b0084034254438640e17ceec48c5be36240c98019f95ad8b6e56acfebe60d11979c6279f715d607d76a860a137da8d109e805753f3f56b0130709f4bbf4cb9974b4c57b8469bf4b873041045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd26050a736563703235366b310008040000006407010009020bb8604104752a727e14bba5bd73b6714d72500f61ffd11026ad1196d2e1c54577cbeeac3d11fc68a64700f8d533f4e311964ea8fb3aa26c588295f2133868d69c3e62869362040000005c6304000000090f01009000"
|
||||||
val responseApdu = ResponseApdu(data.hexToBytes())
|
val responseApdu = ResponseApdu(data.hexToBytes())
|
||||||
val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu)
|
val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu)
|
||||||
val walletManager = WalletManagerFactory.makeWalletManager(card!!)
|
val walletManager = WalletManagerFactory.makeWalletManager(card)
|
||||||
|
|
||||||
Truth.assertThat(walletManager)
|
Truth.assertThat(walletManager)
|
||||||
.isInstanceOf(BitcoinWalletManager::class.java)
|
.isInstanceOf(BitcoinWalletManager::class.java)
|
||||||
|
|
@ -33,7 +34,7 @@ internal class WalletManagerFactoryTest {
|
||||||
val data = "0108bb00000000000536200754414e47454d00020102800a322e3432642053444b000341046c8aea0d5a850b0a608acf9a0c453c39ea86131e88bfa78800de3cfb5bf1007aeaa7b9ffc184212255758605c2461be343c0a661d73cabafa4c9c175b3f0e59a0a04041e76310c618102ffff8a0101820407e30b0d830b54414e47454d2053444b0084034554488640431b6244acfeac479becdff201a7f720a7d70a97edc4e019fb678596baf52dfe9d0e8faf08ceb4443b82d4e66815541f2dc8ec6dd3ff83eb42f06e5eab07f25f3041045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd26050a736563703235366b3100080400000064070100090205dc60410464dddc3f356744aaecfa07427f9eb996ff537d65f20fb5be3abccf0354352a6b5f8a1942e0f8ddeea3a170eda78d060be8162ad60e94e4e91fbbdf0a7054785562040000005b6304000000090f01009000"
|
val data = "0108bb00000000000536200754414e47454d00020102800a322e3432642053444b000341046c8aea0d5a850b0a608acf9a0c453c39ea86131e88bfa78800de3cfb5bf1007aeaa7b9ffc184212255758605c2461be343c0a661d73cabafa4c9c175b3f0e59a0a04041e76310c618102ffff8a0101820407e30b0d830b54414e47454d2053444b0084034554488640431b6244acfeac479becdff201a7f720a7d70a97edc4e019fb678596baf52dfe9d0e8faf08ceb4443b82d4e66815541f2dc8ec6dd3ff83eb42f06e5eab07f25f3041045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd26050a736563703235366b3100080400000064070100090205dc60410464dddc3f356744aaecfa07427f9eb996ff537d65f20fb5be3abccf0354352a6b5f8a1942e0f8ddeea3a170eda78d060be8162ad60e94e4e91fbbdf0a7054785562040000005b6304000000090f01009000"
|
||||||
val responseApdu = ResponseApdu(data.hexToBytes())
|
val responseApdu = ResponseApdu(data.hexToBytes())
|
||||||
val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu)
|
val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu)
|
||||||
val walletManager = WalletManagerFactory.makeWalletManager(card!!)
|
val walletManager = WalletManagerFactory.makeWalletManager(card)
|
||||||
|
|
||||||
Truth.assertThat(walletManager)
|
Truth.assertThat(walletManager)
|
||||||
.isInstanceOf(EthereumWalletManager::class.java)
|
.isInstanceOf(EthereumWalletManager::class.java)
|
||||||
|
|
@ -44,7 +45,7 @@ internal class WalletManagerFactoryTest {
|
||||||
val data = "0108bb00000000000379200754414e47454d00020102800a322e3432642053444b0003410487d7bb51b189213e3cedc3fcfa3fc047b3b71b7805b5b215e14639b3a8ebb1952c9dd5ea4354441b6ada4e8b8327674bb102ddae69df55be69643a2c916edf650a04041e76310c618102ffff8a0101820407e30b0d830b54414e47454d2053444b008403584c4d86409a4bc2baf0e5836887da21167cf33458d5249d1a610bced0e31dc053f23729ed24d715912bf89e6804669430dfe396ed83274e0031f6803e2bdb8c041fa993413041045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd2605086564323535313900080400000064070100090205dc6020e078212d58b2b9d0edc9c936830d10081cd38b90c31778c56dfb1171027e294e62040000003863040000002c0f01009000"
|
val data = "0108bb00000000000379200754414e47454d00020102800a322e3432642053444b0003410487d7bb51b189213e3cedc3fcfa3fc047b3b71b7805b5b215e14639b3a8ebb1952c9dd5ea4354441b6ada4e8b8327674bb102ddae69df55be69643a2c916edf650a04041e76310c618102ffff8a0101820407e30b0d830b54414e47454d2053444b008403584c4d86409a4bc2baf0e5836887da21167cf33458d5249d1a610bced0e31dc053f23729ed24d715912bf89e6804669430dfe396ed83274e0031f6803e2bdb8c041fa993413041045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd2605086564323535313900080400000064070100090205dc6020e078212d58b2b9d0edc9c936830d10081cd38b90c31778c56dfb1171027e294e62040000003863040000002c0f01009000"
|
||||||
val responseApdu = ResponseApdu(data.hexToBytes())
|
val responseApdu = ResponseApdu(data.hexToBytes())
|
||||||
val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu)
|
val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu)
|
||||||
val walletManager = WalletManagerFactory.makeWalletManager(card!!)
|
val walletManager = WalletManagerFactory.makeWalletManager(card)
|
||||||
|
|
||||||
Truth.assertThat(walletManager)
|
Truth.assertThat(walletManager)
|
||||||
.isInstanceOf(StellarWalletManager::class.java)
|
.isInstanceOf(StellarWalletManager::class.java)
|
||||||
|
|
@ -55,7 +56,7 @@ internal class WalletManagerFactoryTest {
|
||||||
val data = "0108bb00000000000502200754414e47454d00020102800a322e3432642053444b0003410402c1e39257d60583489da2d67d35d1cc2a1c005cc05c1021f44838edcaf25d5615cad7c9d11c2e23f5efa93e50904d33c88808d0e169060508df840992e31f4d0a04041e76310c658102ffff8a0101820407e30b0d830b54414e47454d2053444b00840743415244414e4f8640f24ef5c8c6eba0ff97560d5b013edb4a452594270db9647bd0a3543df8104dec75731d4db3ebe0fc493f2afee00195e560b51e3c41189b7c61ba7895d6434b9d3041045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd2605086564323535313900080400000064070100090205dc60208a71161cfdf1e0a85d8e7ff372aa4a01136046292aceb5f9ad7ebdb98d3f60a86204000000646304000000000f01009000"
|
val data = "0108bb00000000000502200754414e47454d00020102800a322e3432642053444b0003410402c1e39257d60583489da2d67d35d1cc2a1c005cc05c1021f44838edcaf25d5615cad7c9d11c2e23f5efa93e50904d33c88808d0e169060508df840992e31f4d0a04041e76310c658102ffff8a0101820407e30b0d830b54414e47454d2053444b00840743415244414e4f8640f24ef5c8c6eba0ff97560d5b013edb4a452594270db9647bd0a3543df8104dec75731d4db3ebe0fc493f2afee00195e560b51e3c41189b7c61ba7895d6434b9d3041045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd2605086564323535313900080400000064070100090205dc60208a71161cfdf1e0a85d8e7ff372aa4a01136046292aceb5f9ad7ebdb98d3f60a86204000000646304000000000f01009000"
|
||||||
val responseApdu = ResponseApdu(data.hexToBytes())
|
val responseApdu = ResponseApdu(data.hexToBytes())
|
||||||
val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu)
|
val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu)
|
||||||
val walletManager = WalletManagerFactory.makeWalletManager(card!!)
|
val walletManager = WalletManagerFactory.makeWalletManager(card)
|
||||||
|
|
||||||
Truth.assertThat(walletManager)
|
Truth.assertThat(walletManager)
|
||||||
.isInstanceOf(CardanoWalletManager::class.java)
|
.isInstanceOf(CardanoWalletManager::class.java)
|
||||||
|
|
@ -66,7 +67,7 @@ internal class WalletManagerFactoryTest {
|
||||||
val data = "0108cb21000000002154200b534d4152542043415348000201028006322e31317200034104bdad63848f97c535da53cf8fd300d24fa33f0516d194aa78ec164a06994d00204bae243a424e316c6ec845e02d9b15eafae8c19018a926b0b7435e6e941cdadb0a0400007e210c5a81020028820407e30502830754414e47454d00840358525086400ed8734b877869722c7d0b37ffb154b9fef21c54bf2c6496feb1fb5c1fc28a2ac28e201dde84f27495fa7f08b3ca2be2fb4954bf0fe78af027d6cdc16c3eee923041048196aa4b410ac44a3b9cce18e7be226aea070acc83a9cf67540fac49af25129f6a538a28ad6341358e3c4f9963064f7e365372a651d374e5c23cdd37fd099bf2050a736563703235366b31000804000f4240070100090205dc604104d2b9fb288540d54e5b32ecaf0381cd571f97f6f1ecd036b66bb11aa52ffe9981110d883080e2e255c6b1640586f7765e6faa325d1340f49b56b83d9de56bc7ed6204000f42406304000000000f01009000"
|
val data = "0108cb21000000002154200b534d4152542043415348000201028006322e31317200034104bdad63848f97c535da53cf8fd300d24fa33f0516d194aa78ec164a06994d00204bae243a424e316c6ec845e02d9b15eafae8c19018a926b0b7435e6e941cdadb0a0400007e210c5a81020028820407e30502830754414e47454d00840358525086400ed8734b877869722c7d0b37ffb154b9fef21c54bf2c6496feb1fb5c1fc28a2ac28e201dde84f27495fa7f08b3ca2be2fb4954bf0fe78af027d6cdc16c3eee923041048196aa4b410ac44a3b9cce18e7be226aea070acc83a9cf67540fac49af25129f6a538a28ad6341358e3c4f9963064f7e365372a651d374e5c23cdd37fd099bf2050a736563703235366b31000804000f4240070100090205dc604104d2b9fb288540d54e5b32ecaf0381cd571f97f6f1ecd036b66bb11aa52ffe9981110d883080e2e255c6b1640586f7765e6faa325d1340f49b56b83d9de56bc7ed6204000f42406304000000000f01009000"
|
||||||
val responseApdu = ResponseApdu(data.hexToBytes())
|
val responseApdu = ResponseApdu(data.hexToBytes())
|
||||||
val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu)
|
val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu)
|
||||||
val walletManager = WalletManagerFactory.makeWalletManager(card!!)
|
val walletManager = WalletManagerFactory.makeWalletManager(card)
|
||||||
|
|
||||||
Truth.assertThat(walletManager)
|
Truth.assertThat(walletManager)
|
||||||
.isInstanceOf(XrpWalletManager::class.java)
|
.isInstanceOf(XrpWalletManager::class.java)
|
||||||
|
|
@ -77,7 +78,7 @@ internal class WalletManagerFactoryTest {
|
||||||
val data = "0108BB00000000000015200754414E47454D00020102800A322E3432642053444B0003410446D4155890B08BE217F0B1FA7DCCB16138C24B3E825A27315D5E4BBD6CAF76A28C7902007052BC1347355A78D54BD73216C9431D555CED827B54FD9255EB3A830A04041E76310C658102FFFF8A0101820407E40410830B54414E47454D2053444B00840742494E414E4345864029F115878EDC7B0CB2A6F4A4009447DCB43BBE922D7629AEBD0C9A910AD1E3BF15AE409C4F579700951ED2FE4D775171A86CFA8E50009A05938CE210D6D4A2583041045F16BD1D2EAFE463E62A335A09E6B2BBCBD04452526885CB679FC4D27AF1BD22F553C7DEEFB54FD3D4F361D14E6DC3F11B7D4EA183250A60720EBDF9E110CD26050A736563703235366B31000804000186A0070100604104E3F3BE3CE3D8284DB3BA073AD0291040093D83C11A277B905D5555C9EC41073E103F4D9D299EDEA8285C51C3356A8681A545618C174251B984DF841F49D2376F62040001869F6304000000010F01009000"
|
val data = "0108BB00000000000015200754414E47454D00020102800A322E3432642053444B0003410446D4155890B08BE217F0B1FA7DCCB16138C24B3E825A27315D5E4BBD6CAF76A28C7902007052BC1347355A78D54BD73216C9431D555CED827B54FD9255EB3A830A04041E76310C658102FFFF8A0101820407E40410830B54414E47454D2053444B00840742494E414E4345864029F115878EDC7B0CB2A6F4A4009447DCB43BBE922D7629AEBD0C9A910AD1E3BF15AE409C4F579700951ED2FE4D775171A86CFA8E50009A05938CE210D6D4A2583041045F16BD1D2EAFE463E62A335A09E6B2BBCBD04452526885CB679FC4D27AF1BD22F553C7DEEFB54FD3D4F361D14E6DC3F11B7D4EA183250A60720EBDF9E110CD26050A736563703235366B31000804000186A0070100604104E3F3BE3CE3D8284DB3BA073AD0291040093D83C11A277B905D5555C9EC41073E103F4D9D299EDEA8285C51C3356A8681A545618C174251B984DF841F49D2376F62040001869F6304000000010F01009000"
|
||||||
val responseApdu = ResponseApdu(data.hexToBytes())
|
val responseApdu = ResponseApdu(data.hexToBytes())
|
||||||
val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu)
|
val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu)
|
||||||
val walletManager = WalletManagerFactory.makeWalletManager(card!!)
|
val walletManager = WalletManagerFactory.makeWalletManager(card)
|
||||||
|
|
||||||
Truth.assertThat(walletManager)
|
Truth.assertThat(walletManager)
|
||||||
.isInstanceOf(BinanceWalletManager::class.java)
|
.isInstanceOf(BinanceWalletManager::class.java)
|
||||||
|
|
@ -88,7 +89,7 @@ internal class WalletManagerFactoryTest {
|
||||||
val data = "0108BB00000000000049200754414E47454D00020102800A322E3432642053444B00034104766A1586D164B436E5D420AED01FDAB41B2AE7EDF0C865D7AF1DA995D70AB297E5B94B761CFBB405084C21BC97C02B4A1EA9ED4F515576EAB4D83AD3A0DFAA8A0A04041E76310C618102FFFF8A0101820407E4041B830B54414E47454D2053444B00840342434886408058F0F628C2466B09ECEB13F2A8EFDD4558F5D2DBDA9BD0628EE8C8CC99A778FF0F1AECD35704B9F3518486EA5C1D20F9DFCBAA66184F4CCCD9282E2632882C3041045F16BD1D2EAFE463E62A335A09E6B2BBCBD04452526885CB679FC4D27AF1BD22F553C7DEEFB54FD3D4F361D14E6DC3F11B7D4EA183250A60720EBDF9E110CD26050A736563703235366B31000804000186A0070100604104BE37CD5251C8999EDBBFC759D800EB41E4DCB718289601EB15819404E1B2F2ED90FE50C2A481D06EC790D1EF6184974EB655ABAE4BE56A6D1C9E1A17B1EFDF0262040001869A6304000000060F01009000"
|
val data = "0108BB00000000000049200754414E47454D00020102800A322E3432642053444B00034104766A1586D164B436E5D420AED01FDAB41B2AE7EDF0C865D7AF1DA995D70AB297E5B94B761CFBB405084C21BC97C02B4A1EA9ED4F515576EAB4D83AD3A0DFAA8A0A04041E76310C618102FFFF8A0101820407E4041B830B54414E47454D2053444B00840342434886408058F0F628C2466B09ECEB13F2A8EFDD4558F5D2DBDA9BD0628EE8C8CC99A778FF0F1AECD35704B9F3518486EA5C1D20F9DFCBAA66184F4CCCD9282E2632882C3041045F16BD1D2EAFE463E62A335A09E6B2BBCBD04452526885CB679FC4D27AF1BD22F553C7DEEFB54FD3D4F361D14E6DC3F11B7D4EA183250A60720EBDF9E110CD26050A736563703235366B31000804000186A0070100604104BE37CD5251C8999EDBBFC759D800EB41E4DCB718289601EB15819404E1B2F2ED90FE50C2A481D06EC790D1EF6184974EB655ABAE4BE56A6D1C9E1A17B1EFDF0262040001869A6304000000060F01009000"
|
||||||
val responseApdu = ResponseApdu(data.hexToBytes())
|
val responseApdu = ResponseApdu(data.hexToBytes())
|
||||||
val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu)
|
val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu)
|
||||||
val walletManager = WalletManagerFactory.makeWalletManager(card!!)
|
val walletManager = WalletManagerFactory.makeWalletManager(card)
|
||||||
|
|
||||||
Truth.assertThat(walletManager)
|
Truth.assertThat(walletManager)
|
||||||
.isInstanceOf(BitcoinCashWalletManager::class.java)
|
.isInstanceOf(BitcoinCashWalletManager::class.java)
|
||||||
|
|
@ -99,9 +100,20 @@ internal class WalletManagerFactoryTest {
|
||||||
val data = "0108BB00000000000023200754414E47454D00020102800A322E3432642053444B000341043539F86A40ADD04CE165764A761FD3E4D251028615D2A573B1C3AE652E60AFDBFAF02E3239E89EF2C43FA448A327557ADC5AF36376A0574570F6DBD20113514A0A04041E76310C618102FFFF8A0101820407E40414830B54414E47454D2053444B0084034C5443864004BDEAD0117544886346CB47F7CA84ABA8C34239502F23D28595A4B16CAD72F7DE506BA818B86A649C2BB945986D4574993B3B755B47CBEE31C4FB931F6748183041045F16BD1D2EAFE463E62A335A09E6B2BBCBD04452526885CB679FC4D27AF1BD22F553C7DEEFB54FD3D4F361D14E6DC3F11B7D4EA183250A60720EBDF9E110CD26050A736563703235366B31000804000186A00701006041044A76C9A70422160F515F956D0F50C71BBBA4F9862A22913817D63F0B1EF7C2FAF512E1C91B1BE827560EFE24FB1652B47337E296C778DFB1014D080CDD35EF6562040001869D6304000000030F01009000"
|
val data = "0108BB00000000000023200754414E47454D00020102800A322E3432642053444B000341043539F86A40ADD04CE165764A761FD3E4D251028615D2A573B1C3AE652E60AFDBFAF02E3239E89EF2C43FA448A327557ADC5AF36376A0574570F6DBD20113514A0A04041E76310C618102FFFF8A0101820407E40414830B54414E47454D2053444B0084034C5443864004BDEAD0117544886346CB47F7CA84ABA8C34239502F23D28595A4B16CAD72F7DE506BA818B86A649C2BB945986D4574993B3B755B47CBEE31C4FB931F6748183041045F16BD1D2EAFE463E62A335A09E6B2BBCBD04452526885CB679FC4D27AF1BD22F553C7DEEFB54FD3D4F361D14E6DC3F11B7D4EA183250A60720EBDF9E110CD26050A736563703235366B31000804000186A00701006041044A76C9A70422160F515F956D0F50C71BBBA4F9862A22913817D63F0B1EF7C2FAF512E1C91B1BE827560EFE24FB1652B47337E296C778DFB1014D080CDD35EF6562040001869D6304000000030F01009000"
|
||||||
val responseApdu = ResponseApdu(data.hexToBytes())
|
val responseApdu = ResponseApdu(data.hexToBytes())
|
||||||
val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu)
|
val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu)
|
||||||
val walletManager = WalletManagerFactory.makeWalletManager(card!!)
|
val walletManager = WalletManagerFactory.makeWalletManager(card)
|
||||||
|
|
||||||
Truth.assertThat(walletManager)
|
Truth.assertThat(walletManager)
|
||||||
.isInstanceOf(LitecoinWalletManager::class.java)
|
.isInstanceOf(LitecoinWalletManager::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createLTezosWalletManager() {
|
||||||
|
val data = "0108BB00000000000080200754414E47454D00020102800A322E3432642053444B0003410436CFC5D0A11353AE6AFEEDC84A2D02B2635C044DEEE47F99913072B8D166D14E557230AC5FB5272F1A0E523332CCE1A744B51DB53102FF7D3FDE023DC3477C460A04041E76310C638102FFFF8A0101820407E40514830B54414E47454D2053444B00840554455A4F538640C752685B29333CFB0DB0A7347579A0AE763F2B5C4BB09FD68E0B81A06CD01EC51347001732815A3ECFFCD78DDE4E53877581B9E4914B069570629D0C40A771B93041045F16BD1D2EAFE463E62A335A09E6B2BBCBD04452526885CB679FC4D27AF1BD22F553C7DEEFB54FD3D4F361D14E6DC3F11B7D4EA183250A60720EBDF9E110CD26050865643235353139000804000186A0070100602098E0E504F3A5FDE704400302ABB0A2EFB0DF0F95C166C91D7F207DEDCE10CBA362040001869F6304000000010F01009000"
|
||||||
|
val responseApdu = ResponseApdu(data.hexToBytes())
|
||||||
|
val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu)
|
||||||
|
val walletManager = WalletManagerFactory.makeWalletManager(card)
|
||||||
|
|
||||||
|
Truth.assertThat(walletManager)
|
||||||
|
.isInstanceOf(TezosWalletManager::class.java)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue