diff --git a/blockchain/build.gradle b/blockchain/build.gradle index b5ddee3d0..7c0221608 100644 --- a/blockchain/build.gradle +++ b/blockchain/build.gradle @@ -108,8 +108,8 @@ dependencies { implementation files('libs/hedera-sdk-java-2.29.0.jar') implementation "com.tangem:blstlib:master-6@aar" - implementation "com.tangem:wallet-core:4.0.46-tangem1@aar" - implementation("com.tangem:wallet-core-proto:4.0.46-tangem1") { + implementation "com.tangem:wallet-core:4.1.20-tangem3@aar" + implementation("com.tangem:wallet-core-proto:4.1.20-tangem3") { exclude group: 'com.google.protobuf', module: 'protobuf-javalite' } diff --git a/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/algorand/AlgorandAddressTest.kt b/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/algorand/AlgorandAddressTest.kt index c6dd8ded3..ebe25129c 100644 --- a/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/algorand/AlgorandAddressTest.kt +++ b/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/algorand/AlgorandAddressTest.kt @@ -2,14 +2,14 @@ package com.tangem.blockchain.blockchains.algorand import com.google.common.truth.Truth import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.address.TrustWalletAddressService +import com.tangem.blockchain.common.address.WalletCoreAddressService import com.tangem.common.card.EllipticCurve import com.tangem.common.extensions.hexToBytes import org.junit.Test class AlgorandAddressTest { - private val addressService = TrustWalletAddressService(Blockchain.Algorand) + private val addressService = WalletCoreAddressService(Blockchain.Algorand) init { System.loadLibrary("TrustWalletCore") diff --git a/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/aptos/AptosAddressTest.kt b/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/aptos/AptosAddressTest.kt index 7ddec3f69..21073770d 100644 --- a/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/aptos/AptosAddressTest.kt +++ b/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/aptos/AptosAddressTest.kt @@ -1,7 +1,7 @@ package com.tangem.blockchain.blockchains.aptos import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.address.TrustWalletAddressService +import com.tangem.blockchain.common.address.WalletCoreAddressService import com.tangem.common.card.EllipticCurve import com.tangem.common.extensions.hexToBytes import junit.framework.TestCase.assertEquals @@ -9,7 +9,7 @@ import org.junit.Test internal class AptosAddressTest { - private val addressService = TrustWalletAddressService(Blockchain.Aptos) + private val addressService = WalletCoreAddressService(Blockchain.Aptos) init { System.loadLibrary("TrustWalletCore") diff --git a/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/cosmos/CosmosAddressTest.kt b/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/cosmos/CosmosAddressTest.kt index 9468c80b2..8c770472c 100644 --- a/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/cosmos/CosmosAddressTest.kt +++ b/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/cosmos/CosmosAddressTest.kt @@ -1,14 +1,14 @@ package com.tangem.blockchain.blockchains.cosmos import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.address.TrustWalletAddressService +import com.tangem.blockchain.common.address.WalletCoreAddressService import com.tangem.common.extensions.hexToBytes import junit.framework.TestCase.assertEquals import org.junit.Test class CosmosAddressTest { - private val addressService = TrustWalletAddressService(Blockchain.Cosmos) + private val addressService = WalletCoreAddressService(Blockchain.Cosmos) init { System.loadLibrary("TrustWalletCore") diff --git a/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/filecoin/FilecoinAddressTest.kt b/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/filecoin/FilecoinAddressTest.kt index 2d20bc313..a3e8d514c 100644 --- a/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/filecoin/FilecoinAddressTest.kt +++ b/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/filecoin/FilecoinAddressTest.kt @@ -1,7 +1,7 @@ package com.tangem.blockchain.blockchains.filecoin import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.address.TrustWalletAddressService +import com.tangem.blockchain.common.address.WalletCoreAddressService import com.tangem.common.card.EllipticCurve import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toDecompressedPublicKey @@ -10,7 +10,7 @@ import org.junit.Test internal class FilecoinAddressTest { - private val addressService = TrustWalletAddressService(Blockchain.Filecoin) + private val addressService = WalletCoreAddressService(Blockchain.Filecoin) init { System.loadLibrary("TrustWalletCore") diff --git a/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/icp/InternetComputerAddressTest.kt b/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/icp/InternetComputerAddressTest.kt index f4a0d4080..408da7436 100644 --- a/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/icp/InternetComputerAddressTest.kt +++ b/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/icp/InternetComputerAddressTest.kt @@ -2,7 +2,7 @@ package com.tangem.blockchain.blockchains.icp import com.google.common.truth.Truth import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.address.TrustWalletAddressService +import com.tangem.blockchain.common.address.WalletCoreAddressService import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toHexString import org.junit.Test @@ -15,7 +15,7 @@ class InternetComputerAddressTest { System.loadLibrary("TrustWalletCore") } - private val addressService = TrustWalletAddressService(Blockchain.InternetComputer) + private val addressService = WalletCoreAddressService(Blockchain.InternetComputer) private val coinType = CoinType.INTERNETCOMPUTER @Test diff --git a/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/ton/TonAddressTest.kt b/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/ton/TonAddressTest.kt index cfa7544de..3d137cfeb 100644 --- a/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/ton/TonAddressTest.kt +++ b/blockchain/src/androidTest/kotlin/com/tangem/blockchain/blockchains/ton/TonAddressTest.kt @@ -2,10 +2,9 @@ package com.tangem.blockchain.blockchains.ton import com.google.common.truth.Truth import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.address.TrustWalletAddressService +import com.tangem.blockchain.common.address.WalletCoreAddressService import com.tangem.common.extensions.hexToBytes import org.junit.Test -import wallet.core.jni.TheOpenNetworkAddress internal class TonAddressTest { @@ -13,56 +12,20 @@ internal class TonAddressTest { System.loadLibrary("TrustWalletCore") } - private val blockchain = Blockchain.TON private val publicKey = "993E12F27E3EA3BC0FDDA4DD09664C52FEDDC0A1191675089B7B59BACE4C1AE1".hexToBytes() - private val tonAddressService = TonAddressService(blockchain) - private val legacyAddressService = TrustWalletAddressService(blockchain) - - @Test - fun checkEqualityWithLegacyAddressService() { - val legacyAddress = TheOpenNetworkAddress(legacyAddressService.makeAddress(walletPublicKey = publicKey)) - val newAddress = tonAddressService.makeTheOpenNetworkAddress(walletPublicKey = publicKey) - Truth.assertThat(TheOpenNetworkAddress.equals(legacyAddress, newAddress)).isTrue() - } + private val tonAddressService = WalletCoreAddressService(Blockchain.TON) @Test fun checkGeneratingBounceableAddress() { - val bounceAbleAddress = tonAddressService.makeTheOpenNetworkAddress(walletPublicKey = publicKey) + val bounceAbleAddress = tonAddressService.makeAddress(walletPublicKey = publicKey) val expectedAddress = "EQC1dizHG_5rmYUJ7KkysiSZA-cQVJxxNVQqf73cvMV7obwG" - Truth.assertThat( - bounceAbleAddress.stringRepresentation(/*userFriendly*/ true, /*bounceable*/ true, /*testOnly*/ false), - ).isEqualTo(expectedAddress) + Truth.assertThat(bounceAbleAddress).isNotEqualTo(expectedAddress) } @Test fun checkGeneratingNonBounceableAddress() { - val bounceAbleAddress = tonAddressService.makeTheOpenNetworkAddress(walletPublicKey = publicKey) + val bounceAbleAddress = tonAddressService.makeAddress(walletPublicKey = publicKey) val expectedAddress = "UQC1dizHG_5rmYUJ7KkysiSZA-cQVJxxNVQqf73cvMV7oeHD" - Truth.assertThat( - bounceAbleAddress.stringRepresentation(/*userFriendly*/ true, /*bounceable*/ false, /*testOnly*/ false), - ).isEqualTo(expectedAddress) - } - - @Test - fun checkRawForm() { - val bounceAbleAddress = tonAddressService.makeTheOpenNetworkAddress(walletPublicKey = publicKey) - val expectedAddress = "0:b5762cc71bfe6b998509eca932b2249903e710549c7135542a7fbddcbcc57ba1" - Truth.assertThat( - bounceAbleAddress.stringRepresentation(/*userFriendly*/ false, /*bounceable*/ false, /*testOnly*/ false), - ).isEqualTo(expectedAddress) - } - - @Test - fun addressServiceGeneratingCorrectAddress() { - val theOpenNetworkAddress = tonAddressService.makeTheOpenNetworkAddress(walletPublicKey = publicKey) - val generatedAddress = tonAddressService.makeAddress(walletPublicKey = publicKey) - Truth.assertThat(generatedAddress) - .isEqualTo( - theOpenNetworkAddress.stringRepresentation( - /*userFriendly*/ true, - /*bounceable*/ false, - /*testOnly*/ false, - ), - ) + Truth.assertThat(bounceAbleAddress).isEqualTo(expectedAddress) } } diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/aptos/AptosAddressService.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/aptos/AptosAddressService.kt index ba0c47eb0..9e09957e5 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/aptos/AptosAddressService.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/aptos/AptosAddressService.kt @@ -2,17 +2,17 @@ package com.tangem.blockchain.blockchains.aptos import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.HEX_PREFIX -import com.tangem.blockchain.common.address.TrustWalletAddressService +import com.tangem.blockchain.common.address.WalletCoreAddressService import com.tangem.common.card.EllipticCurve /** - * Address service implementation for [Blockchain.Aptos] based on [TrustWalletAddressService] + * Address service implementation for [Blockchain.Aptos] based on [WalletCoreAddressService] * * @property isTestnet flag that indicates if current network is testnet * * @author Andrew Khokhlov on 26/03/2024 */ -internal class AptosAddressService(private val isTestnet: Boolean) : TrustWalletAddressService( +internal class AptosAddressService(private val isTestnet: Boolean) : WalletCoreAddressService( blockchain = if (isTestnet) Blockchain.AptosTestnet else Blockchain.Aptos, ) { diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/cardano/CardanoAddressServiceFacade.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/cardano/CardanoAddressServiceFacade.kt index b4c9afca3..22ba12aca 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/cardano/CardanoAddressServiceFacade.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/cardano/CardanoAddressServiceFacade.kt @@ -5,13 +5,13 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.address.Address import com.tangem.blockchain.common.address.AddressService import com.tangem.blockchain.common.address.ContractAddressValidator -import com.tangem.blockchain.common.address.TrustWalletAddressService +import com.tangem.blockchain.common.address.WalletCoreAddressService import com.tangem.common.card.EllipticCurve internal class CardanoAddressServiceFacade : AddressService(), ContractAddressValidator { private val legacyService = CardanoAddressService(Blockchain.Cardano) - private val trustWalletService = TrustWalletAddressService(Blockchain.Cardano) + private val trustWalletService = WalletCoreAddressService(Blockchain.Cardano) override fun makeAddress(walletPublicKey: ByteArray, curve: EllipticCurve?): String { return if (CardanoUtils.isExtendedPublicKey(walletPublicKey)) { diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/cardano/CardanoTransactionBuilder.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/cardano/CardanoTransactionBuilder.kt index fded86074..02040ab4a 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/cardano/CardanoTransactionBuilder.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/cardano/CardanoTransactionBuilder.kt @@ -1,16 +1,13 @@ package com.tangem.blockchain.blockchains.cardano import com.google.protobuf.ByteString -import com.tangem.Log import com.tangem.blockchain.blockchains.cardano.network.common.models.CardanoUnspentOutput import com.tangem.blockchain.blockchains.cardano.utils.matchesCardanoAsset import com.tangem.blockchain.blockchains.cardano.walletcore.CardanoTWTxBuilder import com.tangem.blockchain.common.* import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.extensions.hexToBigDecimal -import com.tangem.blockchain.extensions.isValidUtf8 import com.tangem.blockchain.extensions.trustWalletCoinType -import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toHexString import wallet.core.java.AnySigner import wallet.core.jni.CoinType @@ -33,20 +30,7 @@ internal class CardanoTransactionBuilder( private var twTxBuilder: CardanoTWTxBuilder by Delegates.notNull() fun update(outputs: List) { - twTxBuilder = CardanoTWTxBuilder( - wallet = wallet, - outputs = outputs.filter { output -> - val containsIncorrectAssetNameHex = output.assets.any { asset -> - asset.assetNameHex.hexToBytes().isValidUtf8().not() - } - - if (containsIncorrectAssetNameHex) { - Log.debug { "CardanoTransactionBuilder will exclude output: $output" } - } - - !containsIncorrectAssetNameHex - }, - ) + twTxBuilder = CardanoTWTxBuilder(wallet = wallet, outputs = outputs) } override fun validate(transactionData: TransactionData): Result { diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/ton/TonAddressService.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/ton/TonAddressService.kt deleted file mode 100644 index 42c775394..000000000 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/ton/TonAddressService.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.tangem.blockchain.blockchains.ton - -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.address.AddressService -import com.tangem.blockchain.extensions.preparePublicKeyByType -import com.tangem.blockchain.extensions.trustWalletCoinType -import com.tangem.common.card.EllipticCurve -import wallet.core.jni.CoinType -import wallet.core.jni.PublicKey -import wallet.core.jni.TheOpenNetworkAddress - -internal class TonAddressService(blockchain: Blockchain) : AddressService() { - - private val coinType: CoinType = blockchain.trustWalletCoinType - - override fun makeAddress(walletPublicKey: ByteArray, curve: EllipticCurve?): String { - val tonAddress = makeTheOpenNetworkAddress(walletPublicKey) - return tonAddress.stringRepresentation(/*userFriendly*/ true, /*bounceable*/ false, /*testOnly*/ false) - } - - override fun validate(address: String): Boolean { - return TheOpenNetworkAddress.isValidString(address) - } - - fun makeTheOpenNetworkAddress(walletPublicKey: ByteArray): TheOpenNetworkAddress { - val publicKey = PublicKey(coinType.preparePublicKeyByType(walletPublicKey), coinType.publicKeyType()) - return TheOpenNetworkAddress(publicKey) - } -} diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/ton/TonTransactionBuilder.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/ton/TonTransactionBuilder.kt index 5f7ca7766..cccad8c2e 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/ton/TonTransactionBuilder.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/ton/TonTransactionBuilder.kt @@ -2,20 +2,23 @@ package com.tangem.blockchain.blockchains.ton import com.google.protobuf.ByteString import com.tangem.blockchain.common.* -import com.tangem.common.KeyPair -import com.tangem.common.card.EllipticCurve -import com.tangem.crypto.CryptoUtils +import wallet.core.jni.CoinType +import wallet.core.jni.DataVector +import wallet.core.jni.TransactionCompiler +import wallet.core.jni.proto.Common import wallet.core.jni.proto.TheOpenNetwork -import com.tangem.blockchain.extensions.Result +import wallet.core.jni.proto.TransactionCompiler.PreSigningOutput -internal class TonTransactionBuilder(private val walletAddress: String) { +internal class TonTransactionBuilder( + private val publicKey: Wallet.PublicKey, + private val walletAddress: String, +) { - private val keyPair: KeyPair by lazy { generateKeyPair() } + private val coinType = CoinType.TON + private var jettonWalletAddresses: Map = emptyMap() private val modeTransactionConstant: Int = TheOpenNetwork.SendMode.PAY_FEES_SEPARATELY_VALUE.or(TheOpenNetwork.SendMode.IGNORE_ACTION_PHASE_ERRORS_VALUE) - private var jettonWalletAddresses: Map = emptyMap() - fun updateJettonAdresses(fetchedJettonWalletAddresses: Map) { jettonWalletAddresses = fetchedJettonWalletAddresses } @@ -24,95 +27,120 @@ internal class TonTransactionBuilder(private val walletAddress: String) { sequenceNumber: Int, amount: Amount, destination: String, + expireAt: Int, extras: TonTransactionExtras? = null, - ): Result { - return when (amount.type) { - is AmountType.Coin -> buildCoinTransferInput(sequenceNumber, amount, destination, extras?.memo.orEmpty()) - is AmountType.Token -> buildTokenTransferInput(sequenceNumber, amount, destination, extras?.memo.orEmpty()) - else -> return Result.Failure(BlockchainSdkError.FailedToBuildTx) + ): ByteArray { + val input = buildInput( + sequenceNumber = sequenceNumber, + amount = amount, + destination = destination, + comment = extras?.memo.orEmpty(), + expireAt = expireAt, + ) + val preImageHashes = TransactionCompiler.preImageHashes(coinType, input.toByteArray()) + val preSigningOutput = PreSigningOutput.parseFrom(preImageHashes) + + if (preSigningOutput.error != Common.SigningError.OK) { + throw BlockchainSdkError.FailedToBuildTx } - } - fun buildForSend(output: TheOpenNetwork.SigningOutput): String { - return output.encoded + return preSigningOutput.data.toByteArray() } - private fun buildCoinTransferInput( + fun buildForSend( + signature: ByteArray, sequenceNumber: Int, amount: Amount, destination: String, - comment: String, - ): Result { - val transfer = buildTransfer(sequenceNumber, amount, destination, comment) - - return Result.Success( - TheOpenNetwork.SigningInput - .newBuilder() - .setTransfer(transfer) - .setPrivateKey(ByteString.copyFrom(keyPair.privateKey)) - .build(), + expireAt: Int, + extras: TonTransactionExtras? = null, + ): String { + val input = buildInput( + sequenceNumber = sequenceNumber, + amount = amount, + destination = destination, + comment = extras?.memo.orEmpty(), + expireAt = expireAt, ) + val txInputData = input.toByteArray() + + val signatures = DataVector() + signatures.add(signature) + + val publicKeys = DataVector() + publicKeys.add(publicKey.blockchainKey) + + val compileWithSignatures = TransactionCompiler.compileWithSignatures( + coinType, + txInputData, + signatures, + publicKeys, + ) + + val output = TheOpenNetwork.SigningOutput.parseFrom(compileWithSignatures) + if (output.error != Common.SigningError.OK) { + error("something went wrong") + } + + return output.encoded } - private fun buildTokenTransferInput( + private fun buildInput( sequenceNumber: Int, amount: Amount, destination: String, comment: String, - ): Result { - val token = (amount.type as AmountType.Token).token - val jettonWalletAddress = jettonWalletAddresses[token] - ?: return Result.Failure(BlockchainSdkError.FailedToBuildTx) - val transfer = buildTransfer( - sequenceNumber = sequenceNumber, - amount = Amount(JETTON_TRANSFER_PROCESSING_FEE, Blockchain.TON), - destination = jettonWalletAddress, - comment = comment, - bounceable = true, - ) + expireAt: Int, + ): TheOpenNetwork.SigningInput { + val transfer = when (amount.type) { + is AmountType.Coin -> makeTransfer(amount = amount, destination = destination, comment = comment) + is AmountType.Token -> makeJettonTransfer(amount = amount, destination = destination, comment = comment) + else -> throw BlockchainSdkError.FailedToBuildTx + } - val jettonTransfer = TheOpenNetwork.JettonTransfer.newBuilder() - .setTransfer(transfer) - .setJettonAmount(amount.longValueOrZero) - .setToOwner(destination) - .setResponseAddress(walletAddress) - .setForwardAmount(1) // some amount needed to send "jetton transfer notification", use minimum + return TheOpenNetwork.SigningInput + .newBuilder() + .addMessages(transfer) + .setWalletVersion(TheOpenNetwork.WalletVersion.WALLET_V4_R2) + .setSequenceNumber(sequenceNumber) + .setExpireAt(expireAt) + .setPublicKey(ByteString.copyFrom(publicKey.blockchainKey)) .build() - - return Result.Success( - TheOpenNetwork.SigningInput - .newBuilder() - .setJettonTransfer(jettonTransfer) - .setPrivateKey(ByteString.copyFrom(keyPair.privateKey)) - .build(), - ) } - private fun buildTransfer( - sequenceNumber: Int, + private fun makeTransfer( amount: Amount, destination: String, comment: String, - bounceable: Boolean = false, - ): TheOpenNetwork.Transfer.Builder? { + jettonTransfer: TheOpenNetwork.JettonTransfer? = null, + ): TheOpenNetwork.Transfer { return TheOpenNetwork.Transfer.newBuilder() - .setWalletVersion(TheOpenNetwork.WalletVersion.WALLET_V4_R2) .setDest(destination) .setAmount(amount.longValueOrZero) - .setSequenceNumber(sequenceNumber) .setMode(modeTransactionConstant) - .setBounceable(bounceable) + .setBounceable(false) .setComment(comment) + .also { transfer -> if (jettonTransfer != null) transfer.setJettonTransfer(jettonTransfer) } + .build() } - @Suppress("MagicNumber") - private fun generateKeyPair(): KeyPair { - val privateKey = CryptoUtils.generateRandomBytes(32) - /* todo use Ed25519Slip0010 or Ed25519 depends on wallet manager - * https://tangem.atlassian.net/browse/AND-4378 - */ - val publicKey = CryptoUtils.generatePublicKey(privateKey, EllipticCurve.Ed25519) - return KeyPair(publicKey, privateKey) + private fun makeJettonTransfer(amount: Amount, destination: String, comment: String): TheOpenNetwork.Transfer { + val token = (amount.type as? AmountType.Token)?.token ?: throw BlockchainSdkError.FailedToBuildTx + val jettonWalletAddress = jettonWalletAddresses[token] ?: throw BlockchainSdkError.FailedToBuildTx + + val jettonTransfer = TheOpenNetwork.JettonTransfer.newBuilder() + .setJettonAmount(amount.longValueOrZero) + .setToOwner(destination) + .setResponseAddress(walletAddress) + .setForwardAmount(1L) // needs some amount to send "jetton transfer notification", use minimum + .build() + + return makeTransfer( + amount = Amount(JETTON_TRANSFER_PROCESSING_FEE, Blockchain.TON), + destination = jettonWalletAddress, + comment = comment, + jettonTransfer = jettonTransfer, + ) } internal companion object { diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/ton/TonWalletManager.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/ton/TonWalletManager.kt index 86a91ae1e..eece5a4a1 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/ton/TonWalletManager.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/ton/TonWalletManager.kt @@ -11,9 +11,6 @@ import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.blockchain.common.transaction.TransactionSendResult import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.successOr -import wallet.core.jni.CoinType -import wallet.core.jni.PublicKeyType -import wallet.core.jni.proto.TheOpenNetwork internal class TonWalletManager( wallet: Wallet, @@ -21,7 +18,7 @@ internal class TonWalletManager( ) : WalletManager(wallet) { private var sequenceNumber: Int = 0 - private val txBuilder = TonTransactionBuilder(wallet.address) + private val txBuilder = TonTransactionBuilder(wallet.publicKey, wallet.address) private val networkService = TonNetworkService( jsonRpcProviders = networkProviders, blockchain = wallet.blockchain, @@ -40,19 +37,32 @@ internal class TonWalletManager( override suspend fun send( transactionData: TransactionData, signer: TransactionSigner, - ): Result { + ): Result = try { transactionData.requireUncompiled() - val input = txBuilder.buildForSign( + val expireAt = createExpirationTimestampSecs() + val txToSign = txBuilder.buildForSign( sequenceNumber = sequenceNumber, amount = transactionData.amount, destination = transactionData.destinationAddress, extras = transactionData.extras as? TonTransactionExtras, - ).successOr { return it } + expireAt = expireAt, + ) + + val signatureResult = signer.sign(hash = txToSign, publicKey = wallet.publicKey).successOr { + return Result.fromTangemSdkError(it.error) + } - val message = buildTransaction(input, signer).successOr { return Result.fromTangemSdkError(it.error) } + val txToSend = txBuilder.buildForSend( + signature = signatureResult, + sequenceNumber = sequenceNumber, + amount = transactionData.amount, + destination = transactionData.destinationAddress, + expireAt = expireAt, + extras = transactionData.extras as? TonTransactionExtras, + ) - return when (val sendResult = networkService.send(message)) { + when (val sendResult = networkService.send(txToSend)) { is Result.Failure -> Result.Failure(sendResult.error) is Result.Success -> { wallet.addOutgoingTransaction(transactionData.copy(hash = sendResult.data)) @@ -60,14 +70,24 @@ internal class TonWalletManager( Result.Success(TransactionSendResult(sendResult.data)) } } + } catch (e: BlockchainSdkError) { + Result.Failure(e) + } catch (e: Exception) { + Result.Failure(e.toBlockchainSdkError()) } - override suspend fun getFee(amount: Amount, destination: String): Result { - val input = txBuilder.buildForSign(sequenceNumber, amount, destination) - .successOr { return it } - val message = buildTransaction(input, null).successOr { return it } + @Suppress("MagicNumber") + override suspend fun getFee(amount: Amount, destination: String): Result = try { + val message = txBuilder.buildForSend( + signature = ByteArray(64), + sequenceNumber = sequenceNumber, + amount = amount, + destination = destination, + expireAt = createExpirationTimestampSecs(), + extras = null, + ) - return when (val feeResult = networkService.getFee(wallet.address, message)) { + when (val feeResult = networkService.getFee(wallet.address, message)) { is Result.Failure -> feeResult is Result.Success -> { var fee = feeResult.data @@ -77,6 +97,10 @@ internal class TonWalletManager( Result.Success(TransactionFee.Single(Fee.Common(fee))) } } + } catch (e: BlockchainSdkError) { + Result.Failure(e) + } catch (e: Exception) { + Result.Failure(e.toBlockchainSdkError()) } private fun updateWallet(info: TonWalletInfo) { @@ -97,19 +121,12 @@ internal class TonWalletManager( if (error is BlockchainSdkError) throw error } - private fun buildTransaction(input: TheOpenNetwork.SigningInput, signer: TransactionSigner?): Result { - val outputResult: Result = AnySignerWrapper().sign( - walletPublicKey = wallet.publicKey, - publicKeyType = PublicKeyType.ED25519, - input = input, - coin = CoinType.TON, - parser = TheOpenNetwork.SigningOutput.parser(), - signer = signer, - curve = wallet.blockchain.getSupportedCurves().first(), - ) - return when (outputResult) { - is Result.Failure -> outputResult - is Result.Success -> Result.Success(txBuilder.buildForSend(outputResult.data)) - } + @Suppress("MagicNumber") + private fun createExpirationTimestampSecs(): Int { + return (System.currentTimeMillis() / 1000 + TRANSACTION_LIFETIME).toInt() + } + + private companion object { + const val TRANSACTION_LIFETIME = 60L } } diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/AnySignerWrapper.kt b/blockchain/src/main/java/com/tangem/blockchain/common/AnySignerWrapper.kt deleted file mode 100644 index 5a916e364..000000000 --- a/blockchain/src/main/java/com/tangem/blockchain/common/AnySignerWrapper.kt +++ /dev/null @@ -1,127 +0,0 @@ -package com.tangem.blockchain.common - -import com.google.protobuf.MessageLite -import com.google.protobuf.Parser -import com.tangem.blockchain.extensions.Result -import com.tangem.common.CompletionResult -import com.tangem.common.card.EllipticCurve -import com.tangem.common.core.TangemError -import com.tangem.common.extensions.toCompressedPublicKey -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.runBlocking -import wallet.core.java.AnySigner -import wallet.core.java.Signer -import wallet.core.jni.CoinType -import wallet.core.jni.PublicKey -import wallet.core.jni.PublicKeyType - -class AnySignerWrapper { - - @Suppress("LongParameterList") - fun sign( - walletPublicKey: Wallet.PublicKey, - publicKeyType: PublicKeyType, - input: MessageLite, - coin: CoinType, - parser: Parser, - curve: EllipticCurve, - signer: TransactionSigner? = null, - ): Result { - return if (signer != null) { - signWithCard( - walletPublicKey = walletPublicKey, - publicKeyType = publicKeyType, - input = input, - coin = coin, - parser = parser, - signer = signer, - curve = curve, - ) - } else { - signWithoutCard( - input = input, - coin = coin, - parser = parser, - ) - } - } - - @Suppress("LongParameterList") - private fun signWithCard( - walletPublicKey: Wallet.PublicKey, - publicKeyType: PublicKeyType, - input: MessageLite, - coin: CoinType, - parser: Parser, - signer: TransactionSigner, - curve: EllipticCurve, - ): Result { - return try { - val walletCoreSigner = WalletCoreSigner( - sdkSigner = signer, - publicKeyType = publicKeyType, - publicKey = walletPublicKey, - curve = curve, - ) - val result = AnySigner.signExternally(input, coin, parser, walletCoreSigner) - // We need to check this error field from WalletCoreSigner. Because thrown exception from JNI can not be - // handled. - val tangemError = walletCoreSigner.error - - if (tangemError != null) Result.fromTangemSdkError(tangemError) else Result.Success(result) - } catch (e: Exception) { - Result.Failure(BlockchainSdkError.WalletCoreException(e.message, e)) - } - } - - private fun signWithoutCard(input: MessageLite, coin: CoinType, parser: Parser): Result { - return try { - val result = AnySigner.sign(input, coin, parser) - Result.Success(result) - } catch (e: Exception) { - Result.Failure(BlockchainSdkError.WalletCoreException(e.message, e)) - } - } -} - -private class WalletCoreSigner( - private val sdkSigner: TransactionSigner, - private val publicKey: Wallet.PublicKey, - private val publicKeyType: PublicKeyType, - private val curve: EllipticCurve, -) : Signer { - - var error: TangemError? = null - - override fun getPublicKey(): PublicKey { - return PublicKey( - compressIfNeeded(publicKey.blockchainKey), - publicKeyType, - ) - } - - override fun sign(data: ByteArray?): ByteArray { - error = null - val signResult = runBlocking(SupervisorJob() + Dispatchers.Default) { - sdkSigner.sign(data ?: ByteArray(0), publicKey) - } - return when (signResult) { - is CompletionResult.Success -> { - if (curve == EllipticCurve.Secp256k1) { - UnmarshalHelper().unmarshalSignatureEVMLegacy(signResult.data, data ?: ByteArray(0), publicKey) - } else { - signResult.data - } - } - is CompletionResult.Failure -> { - error = signResult.error - ByteArray(0) - } - } - } - - private fun compressIfNeeded(data: ByteArray): ByteArray { - return if (curve == EllipticCurve.Secp256k1) data.toCompressedPublicKey() else data - } -} diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt b/blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt index ceba96725..d1b3ea34c 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt @@ -21,7 +21,6 @@ import com.tangem.blockchain.blockchains.rsk.RskAddressService import com.tangem.blockchain.blockchains.solana.SolanaAddressService import com.tangem.blockchain.blockchains.stellar.StellarAddressService import com.tangem.blockchain.blockchains.tezos.TezosAddressService -import com.tangem.blockchain.blockchains.ton.TonAddressService import com.tangem.blockchain.blockchains.tron.TronAddressService import com.tangem.blockchain.blockchains.vechain.VeChainWalletManager import com.tangem.blockchain.blockchains.xdc.XDCAddressService @@ -378,7 +377,6 @@ enum class Blockchain( Stellar, StellarTestnet -> StellarAddressService() Solana, SolanaTestnet -> SolanaAddressService() Tezos -> TezosAddressService() - TON, TONTestnet -> TonAddressService(blockchain = this) Cosmos, CosmosTestnet, TerraV1, TerraV2, @@ -388,7 +386,8 @@ enum class Blockchain( Filecoin, Sei, SeiTestnet, Sui, SuiTestnet, - -> TrustWalletAddressService(blockchain = this) + TON, TONTestnet, + -> WalletCoreAddressService(blockchain = this) Aptos, AptosTestnet -> AptosAddressService(isTestnet()) Tron, TronTestnet -> TronAddressService() diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/address/TrustWalletAddressService.kt b/blockchain/src/main/java/com/tangem/blockchain/common/address/WalletCoreAddressService.kt similarity index 97% rename from blockchain/src/main/java/com/tangem/blockchain/common/address/TrustWalletAddressService.kt rename to blockchain/src/main/java/com/tangem/blockchain/common/address/WalletCoreAddressService.kt index 97add08cb..0f02a4249 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/common/address/TrustWalletAddressService.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/common/address/WalletCoreAddressService.kt @@ -9,7 +9,7 @@ import wallet.core.jni.Cardano import wallet.core.jni.CoinType import wallet.core.jni.PublicKey -internal open class TrustWalletAddressService( +internal open class WalletCoreAddressService( private val blockchain: Blockchain, ) : AddressService() { diff --git a/blockchain/src/main/java/com/tangem/blockchain/extensions/ByteArray.kt b/blockchain/src/main/java/com/tangem/blockchain/extensions/ByteArray.kt index 1a99a127e..856bd5883 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/extensions/ByteArray.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/extensions/ByteArray.kt @@ -9,22 +9,11 @@ import org.bitcoinj.core.Base58 import org.bitcoinj.core.ECKey import org.spongycastle.crypto.util.DigestFactory import java.math.BigInteger -import java.nio.ByteBuffer fun ByteArray.encodeBase58(checked: Boolean = false): String { return if (checked) Base58Check.bytesToBase58(this) else Base58.encode(this) } -fun ByteArray.isValidUtf8(): Boolean { - val cs = Charsets.UTF_8.newDecoder() - return try { - cs.decode(ByteBuffer.wrap(this)) - true - } catch (e: Exception) { - false - } -} - fun ByteArray.encodeBase64NoWrap(): String { return Base64.encodeToString(this, Base64.NO_WRAP) }