Skip to content

Commit

Permalink
Merge pull request #852 from tangem/feature/AND-8828_clore_ai_support
Browse files Browse the repository at this point in the history
AND-8828 CloreAI blockchain support
  • Loading branch information
nemelianov-tangem authored Dec 6, 2024
2 parents 24e1329 + 71e0d91 commit 15f0337
Show file tree
Hide file tree
Showing 19 changed files with 192 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.tangem.blockchain.blockchains.bitcoin

import com.tangem.blockchain.blockchains.clore.CloreMainNetParams
import com.tangem.blockchain.blockchains.dash.DashMainNetParams
import com.tangem.blockchain.blockchains.ducatus.DucatusMainNetParams
import com.tangem.blockchain.blockchains.radiant.RadiantMainNetParams
Expand Down Expand Up @@ -39,6 +40,7 @@ open class BitcoinAddressService(
Blockchain.Ravencoin -> RavencoinMainNetParams()
Blockchain.RavencoinTestnet -> RavencoinTestNetParams()
Blockchain.Radiant -> RadiantMainNetParams()
Blockchain.Clore -> CloreMainNetParams()
else -> error(
"${blockchain.fullName} blockchain is not supported by ${this::class.simpleName}",
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.tangem.blockchain.blockchains.bitcoin

import com.tangem.blockchain.blockchains.clore.CloreMainNetParams
import com.tangem.blockchain.blockchains.dash.DashMainNetParams
import com.tangem.blockchain.blockchains.ducatus.DucatusMainNetParams
import com.tangem.blockchain.blockchains.ravencoin.RavencoinMainNetParams
Expand Down Expand Up @@ -44,6 +45,7 @@ open class BitcoinTransactionBuilder(
Blockchain.Dash -> DashMainNetParams()
Blockchain.Ravencoin -> RavencoinMainNetParams()
Blockchain.RavencoinTestnet -> RavencoinTestNetParams()
Blockchain.Clore -> CloreMainNetParams()
else -> error("${blockchain.fullName} blockchain is not supported by ${this::class.simpleName}")
}
var unspentOutputs: List<BitcoinUnspentOutput>? = null
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.tangem.blockchain.blockchains.clore

import org.bitcoinj.core.Block
import org.bitcoinj.core.Utils
import org.bitcoinj.params.AbstractBitcoinNetParams

@Suppress("MagicNumber")
internal class CloreMainNetParams : AbstractBitcoinNetParams() {
init {
interval = INTERVAL
targetTimespan = TARGET_TIMESPAN

// 00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
maxTarget = Utils.decodeCompactBits(0x1e0fffffL)
addressHeader = 23
p2shHeader = 122
port = 8788
bip32HeaderP2PKHpub = 0x0488b21e // The 4 byte header that serializes in base58 to "xpub".
bip32HeaderP2PKHpriv = 0x0488ade4 // The 4 byte header that serializes in base58 to "xprv"
majorityEnforceBlockUpgrade = MAINNET_MAJORITY_ENFORCE_BLOCK_UPGRADE
majorityRejectBlockOutdated = MAINNET_MAJORITY_REJECT_BLOCK_OUTDATED
majorityWindow = MAINNET_MAJORITY_WINDOW
id = ID_MAINNET
dnsSeeds = arrayOf(
"seed.clore.ai",
"seed1.clore.ai",
"seed2.clore.ai",
)
}

override fun getPaymentProtocolId(): String {
return PAYMENT_PROTOCOL_ID_MAINNET
}

override fun getGenesisBlock(): Block {
return Block.createGenesis(this) // Stub genesis block
}

private companion object {
private const val MAINNET_MAJORITY_WINDOW = 1000
private const val MAINNET_MAJORITY_REJECT_BLOCK_OUTDATED = 950
private const val MAINNET_MAJORITY_ENFORCE_BLOCK_UPGRADE = 750
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.tangem.blockchain.blockchains.clore

import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinNetworkProvider
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.BlockchainSdkConfig
import com.tangem.blockchain.common.network.providers.NetworkProvidersBuilder
import com.tangem.blockchain.common.network.providers.ProviderType
import com.tangem.blockchain.network.blockbook.BlockBookNetworkProviderFactory

internal class CloreProvidersBuilder(
override val providerTypes: List<ProviderType>,
private val config: BlockchainSdkConfig,
) : NetworkProvidersBuilder<BitcoinNetworkProvider>() {

private val blockBookProviderFactory by lazy { BlockBookNetworkProviderFactory(config) }

override fun createProviders(blockchain: Blockchain): List<BitcoinNetworkProvider> {
return providerTypes.mapNotNull {
when (it) {
is ProviderType.Public ->
blockBookProviderFactory
.createCloreBlockProvider(blockchain = blockchain, baseHost = it.url)
else -> null
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ enum class Blockchain(
Canxium("canxium", "CAU", "Canxium"),
Chiliz("chiliz", "CHZ", "Chiliz"),
ChilizTestnet("chiliz/test", "CHZ", "Chiliz Spicy Testnet"),
Clore("clore-ai", "CLORE", "Clore"),
;

private val externalLinkProvider: ExternalLinkProvider by lazy { ExternalLinkProviderFactory.makeProvider(this) }
Expand Down Expand Up @@ -217,6 +218,7 @@ enum class Blockchain(
Radiant,
Koinos, KoinosTestnet,
InternetComputer,
Clore,
-> 8

Solana, SolanaTestnet,
Expand Down Expand Up @@ -308,6 +310,7 @@ enum class Blockchain(
Ducatus,
Dash,
Ravencoin, RavencoinTestnet,
Clore,
-> BitcoinAddressService(this)

BitcoinCash, BitcoinCashTestnet -> BitcoinCashAddressService(this)
Expand Down Expand Up @@ -554,6 +557,7 @@ enum class Blockchain(
Chiliz, ChilizTestnet,
Xodex,
Canxium,
Clore,
-> listOf(EllipticCurve.Secp256k1)

Stellar, StellarTestnet,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ class WalletManagerFactory(
Blockchain.BitcoinCash, Blockchain.BitcoinCashTestnet -> BitcoinCashWalletManagerAssembly
Blockchain.Ravencoin, Blockchain.RavencoinTestnet -> RavencoinWalletManagerAssembly
Blockchain.Ducatus -> DucatusWalletManagerAssembly
Blockchain.Clore -> CloreWalletManagerAssembly
// endregion

// region ETH-like blockchains
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ class EstimationFeeAddressFactory {
-> "0xbca45e36a271e106546c89984108685215724e488570a0049a187c473cd521bc"
Blockchain.EnergyWebX, Blockchain.EnergyWebXTestnet,
-> "5CogUCbb5PYYbEHhDVGDN6JRRYBkd4sFRVc4wwP8oy5Su34Z"
Blockchain.Clore -> "AJfAu7RJxiTowM9qVaTbVuS5JCPCpV3p7M"
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.tangem.blockchain.common.assembly.impl

import com.tangem.blockchain.blockchains.bitcoin.BitcoinTransactionBuilder
import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinNetworkService
import com.tangem.blockchain.blockchains.clore.CloreProvidersBuilder
import com.tangem.blockchain.blockchains.ravencoin.RavencoinFeesCalculator
import com.tangem.blockchain.blockchains.ravencoin.RavencoinWalletManager
import com.tangem.blockchain.common.assembly.WalletManagerAssembly
import com.tangem.blockchain.common.assembly.WalletManagerAssemblyInput
import com.tangem.blockchain.transactionhistory.TransactionHistoryProviderFactory

internal object CloreWalletManagerAssembly : WalletManagerAssembly<RavencoinWalletManager>() {

override fun make(input: WalletManagerAssemblyInput): RavencoinWalletManager {
with(input.wallet) {
return RavencoinWalletManager(
wallet = this,
transactionBuilder = BitcoinTransactionBuilder(
walletPublicKey = publicKey.blockchainKey,
blockchain = blockchain,
walletAddresses = addresses,
),
networkProvider = BitcoinNetworkService(
providers = CloreProvidersBuilder(input.providerTypes, input.config).build(blockchain),
),
transactionHistoryProvider = TransactionHistoryProviderFactory.makeProvider(blockchain, input.config),
feesCalculator = RavencoinFeesCalculator(blockchain),
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ object DerivationConfigV1 : DerivationConfig() {
Blockchain.Casper,
Blockchain.CasperTestnet,
-> mapOf(AddressType.Default to DerivationPath("m/44'/506'/0'/0/0"))
Blockchain.Clore -> mapOf(AddressType.Default to DerivationPath("m/44'/1313'/0'/0/0"))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ object DerivationConfigV2 : DerivationConfig() {
Blockchain.Casper,
Blockchain.CasperTestnet,
-> mapOf(AddressType.Default to DerivationPath("m/44'/506'/0'/0/0"))
Blockchain.Clore -> mapOf(AddressType.Default to DerivationPath("m/44'/1313'/0'/0/0"))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ object DerivationConfigV3 : DerivationConfig() {
Blockchain.Casper,
Blockchain.CasperTestnet,
-> mapOf(AddressType.Default to DerivationPath("m/44'/506'/0'/0/0"))
Blockchain.Clore -> mapOf(AddressType.Default to DerivationPath("m/44'/1313'/0'/0/0"))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ internal object ExternalLinkProviderFactory {
Blockchain.Chiliz, Blockchain.ChilizTestnet -> ChilizExternalLinkProvider(isTestnet)
Blockchain.Xodex -> XodexExternalLinkProvider()
Blockchain.Canxium -> CanxiumExternalLinkProvider()
Blockchain.Clore -> CloreExternalLinkProvider()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.tangem.blockchain.externallinkprovider.providers

import com.tangem.blockchain.externallinkprovider.ExternalLinkProvider
import com.tangem.blockchain.externallinkprovider.TxExploreState

internal class CloreExternalLinkProvider : ExternalLinkProvider {

override val explorerBaseUrl: String = "https://clore.cryptoscope.io/"

override fun explorerUrl(walletAddress: String, contractAddress: String?): String {
return explorerBaseUrl + "address/?address=$walletAddress"
}

override fun getExplorerTxUrl(transactionHash: String): TxExploreState {
return TxExploreState.Url(explorerBaseUrl + "tx/?txid=$transactionHash")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.tangem.blockchain.blockchains.bitcoin.BitcoinUnspentOutput
import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinAddressInfo
import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinFee
import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinNetworkProvider
import com.tangem.blockchain.blockchains.clore.CloreMainNetParams
import com.tangem.blockchain.blockchains.dash.DashMainNetParams
import com.tangem.blockchain.blockchains.ducatus.DucatusMainNetParams
import com.tangem.blockchain.blockchains.ravencoin.RavencoinMainNetParams
Expand Down Expand Up @@ -49,6 +50,7 @@ class BlockBookNetworkProvider(
Blockchain.Dash -> DashMainNetParams()
Blockchain.Ravencoin -> RavencoinMainNetParams()
Blockchain.RavencoinTestnet -> RavencoinTestNetParams()
Blockchain.Clore -> CloreMainNetParams()
else -> error("${blockchain.fullName} blockchain is not supported by ${this::class.simpleName}")
}

Expand Down Expand Up @@ -189,11 +191,16 @@ class BlockBookNetworkProvider(
}

private suspend fun getFeePerKb(param: Int): BigDecimal {
val getFeeResponse = withContext(Dispatchers.IO) { api.getFee(param) }
val feeRate = withContext(Dispatchers.IO) {
when (blockchain) {
Blockchain.Clore -> api.getFees(param).result
else -> api.getFee(param).result.feerate
}
}

if (getFeeResponse.result.feerate <= 0) throw BlockchainSdkError.FailedToLoadFee
if (feeRate <= 0) throw BlockchainSdkError.FailedToLoadFee

return getFeeResponse.result.feerate
return feeRate
.toBigDecimal()
.setScale(blockchain.decimals(), RoundingMode.UP)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinNetworkProvider
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.BlockchainSdkConfig
import com.tangem.blockchain.extensions.letNotBlank
import com.tangem.blockchain.network.blockbook.config.CloreBlockBookConfig
import com.tangem.blockchain.network.blockbook.config.GetBlockConfig
import com.tangem.blockchain.network.blockbook.config.NowNodesConfig

Expand Down Expand Up @@ -42,4 +43,13 @@ internal class BlockBookNetworkProviderFactory(
null
}
}

fun createCloreBlockProvider(blockchain: Blockchain, baseHost: String): BitcoinNetworkProvider {
return BlockBookNetworkProvider(
config = CloreBlockBookConfig(
baseHost = baseHost,
),
blockchain = blockchain,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.tangem.blockchain.network.blockbook.config

import com.tangem.blockchain.common.Blockchain

internal class CloreBlockBookConfig(override val baseHost: String) : BlockBookConfig(credentials = null) {

override fun getHost(blockchain: Blockchain, request: BlockBookRequest): String = baseHost

override fun path(request: BlockBookRequest): String = "api/v2"
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ import com.tangem.blockchain.common.logging.AddHeaderInterceptor
import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder
import com.tangem.blockchain.network.blockbook.config.BlockBookConfig
import com.tangem.blockchain.network.blockbook.config.BlockBookRequest
import com.tangem.blockchain.network.blockbook.network.responses.GetAddressResponse
import com.tangem.blockchain.network.blockbook.network.responses.GetFeeResponse
import com.tangem.blockchain.network.blockbook.network.responses.GetUtxoResponseItem
import com.tangem.blockchain.network.blockbook.network.responses.SendTransactionResponse
import com.tangem.blockchain.network.blockbook.network.responses.*
import com.tangem.blockchain.network.moshi
import com.tangem.blockchain.transactionhistory.models.TransactionHistoryRequest
import okhttp3.MediaType.Companion.toMediaTypeOrNull
Expand Down Expand Up @@ -92,6 +89,24 @@ internal class BlockBookApi(private val config: BlockBookConfig, private val blo
.unpack()
}

/**
* It is also a request to receive a fee for confirmation blocks.
* Some blockchains use this method. Such blockchains:
* - CloreAI
*/
suspend fun getFees(param: Int): GetFeesResponse {
val requestBaseUrl = config.getRequestBaseUrl(BlockBookRequest.GetFee, blockchain)
return client
.newCall(
request = Request.Builder()
.get()
.url("$requestBaseUrl/estimatefee/$param")
.build(),
)
.await()
.unpack()
}

suspend fun sendTransaction(txHex: String): SendTransactionResponse {
val requestBaseUrl = config.getRequestBaseUrl(BlockBookRequest.SendTransaction, blockchain)
return client
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.tangem.blockchain.network.blockbook.network.responses

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class GetFeesResponse(
@Json(name = "result") val result: Double,
)
2 changes: 1 addition & 1 deletion tangem-android-tools

0 comments on commit 15f0337

Please sign in to comment.