Skip to content

Commit

Permalink
AND-4536 Added decimal chain support
Browse files Browse the repository at this point in the history
  • Loading branch information
iiiburnyiii authored and kozarezvlad committed Nov 3, 2023
1 parent e8648ec commit ded4cd8
Show file tree
Hide file tree
Showing 13 changed files with 288 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.tangem.blockchain.blockchains.decimal

import com.tangem.blockchain.blockchains.binance.client.encoding.Crypto
import com.tangem.blockchain.common.address.AddressService
import com.tangem.blockchain.common.address.AddressType
import com.tangem.common.card.EllipticCurve
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toDecompressedPublicKey
import org.bitcoinj.core.Bech32
import org.kethereum.crypto.toAddress
import org.kethereum.erc55.hasValidERC55ChecksumOrNoChecksum
import org.kethereum.erc55.withERC55Checksum
import org.kethereum.model.Address
import org.kethereum.model.PublicKey
import org.komputing.khex.extensions.toHexString
import com.tangem.blockchain.common.address.Address as SdkAddress

internal class DecimalAddressService : AddressService() {

override fun makeAddress(walletPublicKey: ByteArray, curve: EllipticCurve?): String {
return makeErcAddress(walletPublicKey)
}

override fun makeAddresses(
walletPublicKey: ByteArray,
curve: EllipticCurve?,
): Set<SdkAddress> {
val ercAddress = makeErcAddress(walletPublicKey)

return setOf(
SdkAddress(ercAddress),
SdkAddress(
convertErcAddressToDscAddress(ercAddress),
AddressType.Legacy,
)
)
}

private fun makeErcAddress(walletPublicKey: ByteArray): String {
val decompressedPublicKey = walletPublicKey
.toDecompressedPublicKey()
.sliceArray(1..64)

return PublicKey(decompressedPublicKey)
.toAddress()
.withERC55Checksum()
.hex
}

override fun validate(address: String): Boolean {
val addressToValidate = when {
address.startsWith(ADDRESS_PREFIX) || address.startsWith(LEGACY_ADDRESS_PREFIX) -> {
convertDscAddressToErcAddress(address) ?: return false
}

else -> address
}

return Address(addressToValidate).hasValidERC55ChecksumOrNoChecksum()
}

companion object {
private const val ADDRESS_PREFIX = "d0"
private const val LEGACY_ADDRESS_PREFIX = "dx"
private const val ERC55_ADDRESS_PREFIX = "0x"

fun convertDscAddressToErcAddress(addressHex: String): String? {
if (addressHex.startsWith(ERC55_ADDRESS_PREFIX)) {
return addressHex
}

val (prefix, addressBytes) = Bech32.decode(addressHex).let { it.hrp to it.data }
if (prefix == null || addressBytes == null) return null

val convertedAddressBytes = Crypto.convertBits(addressBytes, 0, addressBytes.size, 5, 8, false)

return convertedAddressBytes.toHexString()
}

fun convertErcAddressToDscAddress(addressHex: String): String {
if (addressHex.startsWith(ADDRESS_PREFIX) || addressHex.startsWith(LEGACY_ADDRESS_PREFIX)) {
return addressHex
}

val addressBytes = addressHex.hexToBytes()
val converted = Crypto.convertBits(addressBytes, 0, addressBytes.size, 8, 5, false)

return Bech32.encode(ADDRESS_PREFIX, converted)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.tangem.blockchain.blockchains.decimal

import com.tangem.blockchain.blockchains.ethereum.CompiledEthereumTransaction
import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionBuilder
import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager
import com.tangem.blockchain.blockchains.ethereum.network.EthereumNetworkProvider
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.TransactionSigner
import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.SimpleResult
import java.math.BigInteger

internal class DecimalWalletManager(
wallet: Wallet,
transactionBuilder: EthereumTransactionBuilder,
networkProvider: EthereumNetworkProvider,
) : EthereumWalletManager(wallet, transactionBuilder, networkProvider) {

override suspend fun getFee(amount: Amount, destination: String): Result<TransactionFee> {
return super.getFee(amount, convertAddress(destination))
}

override suspend fun getFee(amount: Amount, destination: String, data: String): Result<TransactionFee> {
return super.getFee(amount, convertAddress(destination), data)
}

override suspend fun getFeeInternal(amount: Amount, destination: String, data: String?): Result<TransactionFee> {
return super.getFeeInternal(amount, convertAddress(destination), data)
}

override suspend fun getGasLimit(amount: Amount, destination: String): Result<BigInteger> {
return super.getGasLimit(amount, convertAddress(destination))
}

override suspend fun getGasLimit(amount: Amount, destination: String, data: String): Result<BigInteger> {
return super.getGasLimit(amount, convertAddress(destination), data)
}

override suspend fun send(transactionData: TransactionData, signer: TransactionSigner): SimpleResult {
return super.send(convertTransactionDataAddress(transactionData), signer)
}

override suspend fun sign(
transactionData: TransactionData,
signer: TransactionSigner,
): Result<Pair<ByteArray, CompiledEthereumTransaction>> {
return super.sign(convertTransactionDataAddress(transactionData), signer)
}

private fun convertTransactionDataAddress(transactionData: TransactionData) = transactionData.copy(
destinationAddress = convertAddress(transactionData.destinationAddress),
)

private fun convertAddress(destinationAddress: String): String {
return DecimalAddressService.convertDscAddressToErcAddress(destinationAddress) ?: destinationAddress
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,28 @@ internal fun Blockchain.getEthereumJsonRpcProviders(
EthereumJsonRpcProvider(baseUrl = "https://api.kainosbp.com", postfixUrl = "evm"),
EthereumJsonRpcProvider(baseUrl = "https://telos-evm.rpc.thirdweb.com/")
)

Blockchain.TelosTestnet -> listOf(
EthereumJsonRpcProvider(baseUrl = "https://telos-evm-testnet.rpc.thirdweb.com/"),
)

Blockchain.OctaSpace -> listOf(
EthereumJsonRpcProvider(baseUrl = "https://rpc.octa.space"),
EthereumJsonRpcProvider(baseUrl = "https://octaspace.rpc.thirdweb.com"),
)

Blockchain.Decimal -> listOf(
EthereumJsonRpcProvider(baseUrl = "https://node.decimalchain.com/web3/"),
EthereumJsonRpcProvider(baseUrl = "https://node1-mainnet.decimalchain.com/web3/"),
EthereumJsonRpcProvider(baseUrl = "https://node2-mainnet.decimalchain.com/web3/"),
EthereumJsonRpcProvider(baseUrl = "https://node3-mainnet.decimalchain.com/web3/"),
EthereumJsonRpcProvider(baseUrl = "https://node4-mainnet.decimalchain.com/web3/"),
)

Blockchain.DecimalTestnet -> listOf(
EthereumJsonRpcProvider(baseUrl = "https://testnet-val.decimalchain.com/web3/")
)

else -> throw IllegalStateException("$this isn't supported")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,6 @@ enum class Chain(
Cronos(25, Blockchain.Cronos),
OctaSpace(800001, Blockchain.OctaSpace),
OctaSpaceTestnet(800002, Blockchain.OctaSpaceTestnet),
Decimal(75, Blockchain.Decimal),
DecimalTestnet(202020, Blockchain.DecimalTestnet)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.tangem.blockchain.blockchains.bitcoin.BitcoinAddressService
import com.tangem.blockchain.blockchains.bitcoincash.BitcoinCashAddressService
import com.tangem.blockchain.blockchains.cardano.CardanoAddressServiceFacade
import com.tangem.blockchain.blockchains.chia.ChiaAddressService
import com.tangem.blockchain.blockchains.decimal.DecimalAddressService
import com.tangem.blockchain.blockchains.ethereum.Chain
import com.tangem.blockchain.blockchains.ethereum.EthereumAddressService
import com.tangem.blockchain.blockchains.kaspa.KaspaAddressService
Expand Down Expand Up @@ -96,6 +97,8 @@ enum class Blockchain(
OctaSpaceTestnet("octaspace/test", "OCTA", "OctaSpace Testnet"),
Chia("chia", "XCH", "Chia Network"),
ChiaTestnet("chia/test", "TXCH", "Chia Network Testnet"),
Decimal("decimal", "DEL", "Decimal Smart Chain"),
DecimalTestnet("decimal/test", "tDEL", "Decimal Smart Chain Testnet"),
;

private val externalLinkProvider: ExternalLinkProvider by lazy { ExternalLinkProviderFactory.makeProvider(this) }
Expand All @@ -104,6 +107,7 @@ enum class Blockchain(
Unknown -> 0
Near, NearTestnet,
-> 5

Cardano,
XRP,
Tezos,
Expand All @@ -113,6 +117,7 @@ enum class Blockchain(
-> 6

Stellar, StellarTestnet -> 7

Bitcoin, BitcoinTestnet,
BitcoinCash, BitcoinCashTestnet,
Binance, BinanceTestnet,
Expand All @@ -129,6 +134,7 @@ enum class Blockchain(
-> 9

Polkadot -> 10

PolkadotTestnet, Kusama, AlephZero, AlephZeroTestnet,
Chia, ChiaTestnet,
-> 12
Expand All @@ -148,6 +154,7 @@ enum class Blockchain(
Cronos,
Telos, TelosTestnet,
OctaSpace, OctaSpaceTestnet,
Decimal, DecimalTestnet,
-> 18
}

Expand Down Expand Up @@ -175,7 +182,6 @@ enum class Blockchain(
Dash,
Ravencoin, RavencoinTestnet,
-> BitcoinAddressService(this)

BitcoinCash, BitcoinCashTestnet -> BitcoinCashAddressService(this)
Arbitrum, ArbitrumTestnet,
Ethereum, EthereumTestnet,
Expand All @@ -194,6 +200,7 @@ enum class Blockchain(
OctaSpace, OctaSpaceTestnet,
-> EthereumAddressService()

Decimal, DecimalTestnet -> DecimalAddressService()
RSK -> RskAddressService()
Cardano -> CardanoAddressServiceFacade()
XRP -> XrpAddressService()
Expand All @@ -205,7 +212,6 @@ enum class Blockchain(
Tezos -> TezosAddressService()
TON, TONTestnet, Cosmos, CosmosTestnet, TerraV1, TerraV2, Near, NearTestnet,
-> TrustWalletAddressService(blockchain = this)

Tron, TronTestnet -> TronAddressService()
Kaspa -> KaspaAddressService()
Chia, ChiaTestnet -> ChiaAddressService(this)
Expand Down Expand Up @@ -269,6 +275,7 @@ enum class Blockchain(
OctaSpace, OctaSpaceTestnet -> OctaSpaceTestnet
Chia, ChiaTestnet -> ChiaTestnet
Near, NearTestnet -> NearTestnet
Decimal, DecimalTestnet -> DecimalTestnet
else -> null
}
}
Expand Down Expand Up @@ -313,6 +320,7 @@ enum class Blockchain(
TerraV1, TerraV2,
Cronos,
OctaSpace, OctaSpaceTestnet,
Decimal, DecimalTestnet,
-> listOf(EllipticCurve.Secp256k1)

Stellar, StellarTestnet,
Expand Down Expand Up @@ -357,6 +365,8 @@ enum class Blockchain(
Cronos -> Chain.Cronos.id
OctaSpace -> Chain.OctaSpace.id
OctaSpaceTestnet -> Chain.OctaSpaceTestnet.id
Decimal -> Chain.Decimal.id
DecimalTestnet -> Chain.DecimalTestnet.id
else -> null
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import com.tangem.blockchain.common.assembly.WalletManagerAssembly
import com.tangem.blockchain.common.assembly.WalletManagerAssemblyInput
import com.tangem.blockchain.common.assembly.impl.*
import com.tangem.common.card.EllipticCurve
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey

class WalletManagerFactory(private val config: BlockchainSdkConfig = BlockchainSdkConfig()) {

Expand Down Expand Up @@ -153,6 +151,10 @@ class WalletManagerFactory(private val config: BlockchainSdkConfig = BlockchainS
EthereumLikeWalletManagerAssembly
}

Blockchain.Decimal, Blockchain.DecimalTestnet -> {
DecimalWalletManagerAssembly
}

Blockchain.Optimism, Blockchain.OptimismTestnet -> {
OptimismWalletManagerAssembly
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.tangem.blockchain.common.assembly.impl

import com.tangem.blockchain.blockchains.decimal.DecimalWalletManager
import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionBuilder
import com.tangem.blockchain.blockchains.ethereum.getEthereumJsonRpcProviders
import com.tangem.blockchain.blockchains.ethereum.network.EthereumNetworkService
import com.tangem.blockchain.common.assembly.WalletManagerAssembly
import com.tangem.blockchain.common.assembly.WalletManagerAssemblyInput

internal object DecimalWalletManagerAssembly : WalletManagerAssembly<DecimalWalletManager>() {

override fun make(input: WalletManagerAssemblyInput): DecimalWalletManager {
return with(input.wallet) {
DecimalWalletManager(
wallet = this,
transactionBuilder = EthereumTransactionBuilder(
walletPublicKey = publicKey.blockchainKey,
blockchain = blockchain
),
networkProvider = EthereumNetworkService(
jsonRpcProviders = blockchain.getEthereumJsonRpcProviders(input.config),
)
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,12 @@ object DerivationConfigV1 : DerivationConfig() {
Blockchain.Ethereum,
Blockchain.EthereumPow,
Blockchain.EthereumFair,
Blockchain.OctaSpace
Blockchain.OctaSpace,
Blockchain.Decimal,
-> {
mapOf(AddressType.Default to DerivationPath("m/44'/60'/0'/0/0"))
}

Blockchain.EthereumClassic -> {
mapOf(AddressType.Default to DerivationPath("m/44'/61'/0'/0/0"))
}
Expand Down Expand Up @@ -174,9 +176,10 @@ object DerivationConfigV1 : DerivationConfig() {
Blockchain.AlephZeroTestnet,
Blockchain.OctaSpaceTestnet,
Blockchain.NearTestnet,
Blockchain.DecimalTestnet,
-> {
mapOf(AddressType.Default to DerivationPath("m/44'/1'/0'/0/0"))
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,12 @@ object DerivationConfigV2 : DerivationConfig() {
Blockchain.Kava,
Blockchain.Cronos,
Blockchain.Telos,
Blockchain.OctaSpace -> {
Blockchain.OctaSpace,
Blockchain.Decimal,
-> {
mapOf(AddressType.Default to DerivationPath("m/44'/60'/0'/0/0"))
}

Blockchain.Binance -> {
mapOf(AddressType.Default to DerivationPath("m/44'/714'/0'/0/0"))
}
Expand Down Expand Up @@ -148,10 +151,11 @@ object DerivationConfigV2 : DerivationConfig() {
Blockchain.TelosTestnet,
Blockchain.AlephZeroTestnet,
Blockchain.OctaSpaceTestnet,
Blockchain.NearTestnet
Blockchain.NearTestnet,
Blockchain.DecimalTestnet,
-> {
mapOf(AddressType.Default to DerivationPath("m/44'/1'/0'/0/0"))
}
}
}
}
}
Loading

0 comments on commit ded4cd8

Please sign in to comment.