Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AND-4536 Added decimal chain support #358

Merged
merged 1 commit into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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