From 9e2c2aa3de41f97a9a81109d343ed79fd5847768 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 20 Jul 2020 16:01:42 +0300 Subject: [PATCH 1/5] Updated on 2026-08-14 --- .../tangem/ui/fragment/wallet/LoadedWalletFragment.kt | 10 +++------- .../main/java/com/tangem/util/extensions/TangemCard.kt | 6 ++++++ .../java/com/tangem/ui/PrepareTransactionFragment.kt | 5 ++++- 3 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/com/tangem/util/extensions/TangemCard.kt diff --git a/app/src/main/java/com/tangem/ui/fragment/wallet/LoadedWalletFragment.kt b/app/src/main/java/com/tangem/ui/fragment/wallet/LoadedWalletFragment.kt index 0e176293d4..25fd8604ad 100644 --- a/app/src/main/java/com/tangem/ui/fragment/wallet/LoadedWalletFragment.kt +++ b/app/src/main/java/com/tangem/ui/fragment/wallet/LoadedWalletFragment.kt @@ -49,6 +49,7 @@ import com.tangem.ui.fragment.pin.PinSwapFragment import com.tangem.ui.navigation.NavigationResultListener import com.tangem.util.LOG import com.tangem.util.UtilHelper +import com.tangem.util.extensions.isStart2CoinCard import com.tangem.wallet.* import kotlinx.android.synthetic.main.dialog_pay_id.view.* import kotlinx.android.synthetic.main.fr_loaded_wallet.* @@ -155,7 +156,7 @@ class LoadedWalletFragment : BaseFragment(), NavigationResultListener, NfcAdapte getString(R.string.loaded_wallet_load_via_qr) ) - if (ctx.blockchain.isPayIdSupported) { + if (ctx.blockchain.isPayIdSupported && !ctx.card.isStart2CoinCard()) { ivPayId.visibility = View.VISIBLE ivPayId.imageAlpha = 100 ivPayId.setOnClickListener { createPayIdDialog() } @@ -325,7 +326,7 @@ class LoadedWalletFragment : BaseFragment(), NavigationResultListener, NfcAdapte } private fun getPayIdIfApplicable() { - if (ctx.blockchain.isPayIdSupported) { + if (ctx.blockchain.isPayIdSupported && !ctx.card.isStart2CoinCard()) { viewModel.getPayId( Util.byteArrayToHexString(ctx.card.cid!!), Util.byteArrayToHexString(ctx.card.cardPublicKey!!)) @@ -960,9 +961,4 @@ class LoadedWalletFragment : BaseFragment(), NavigationResultListener, NfcAdapte clipboard.primaryClip = ClipData.newPlainText(text, text) Toast.makeText(activity, R.string.loaded_wallet_toast_copied, Toast.LENGTH_LONG).show() } - - private fun TangemCard.isStart2CoinCard(): Boolean { - return (Util.bytesToHex(ctx.card?.cid)?.startsWith("1") == true) - } - } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/util/extensions/TangemCard.kt b/app/src/main/java/com/tangem/util/extensions/TangemCard.kt new file mode 100644 index 0000000000..4e3cc50e4b --- /dev/null +++ b/app/src/main/java/com/tangem/util/extensions/TangemCard.kt @@ -0,0 +1,6 @@ +package com.tangem.util.extensions + +import com.tangem.tangem_card.data.TangemCard +import com.tangem.tangem_card.util.Util + +fun TangemCard.isStart2CoinCard(): Boolean = (Util.bytesToHex(this.cid).startsWith("1")) \ No newline at end of file diff --git a/app/src/tangemAccess/java/com/tangem/ui/PrepareTransactionFragment.kt b/app/src/tangemAccess/java/com/tangem/ui/PrepareTransactionFragment.kt index 1e75eda210..8ca425b1e7 100644 --- a/app/src/tangemAccess/java/com/tangem/ui/PrepareTransactionFragment.kt +++ b/app/src/tangemAccess/java/com/tangem/ui/PrepareTransactionFragment.kt @@ -17,6 +17,7 @@ import com.tangem.ui.fragment.BaseFragment import com.tangem.ui.fragment.qr.CameraPermissionManager import com.tangem.ui.navigation.NavigationResultListener import com.tangem.util.UtilHelper +import com.tangem.util.extensions.isStart2CoinCard import com.tangem.wallet.CoinEngineFactory import com.tangem.wallet.R import com.tangem.wallet.TangemContext @@ -45,7 +46,9 @@ class PrepareTransactionFragment : BaseFragment(), NavigationResultListener, Nfc Html.fromHtml(engine!!.balanceHTML) tvBalance.text = html - if (ctx.blockchain.isPayIdSupported) etWallet.hint = getString(R.string.prepare_transaction_hint_address_or_pay_id) + if (ctx.blockchain.isPayIdSupported && !ctx.card.isStart2CoinCard()) { + etWallet.hint = getString(R.string.prepare_transaction_hint_address_or_pay_id) + } if (!engine.allowSelectFeeInclusion()) { rgIncFee.visibility = View.INVISIBLE From 5343873ad488898baadb01f246368d514bb4c0f8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 20 Jul 2020 17:41:20 +0300 Subject: [PATCH 2/5] Updated on 2026-08-14 --- .../main/java/com/tangem/data/Blockchain.java | 9 ----- app/src/main/java/com/tangem/data/PayId.kt | 37 +++++++++++++++++++ .../fragment/wallet/LoadedWalletFragment.kt | 10 ++--- 3 files changed, 42 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/com/tangem/data/PayId.kt diff --git a/app/src/main/java/com/tangem/data/Blockchain.java b/app/src/main/java/com/tangem/data/Blockchain.java index 278732d3f1..75b9b28495 100644 --- a/app/src/main/java/com/tangem/data/Blockchain.java +++ b/app/src/main/java/com/tangem/data/Blockchain.java @@ -2,8 +2,6 @@ package com.tangem.data; import com.tangem.tangem_sdk.R; -import java.util.EnumSet; - /** * Created by dvol on 06.08.2017. */ @@ -38,8 +36,6 @@ public enum Blockchain { FlowDemo("FLOW/demo", "", 1.0, R.drawable.tangem2, "Flow demo"), TokenEmv("TTW", "ETH", 1.0, R.drawable.ic_logo_ethereum, "Ethereum"); - static private EnumSet payIdSupported = EnumSet.of(Blockchain.Ripple, Blockchain.Ethereum, Blockchain.Bitcoin, Blockchain.Token); - Blockchain(String ID, String currency, double multiplier, int imageResource, String officialName) { mID = ID; mCurrency = currency; @@ -129,9 +125,4 @@ public enum Blockchain { } return scheme; } - - public Boolean isPayIdSupported() { - return payIdSupported.contains(this); - } - } diff --git a/app/src/main/java/com/tangem/data/PayId.kt b/app/src/main/java/com/tangem/data/PayId.kt new file mode 100644 index 0000000000..17e2085deb --- /dev/null +++ b/app/src/main/java/com/tangem/data/PayId.kt @@ -0,0 +1,37 @@ +package com.tangem.data + +import java.util.* + +private val payIdSupported = EnumSet.of( + Blockchain.Ripple, + Blockchain.Ethereum, + Blockchain.Bitcoin, + Blockchain.Token, + Blockchain.Litecoin, + Blockchain.Stellar, + Blockchain.StellarAsset, + Blockchain.Binance, + Blockchain.Cardano, + Blockchain.Ducatus, + Blockchain.BitcoinCash, + Blockchain.Binance, + Blockchain.BinanceAsset, + Blockchain.Rootstock, + Blockchain.RootstockToken, + Blockchain.Tezos, + Blockchain.Eos, + Blockchain.Matic +) + +fun Blockchain.isPayIdSupported(): Boolean { + return payIdSupported.contains(this) +} + +fun Blockchain.getPayIdNetwork(): String { + return when (this) { + Blockchain.Ripple -> "XRPL" + Blockchain.Rootstock, Blockchain.RootstockToken -> "RSK" + else -> this.currency + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/ui/fragment/wallet/LoadedWalletFragment.kt b/app/src/main/java/com/tangem/ui/fragment/wallet/LoadedWalletFragment.kt index 25fd8604ad..b358ddebf5 100644 --- a/app/src/main/java/com/tangem/ui/fragment/wallet/LoadedWalletFragment.kt +++ b/app/src/main/java/com/tangem/ui/fragment/wallet/LoadedWalletFragment.kt @@ -25,6 +25,8 @@ import com.tangem.App import com.tangem.Constant import com.tangem.data.Blockchain import com.tangem.data.dp.PrefsManager +import com.tangem.data.getPayIdNetwork +import com.tangem.data.isPayIdSupported import com.tangem.server_android.Result import com.tangem.server_android.ServerApiTangem import com.tangem.server_android.model.CardVerifyAndGetInfo @@ -156,7 +158,7 @@ class LoadedWalletFragment : BaseFragment(), NavigationResultListener, NfcAdapte getString(R.string.loaded_wallet_load_via_qr) ) - if (ctx.blockchain.isPayIdSupported && !ctx.card.isStart2CoinCard()) { + if (ctx.blockchain.isPayIdSupported() && !ctx.card.isStart2CoinCard()) { ivPayId.visibility = View.VISIBLE ivPayId.imageAlpha = 100 ivPayId.setOnClickListener { createPayIdDialog() } @@ -326,7 +328,7 @@ class LoadedWalletFragment : BaseFragment(), NavigationResultListener, NfcAdapte } private fun getPayIdIfApplicable() { - if (ctx.blockchain.isPayIdSupported && !ctx.card.isStart2CoinCard()) { + if (ctx.blockchain.isPayIdSupported() && !ctx.card.isStart2CoinCard()) { viewModel.getPayId( Util.byteArrayToHexString(ctx.card.cid!!), Util.byteArrayToHexString(ctx.card.cardPublicKey!!)) @@ -376,13 +378,11 @@ class LoadedWalletFragment : BaseFragment(), NavigationResultListener, NfcAdapte } private fun createPayId(payId: String) { - val network = if (ctx.blockchain == Blockchain.Ripple) "XRPL" else ctx.blockchain.currency viewModel.setPayId( Util.byteArrayToHexString(ctx.card.cid!!), Util.byteArrayToHexString(ctx.card.cardPublicKey!!), payId, - ctx.coinData.wallet, - network + ctx.coinData.wallet, ctx.blockchain.getPayIdNetwork() ) .observe(viewLifecycleOwner, Observer { result -> when (result) { From a4f2bd681cee14f3d09ecc36b7b8e0b881509546 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 20 Jul 2020 18:22:12 +0300 Subject: [PATCH 3/5] Updated on 2026-08-14 --- .../java/com/tangem/ui/PrepareTransactionFragment.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/tangemAccess/java/com/tangem/ui/PrepareTransactionFragment.kt b/app/src/tangemAccess/java/com/tangem/ui/PrepareTransactionFragment.kt index 8ca425b1e7..387f992bb6 100644 --- a/app/src/tangemAccess/java/com/tangem/ui/PrepareTransactionFragment.kt +++ b/app/src/tangemAccess/java/com/tangem/ui/PrepareTransactionFragment.kt @@ -12,6 +12,7 @@ import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputMethodManager import android.widget.Toast import com.tangem.Constant +import com.tangem.data.isPayIdSupported import com.tangem.ui.activity.MainActivity import com.tangem.ui.fragment.BaseFragment import com.tangem.ui.fragment.qr.CameraPermissionManager @@ -46,7 +47,7 @@ class PrepareTransactionFragment : BaseFragment(), NavigationResultListener, Nfc Html.fromHtml(engine!!.balanceHTML) tvBalance.text = html - if (ctx.blockchain.isPayIdSupported && !ctx.card.isStart2CoinCard()) { + if (ctx.blockchain.isPayIdSupported() && !ctx.card.isStart2CoinCard()) { etWallet.hint = getString(R.string.prepare_transaction_hint_address_or_pay_id) } From 0a6264c1f971ba8335ca5a54d6e285dec3396c6d Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 20 Jul 2020 22:58:59 +0300 Subject: [PATCH 4/5] Updated on 2026-08-14 --- app/src/main/java/com/tangem/data/PayId.kt | 1 - .../tangem/data/network/ServerApiPayId.java | 13 +++ .../main/java/com/tangem/wallet/CoinData.java | 17 ++++ .../java/com/tangem/wallet/CoinEngine.java | 15 +++ .../com/tangem/wallet/bch/BtcCashEngine.java | 87 ++++++++++++++++- .../tangem/wallet/binance/BinanceEngine.java | 78 ++++++++++++++- .../java/com/tangem/wallet/btc/BtcData.java | 14 --- .../java/com/tangem/wallet/btc/BtcEngine.java | 13 +-- .../tangem/wallet/cardano/CardanoEngine.java | 91 +++++++++++++++++- .../tangem/wallet/ducatus/DucatusEngine.java | 89 +++++++++++++++++- .../java/com/tangem/wallet/eth/EthData.java | 14 --- .../java/com/tangem/wallet/eth/EthEngine.java | 13 +-- .../java/com/tangem/wallet/ltc/LtcEngine.java | 94 ++++++++++++++++++- .../java/com/tangem/wallet/rsk/RskEngine.java | 55 ++++++++++- .../com/tangem/wallet/rsk/RskTokenEngine.java | 55 ++++++++++- .../com/tangem/wallet/xlm/XlmAssetEngine.java | 83 +++++++++++++++- .../java/com/tangem/wallet/xlm/XlmEngine.java | 80 +++++++++++++++- .../java/com/tangem/wallet/xrp/XrpData.java | 14 +-- .../java/com/tangem/wallet/xrp/XrpEngine.java | 13 +-- dependencies.gradle | 2 +- 20 files changed, 736 insertions(+), 105 deletions(-) diff --git a/app/src/main/java/com/tangem/data/PayId.kt b/app/src/main/java/com/tangem/data/PayId.kt index 17e2085deb..5698d0cbdd 100644 --- a/app/src/main/java/com/tangem/data/PayId.kt +++ b/app/src/main/java/com/tangem/data/PayId.kt @@ -10,7 +10,6 @@ private val payIdSupported = EnumSet.of( Blockchain.Litecoin, Blockchain.Stellar, Blockchain.StellarAsset, - Blockchain.Binance, Blockchain.Cardano, Blockchain.Ducatus, Blockchain.BitcoinCash, diff --git a/app/src/main/java/com/tangem/data/network/ServerApiPayId.java b/app/src/main/java/com/tangem/data/network/ServerApiPayId.java index e0ebf5a849..03a8a03a68 100644 --- a/app/src/main/java/com/tangem/data/network/ServerApiPayId.java +++ b/app/src/main/java/com/tangem/data/network/ServerApiPayId.java @@ -23,9 +23,22 @@ public class ServerApiPayId { switch (blockchain) { case Ripple: return "application/xrpl-mainnet+json"; case Bitcoin: return "application/btc-mainnet+json"; + case Litecoin: return "application/ltc-mainnet+json"; + case Cardano: return "application/ada-mainnet+json"; + case Ducatus: return "application/duc-mainnet+json"; + case BitcoinCash: return "application/bch-mainnet+json"; case Ethereum: case Token: return "application/eth-mainnet+json"; + case Stellar: + case StellarAsset: + return "application/xlm-mainnet+json"; + case Binance: + case BinanceAsset: + return "application/bnb-mainnet+json"; + case Rootstock: + case RootstockToken: + return "application/rsk-mainnet+json"; default: throw new InvalidParameterException("PayID is not supported for " + blockchain.getOfficialName()); } } diff --git a/app/src/main/java/com/tangem/wallet/CoinData.java b/app/src/main/java/com/tangem/wallet/CoinData.java index 2e46eacc86..67dba01d34 100644 --- a/app/src/main/java/com/tangem/wallet/CoinData.java +++ b/app/src/main/java/com/tangem/wallet/CoinData.java @@ -58,6 +58,9 @@ public abstract class CoinData { if (B.containsKey("sentTransactionsCount")) { sentTransactionsCount = B.getInt("sentTransactionsCount"); } + + if (B.containsKey("ResolvedPayIdAddress")) resolvedPayIdAddress = B.getString("ResolvedPayIdAddress"); + else resolvedPayIdAddress = null; } public void saveToBundle(Bundle B) { @@ -76,6 +79,8 @@ public abstract class CoinData { B.putString("validationNodeDescription", validationNodeDescription); B.putInt("sentTransactionsCount", sentTransactionsCount); + + if (resolvedPayIdAddress != null) B.putString("ResolvedPayIdAddress", resolvedPayIdAddress); } catch (Exception e) { Log.e("Can't save to bundle ", e.getMessage()); } @@ -155,6 +160,7 @@ public abstract class CoinData { rate = 0f; rateAlter = 0f; sentTransactionsCount = 0; + resolvedPayIdAddress = null; } // private AtomicInteger failedBalanceRequestCounter; @@ -212,4 +218,15 @@ public abstract class CoinData { public void incSentTransactionsCount() { sentTransactionsCount++; } + + private String resolvedPayIdAddress = null; + + public String getResolvedPayIdAddress() { + return resolvedPayIdAddress; + } + + public void setResolvedPayIdAddress(String resolvedPayIdAddress) { + this.resolvedPayIdAddress = resolvedPayIdAddress; + } + } diff --git a/app/src/main/java/com/tangem/wallet/CoinEngine.java b/app/src/main/java/com/tangem/wallet/CoinEngine.java index edf38cad4c..9e30d2df6f 100644 --- a/app/src/main/java/com/tangem/wallet/CoinEngine.java +++ b/app/src/main/java/com/tangem/wallet/CoinEngine.java @@ -11,6 +11,7 @@ import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; +import java.net.URL; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.text.DecimalFormat; @@ -392,4 +393,18 @@ public abstract class CoinEngine { public int pendingTransactionTimeoutInSeconds() { return 30; } + protected boolean validatePayId(String payId) { + String[] addressParts = payId.split("\\$"); + + if (addressParts.length != 2) { + return false; + } + String addressURL = "https://" + addressParts[1] + "/" + addressParts[0]; + try { + new URL(addressURL).toURI(); + return true; + } catch (Exception e) { + return false; + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/wallet/bch/BtcCashEngine.java b/app/src/main/java/com/tangem/wallet/bch/BtcCashEngine.java index 549ef88649..8f1ec5fc5e 100644 --- a/app/src/main/java/com/tangem/wallet/bch/BtcCashEngine.java +++ b/app/src/main/java/com/tangem/wallet/bch/BtcCashEngine.java @@ -6,11 +6,14 @@ import android.util.Log; import com.tangem.App; import com.tangem.data.network.ServerApiBlockchair; +import com.tangem.data.network.ServerApiPayId; import com.tangem.data.network.model.BlockchairAddressData; import com.tangem.data.network.model.BlockchairAddressResponse; import com.tangem.data.network.model.BlockchairStatsResponse; import com.tangem.data.network.model.BlockchairTransactionResponse; import com.tangem.data.network.model.BlockchairUnspentOutput; +import com.tangem.data.network.model.PayIdAddress; +import com.tangem.data.network.model.PayIdResponse; import com.tangem.tangem_card.data.TangemCard; import com.tangem.tangem_card.reader.CardProtocol; import com.tangem.tangem_card.tasks.SignTask; @@ -130,6 +133,10 @@ public class BtcCashEngine extends CoinEngine { @Override public boolean validateAddress(String address) { + if (address != null && address.contains("$")) { // PayID + return validatePayId(address); + } + return CashAddr.isValidCashAddress(address); } @@ -373,8 +380,16 @@ public class BtcCashEngine extends CoinEngine { public SignTask.TransactionToSign constructTransaction(Amount amountValue, Amount feeValue, boolean IncFee, String targetAddress) throws Exception { checkBlockchainDataExists(); + String destination; + //PayID + if (coinData.getResolvedPayIdAddress() != null) { + destination = coinData.getResolvedPayIdAddress(); + } else { + destination = targetAddress; + } + String srcLegacyAddress = convertToLegacyAddress(ctx.getCoinData().getWallet()); - String destLegacyAddress = convertToLegacyAddress(targetAddress); + String destLegacyAddress = convertToLegacyAddress(destination); byte[] pbKey = ctx.getCard().getWalletPublicKeyRar(); //ALWAYS USING COMPRESS KEY final ArrayList unspentOutputs = new ArrayList<>(); @@ -576,8 +591,38 @@ public class BtcCashEngine extends CoinEngine { @Override public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) throws Exception { - final int calcSize = calculateEstimatedTransactionSize(targetAddress, amount.toValueString()); - Log.e(TAG, String.format("Estimated tx size %d", calcSize)); + + CompletableObserver payIdObserver = new DisposableCompletableObserver() { + @Override + public void onComplete() { + int calcSize = calculateEstimatedTransactionSize(coinData.getResolvedPayIdAddress(), amount.toValueString()); + Log.e(TAG, String.format("Estimated tx size %d", calcSize)); + + checkFee(blockchainRequestsCallbacks, calcSize); + + blockchainRequestsCallbacks.onComplete(true); + } + + @Override + public void onError(Throwable e) { + ctx.setError(e.getMessage()); + blockchainRequestsCallbacks.onComplete(false); + } + }; + + if (targetAddress.contains("$")) { // PayID + resolvePayID(targetAddress, payIdObserver); + } else { + int calcSize = calculateEstimatedTransactionSize(targetAddress, amount.toValueString()); + Log.e(TAG, String.format("Estimated tx size %d", calcSize)); + + checkFee(blockchainRequestsCallbacks, calcSize); + + blockchainRequestsCallbacks.onComplete(true); + } + } + + private void checkFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, int calcSize) { coinData.minFee = null; coinData.maxFee = null; coinData.normalFee = null; @@ -618,6 +663,42 @@ public class BtcCashEngine extends CoinEngine { serverApiBlockchair.getStats(statsObserver); } + private void resolvePayID(String targetAddress, CompletableObserver observer) { + final ServerApiPayId serverApiPayId = new ServerApiPayId(); + + SingleObserver payIdObserver = new DisposableSingleObserver() { + @Override + public void onSuccess(PayIdResponse payIdResponse) { + try { + String resolvedAddress = null; + for (PayIdAddress address : payIdResponse.getAddresses()) { + if (address.getPaymentNetwork().equals(ctx.getBlockchain().getCurrency()) && + address.getEnvironment().equals("MAINNET")) { + resolvedAddress = address.getAddressDetails().getAddress(); + break; + } + } + if (validateAddress(resolvedAddress)) { + coinData.setResolvedPayIdAddress(resolvedAddress); + observer.onComplete(); + } else { + observer.onError(new Exception("Unknown address format in PayID response")); + } + } catch (Exception e) { + observer.onError(new Exception("Unknown response format on PayID request")); + } + } + + @Override + public void onError(Throwable e) { + Log.i(TAG, "onFail: " + "payID" + " " + e.getMessage()); + observer.onError(new Exception("PayID error:" + e.getMessage())); + } + }; + + serverApiPayId.getAddress(targetAddress, ctx.getBlockchain(), payIdObserver); + } + @Override public void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) throws Exception { final ServerApiBlockchair serverApiBlockchair = new ServerApiBlockchair(ctx.getBlockchain()); diff --git a/app/src/main/java/com/tangem/wallet/binance/BinanceEngine.java b/app/src/main/java/com/tangem/wallet/binance/BinanceEngine.java index ca6286a35d..a8f459b4bc 100644 --- a/app/src/main/java/com/tangem/wallet/binance/BinanceEngine.java +++ b/app/src/main/java/com/tangem/wallet/binance/BinanceEngine.java @@ -11,7 +11,10 @@ import com.tangem.data.Blockchain; import com.tangem.data.network.BinanceApi; import com.tangem.data.network.Server; import com.tangem.data.network.ServerApiBinance; +import com.tangem.data.network.ServerApiPayId; import com.tangem.data.network.model.BinanceFees; +import com.tangem.data.network.model.PayIdAddress; +import com.tangem.data.network.model.PayIdResponse; import com.tangem.tangem_card.data.TangemCard; import com.tangem.tangem_card.reader.CardProtocol; import com.tangem.tangem_card.tasks.SignTask; @@ -44,6 +47,10 @@ import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.List; +import io.reactivex.CompletableObserver; +import io.reactivex.SingleObserver; +import io.reactivex.observers.DisposableCompletableObserver; +import io.reactivex.observers.DisposableSingleObserver; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; @@ -147,6 +154,10 @@ public class BinanceEngine extends CoinEngine { return false; } + if (address.contains("$")) { // PayID + return validatePayId(address); + } + try { Crypto.decodeAddress(address); } catch (Exception e) { @@ -350,6 +361,14 @@ public class BinanceEngine extends CoinEngine { public SignTask.TransactionToSign constructTransaction(Amount amountValue, Amount feeValue, boolean IncFee, String targetAddress) throws Exception { checkBlockchainDataExists(); + String destination; + //PayID + if (coinData.getResolvedPayIdAddress() != null) { + destination = coinData.getResolvedPayIdAddress(); + } else { + destination = targetAddress; + } + String amount; if (IncFee) { @@ -368,7 +387,7 @@ public class BinanceEngine extends CoinEngine { Transfer transfer = new Transfer(); transfer.setCoin("BNB"); transfer.setFromAddress(ctx.getCoinData().getWallet()); - transfer.setToAddress(targetAddress); + transfer.setToAddress(destination); transfer.setAmount(amount); TransactionOption options = TransactionOption.DEFAULT_INSTANCE; @@ -466,6 +485,27 @@ public class BinanceEngine extends CoinEngine { } public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) { + CompletableObserver payIdObserver = new DisposableCompletableObserver() { + @Override + public void onComplete() { + checkFee(blockchainRequestsCallbacks); + } + + @Override + public void onError(Throwable e) { + ctx.setError(e.getMessage()); + blockchainRequestsCallbacks.onComplete(false); + } + }; + + if (targetAddress.contains("$")) { // PayID + resolvePayID(targetAddress, payIdObserver); + } else { + checkFee(blockchainRequestsCallbacks); + } + } + + private void checkFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks) { try { String baseUrl; @@ -519,6 +559,42 @@ public class BinanceEngine extends CoinEngine { } } + private void resolvePayID(String targetAddress, CompletableObserver observer) { + final ServerApiPayId serverApiPayId = new ServerApiPayId(); + + SingleObserver payIdObserver = new DisposableSingleObserver() { + @Override + public void onSuccess(PayIdResponse payIdResponse) { + try { + String resolvedAddress = null; + for (PayIdAddress address : payIdResponse.getAddresses()) { + if (address.getPaymentNetwork().equals(ctx.getBlockchain().getCurrency()) && + address.getEnvironment().equals("MAINNET")) { + resolvedAddress = address.getAddressDetails().getAddress(); + break; + } + } + if (validateAddress(resolvedAddress)) { + coinData.setResolvedPayIdAddress(resolvedAddress); + observer.onComplete(); + } else { + observer.onError(new Exception("Unknown address format in PayID response")); + } + } catch (Exception e) { + observer.onError(new Exception("Unknown response format on PayID request")); + } + } + + @Override + public void onError(Throwable e) { + Log.i(TAG, "onFail: " + "payID" + " " + e.getMessage()); + observer.onError(new Exception("PayID error:" + e.getMessage())); + } + }; + + serverApiPayId.getAddress(targetAddress, ctx.getBlockchain(), payIdObserver); + } + public void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) { // RequestBody requestBody = TransactionRequestAssemblerExtSign.createRequestBody(txForSend); try { diff --git a/app/src/main/java/com/tangem/wallet/btc/BtcData.java b/app/src/main/java/com/tangem/wallet/btc/BtcData.java index 1a892adee9..28648010df 100644 --- a/app/src/main/java/com/tangem/wallet/btc/BtcData.java +++ b/app/src/main/java/com/tangem/wallet/btc/BtcData.java @@ -22,8 +22,6 @@ public class BtcData extends CoinData { //for blockchain.info private boolean hasUnconfirmed = false; - private String resolvedPayIdAddress = null; - public Unspents getUnspentInputsDescription() { try { int gatheredUnspents = 0; @@ -92,8 +90,6 @@ public class BtcData extends CoinData { } if (B.containsKey("UseBlockcypher")) useBlockcypher = B.getBoolean("UseBlockcypher"); if (B.containsKey("HasUnconfirmed")) useBlockcypher = B.getBoolean("HasUnconfirmed"); - if (B.containsKey("ResolvedPayIdAddress")) resolvedPayIdAddress = B.getString("ResolvedPayIdAddress"); - else resolvedPayIdAddress = null; } @Override @@ -111,7 +107,6 @@ public class BtcData extends CoinData { if (balanceUnconfirmed != null) B.putLong("BalanceUnconfirmed", balanceUnconfirmed); if (useBlockcypher) B.putBoolean("UseBlockcypher", true); if (hasUnconfirmed) B.putBoolean("HasUnconfirmed", true); - if (resolvedPayIdAddress != null) B.putString("ResolvedPayIdAddress", resolvedPayIdAddress); } catch (Exception e) { Log.e("Can't save to bundle ", e.getMessage()); } @@ -124,7 +119,6 @@ public class BtcData extends CoinData { balanceUnconfirmed = null; unspentTransactions = null; hasUnconfirmed = false; - resolvedPayIdAddress = null; } public CoinEngine.InternalAmount getBalanceInInternalUnits() { @@ -166,12 +160,4 @@ public class BtcData extends CoinData { public void setHasUnconfirmed(boolean hasUnconfirmed) { this.hasUnconfirmed = hasUnconfirmed; } - - public String getResolvedPayIdAddress() { - return resolvedPayIdAddress; - } - - public void setResolvedPayIdAddress(String resolvedPayIdAddress) { - this.resolvedPayIdAddress = resolvedPayIdAddress; - } } diff --git a/app/src/main/java/com/tangem/wallet/btc/BtcEngine.java b/app/src/main/java/com/tangem/wallet/btc/BtcEngine.java index df6b877f98..062f544334 100644 --- a/app/src/main/java/com/tangem/wallet/btc/BtcEngine.java +++ b/app/src/main/java/com/tangem/wallet/btc/BtcEngine.java @@ -161,18 +161,7 @@ public class BtcEngine extends CoinEngine { } if (address.contains("$")) { // PayID - String[] addressParts = address.split("\\$"); - - if (addressParts.length != 2) { - return false; - } - String addressURL = "https://" + addressParts[1] + "/" + addressParts[0]; - try { - new URL(addressURL).toURI(); - return true; - } catch (Exception e) { - return false; - } + return validatePayId(address); } if (address.startsWith("1") || address.startsWith("2") || address.startsWith("3") || address.startsWith("n") || address.startsWith("m")) { diff --git a/app/src/main/java/com/tangem/wallet/cardano/CardanoEngine.java b/app/src/main/java/com/tangem/wallet/cardano/CardanoEngine.java index 1614b760ca..68037e128b 100644 --- a/app/src/main/java/com/tangem/wallet/cardano/CardanoEngine.java +++ b/app/src/main/java/com/tangem/wallet/cardano/CardanoEngine.java @@ -9,10 +9,14 @@ import com.tangem.App; import com.tangem.Constant; import com.tangem.data.local.PendingTransactionsStorage; import com.tangem.data.network.ServerApiAdalite; +import com.tangem.data.network.ServerApiBlockcypher; +import com.tangem.data.network.ServerApiPayId; import com.tangem.data.network.model.AdaliteResponse; import com.tangem.data.network.model.AdaliteResponseUtxo; import com.tangem.data.network.model.AdaliteTxData; import com.tangem.data.network.model.AdaliteUtxoData; +import com.tangem.data.network.model.PayIdAddress; +import com.tangem.data.network.model.PayIdResponse; import com.tangem.tangem_card.data.TangemCard; import com.tangem.tangem_card.tasks.SignTask; import com.tangem.tangem_card.util.Util; @@ -46,6 +50,10 @@ import co.nstant.in.cbor.model.Array; import co.nstant.in.cbor.model.ByteString; import co.nstant.in.cbor.model.DataItem; import co.nstant.in.cbor.model.UnsignedInteger; +import io.reactivex.CompletableObserver; +import io.reactivex.SingleObserver; +import io.reactivex.observers.DisposableCompletableObserver; +import io.reactivex.observers.DisposableSingleObserver; import static com.tangem.wallet.Base58.decodeBase58; import static com.tangem.wallet.Base58.encodeBase58; @@ -143,6 +151,10 @@ public class CardanoEngine extends CoinEngine { return false; } + if (address.contains("$")) { // PayID + return validatePayId(address); + } + byte[] decAddress = Base58.decodeBase58(address); if (decAddress == null || decAddress.length == 0) { @@ -399,6 +411,14 @@ public class CardanoEngine extends CoinEngine { public SignTask.TransactionToSign constructTransaction(Amount amountValue, Amount feeValue, boolean IncFee, String targetAddress) throws Exception { checkBlockchainDataExists(); + String destination; + //PayID + if (coinData.getResolvedPayIdAddress() != null) { + destination = coinData.getResolvedPayIdAddress(); + } else { + destination = targetAddress; + } + String myAddress = ctx.getCoinData().getWallet(); byte[] pbKey = ctx.getCard().getWalletPublicKey(); List utxoList = coinData.getUnspentOutputs(); @@ -457,7 +477,7 @@ public class CardanoEngine extends CoinEngine { } //1st output - DataItem targetAddressItem = new CborDecoder(new ByteArrayInputStream(decodeBase58(targetAddress))).decode().get(0); + DataItem targetAddressItem = new CborDecoder(new ByteArrayInputStream(decodeBase58(destination))).decode().get(0); outputsArray .addArray() .add(targetAddressItem) @@ -697,9 +717,40 @@ public class CardanoEngine extends CoinEngine { @Override public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) { +// int calcSize = calculateEstimatedTransactionSize(targetAddress, amount); + CompletableObserver payIdObserver = new DisposableCompletableObserver() { + @Override + public void onComplete() { + int calcSize = calculateEstimatedTransactionSize(coinData.getResolvedPayIdAddress(), amount); + Log.e(TAG, String.format("Estimated tx size %d", calcSize)); + + calculateFee(calcSize); + + blockchainRequestsCallbacks.onComplete(true); + } + + @Override + public void onError(Throwable e) { + ctx.setError(e.getMessage()); + blockchainRequestsCallbacks.onComplete(false); + } + }; + + if (targetAddress.contains("$")) { // PayID + resolvePayID(targetAddress, payIdObserver); + } else { + int calcSize = calculateEstimatedTransactionSize(targetAddress, amount); + Log.e(TAG, String.format("Estimated tx size %d", calcSize)); + + calculateFee(calcSize); + + blockchainRequestsCallbacks.onComplete(true); + } + } + + private void calculateFee(int calcSize) { final double A = 0.155381; final double B = 0.000043946; - int calcSize = calculateEstimatedTransactionSize(targetAddress, amount); double fee = A + B * calcSize; Amount feeAmount = new Amount(new BigDecimal(fee).setScale(getDecimals(), RoundingMode.UP), getFeeCurrency()); @@ -707,8 +758,42 @@ public class CardanoEngine extends CoinEngine { coinData.minFee = feeAmount; coinData.normalFee = feeAmount; coinData.maxFee = feeAmount; + } - blockchainRequestsCallbacks.onComplete(true); + private void resolvePayID(String targetAddress, CompletableObserver observer) { + final ServerApiPayId serverApiPayId = new ServerApiPayId(); + + SingleObserver payIdObserver = new DisposableSingleObserver() { + @Override + public void onSuccess(PayIdResponse payIdResponse) { + try { + String resolvedAddress = null; + for (PayIdAddress address : payIdResponse.getAddresses()) { + if (address.getPaymentNetwork().equals(ctx.getBlockchain().getCurrency()) && + address.getEnvironment().equals("MAINNET")) { + resolvedAddress = address.getAddressDetails().getAddress(); + break; + } + } + if (validateAddress(resolvedAddress)) { + coinData.setResolvedPayIdAddress(resolvedAddress); + observer.onComplete(); + } else { + observer.onError(new Exception("Unknown address format in PayID response")); + } + } catch (Exception e) { + observer.onError(new Exception("Unknown response format on PayID request")); + } + } + + @Override + public void onError(Throwable e) { + Log.i(TAG, "onFail: " + "payID" + " " + e.getMessage()); + observer.onError(new Exception("PayID error:" + e.getMessage())); + } + }; + + serverApiPayId.getAddress(targetAddress, ctx.getBlockchain(), payIdObserver); } @Override diff --git a/app/src/main/java/com/tangem/wallet/ducatus/DucatusEngine.java b/app/src/main/java/com/tangem/wallet/ducatus/DucatusEngine.java index f346435773..307adbee55 100644 --- a/app/src/main/java/com/tangem/wallet/ducatus/DucatusEngine.java +++ b/app/src/main/java/com/tangem/wallet/ducatus/DucatusEngine.java @@ -6,9 +6,12 @@ import android.util.Log; import com.tangem.App; import com.tangem.data.network.ServerApiBitcore; +import com.tangem.data.network.ServerApiPayId; import com.tangem.data.network.model.BitcoreBalanceAndUnspents; import com.tangem.data.network.model.BitcoreSendResponse; import com.tangem.data.network.model.BitcoreUtxo; +import com.tangem.data.network.model.PayIdAddress; +import com.tangem.data.network.model.PayIdResponse; import com.tangem.tangem_card.data.TangemCard; import com.tangem.tangem_card.reader.CardProtocol; import com.tangem.tangem_card.tasks.SignTask; @@ -37,7 +40,9 @@ import java.security.NoSuchProviderException; import java.util.ArrayList; import java.util.Arrays; +import io.reactivex.CompletableObserver; import io.reactivex.SingleObserver; +import io.reactivex.observers.DisposableCompletableObserver; import io.reactivex.observers.DisposableSingleObserver; public class DucatusEngine extends BtcEngine { @@ -131,6 +136,10 @@ public class DucatusEngine extends BtcEngine { return false; } + if (address.contains("$")) { // PayID + return validatePayId(address); + } + if (address.length() < 25) { return false; } @@ -397,6 +406,14 @@ public class DucatusEngine extends BtcEngine { ArrayList unspentOutputs = new ArrayList<>(); checkBlockchainDataExists(); + String destination; + //PayID + if (coinData.getResolvedPayIdAddress() != null) { + destination = coinData.getResolvedPayIdAddress(); + } else { + destination = targetAddress; + } + String myAddress = ctx.getCoinData().getWallet(); byte[] pbKey = ctx.getCard().getWalletPublicKey(); @@ -430,7 +447,7 @@ public class DucatusEngine extends BtcEngine { final byte[][] bodyHash = new byte[unspentOutputs.size()][]; for (int i = 0; i < unspentOutputs.size(); ++i) { - txForSign[i] = BTCUtils.buildTXForSign(myAddress, targetAddress, myAddress, unspentOutputs, i, amount, change); + txForSign[i] = BTCUtils.buildTXForSign(myAddress, destination, myAddress, unspentOutputs, i, amount, change); bodyHash[i] = Util.calculateSHA256(txForSign[i]); bodyDoubleHash[i] = Util.calculateSHA256(bodyHash[i]); } @@ -483,7 +500,7 @@ public class DucatusEngine extends BtcEngine { unspentOutputs.get(i).scriptForBuild = DerEncodingUtil.packSignDer(r, s, pbKey); } - byte[] txForSend = BTCUtils.buildTXForSend(targetAddress, myAddress, unspentOutputs, amountFinal, changeFinal); + byte[] txForSend = BTCUtils.buildTXForSend(destination, myAddress, unspentOutputs, amountFinal, changeFinal); notifyOnNeedSendTransaction(txForSend); return txForSend; } @@ -535,14 +552,76 @@ public class DucatusEngine extends BtcEngine { @Override public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) throws Exception { - final int calcSize = calculateEstimatedTransactionSize(targetAddress, amount.toValueString()); - Log.e(TAG, String.format("Estimated tx size %d", calcSize)); + CompletableObserver payIdObserver = new DisposableCompletableObserver() { + @Override + public void onComplete() { + int calcSize = calculateEstimatedTransactionSize(coinData.getResolvedPayIdAddress(), amount.toValueString()); + Log.e(TAG, String.format("Estimated tx size %d", calcSize)); + calculateFee(calcSize); + + blockchainRequestsCallbacks.onComplete(true); + } + + @Override + public void onError(Throwable e) { + ctx.setError(e.getMessage()); + blockchainRequestsCallbacks.onComplete(false); + } + }; + + if (targetAddress.contains("$")) { // PayID + resolvePayID(targetAddress, payIdObserver); + } else { + int calcSize = calculateEstimatedTransactionSize(targetAddress, amount.toValueString()); + Log.e(TAG, String.format("Estimated tx size %d", calcSize)); + + calculateFee(calcSize); + + blockchainRequestsCallbacks.onComplete(true); + } + } + + private void calculateFee(int calcSize) { coinData.minFee = new Amount(BigDecimal.valueOf(calcSize).multiply(BigDecimal.valueOf(0.00000089)), ctx.getBlockchain().getCurrency()); //fee for byte from Ducatus wallet for android coinData.normalFee = new Amount(BigDecimal.valueOf(calcSize).multiply(BigDecimal.valueOf(0.00000144)), ctx.getBlockchain().getCurrency()); coinData.maxFee = new Amount(BigDecimal.valueOf(calcSize).multiply(BigDecimal.valueOf(0.00000350)), ctx.getBlockchain().getCurrency()); + } - blockchainRequestsCallbacks.onComplete(true); + private void resolvePayID(String targetAddress, CompletableObserver observer) { + final ServerApiPayId serverApiPayId = new ServerApiPayId(); + + SingleObserver payIdObserver = new DisposableSingleObserver() { + @Override + public void onSuccess(PayIdResponse payIdResponse) { + try { + String resolvedAddress = null; + for (PayIdAddress address : payIdResponse.getAddresses()) { + if (address.getPaymentNetwork().equals(ctx.getBlockchain().getCurrency()) && + address.getEnvironment().equals("MAINNET")) { + resolvedAddress = address.getAddressDetails().getAddress(); + break; + } + } + if (validateAddress(resolvedAddress)) { + coinData.setResolvedPayIdAddress(resolvedAddress); + observer.onComplete(); + } else { + observer.onError(new Exception("Unknown address format in PayID response")); + } + } catch (Exception e) { + observer.onError(new Exception("Unknown response format on PayID request")); + } + } + + @Override + public void onError(Throwable e) { + Log.i(TAG, "onFail: " + "payID" + " " + e.getMessage()); + observer.onError(new Exception("PayID error:" + e.getMessage())); + } + }; + + serverApiPayId.getAddress(targetAddress, ctx.getBlockchain(), payIdObserver); } @Override diff --git a/app/src/main/java/com/tangem/wallet/eth/EthData.java b/app/src/main/java/com/tangem/wallet/eth/EthData.java index 73511313ca..92611d8d01 100644 --- a/app/src/main/java/com/tangem/wallet/eth/EthData.java +++ b/app/src/main/java/com/tangem/wallet/eth/EthData.java @@ -14,8 +14,6 @@ public class EthData extends CoinData { private BigInteger countConfirmedTX = null; private BigInteger countUnconfirmedTX = BigInteger.valueOf(0); - private String resolvedPayIdAddress = null; - public BigInteger getConfirmedTXCount() { if (countConfirmedTX == null) { countConfirmedTX = BigInteger.valueOf(0); @@ -48,7 +46,6 @@ public class EthData extends CoinData { public void clearInfo() { super.clearInfo(); balance = null; - resolvedPayIdAddress = null; } public CoinEngine.InternalAmount getBalanceInInternalUnits() { @@ -60,14 +57,6 @@ public class EthData extends CoinData { balance = value; } - public String getResolvedPayIdAddress() { - return resolvedPayIdAddress; - } - - public void setResolvedPayIdAddress(String resolvedPayIdAddress) { - this.resolvedPayIdAddress = resolvedPayIdAddress; - } - @Override public void loadFromBundle(Bundle B) { super.loadFromBundle(B); @@ -83,8 +72,6 @@ public class EthData extends CoinData { countConfirmedTX = new BigInteger(B.getString("confirmTx"), 16); if (B.containsKey("unconfirmTx")) countUnconfirmedTX = new BigInteger(B.getString("unconfirmTx"), 16); - if (B.containsKey("ResolvedPayIdAddress")) resolvedPayIdAddress = B.getString("ResolvedPayIdAddress"); - else resolvedPayIdAddress = null; } @Override @@ -98,7 +85,6 @@ public class EthData extends CoinData { B.putString("confirmTx", getConfirmedTXCount().toString(16)); B.putString("unconfirmTx", getUnconfirmedTXCount().toString(16)); - if (resolvedPayIdAddress != null) B.putString("ResolvedPayIdAddress", resolvedPayIdAddress); } catch (Exception e) { Log.e("Can't save to bundle ", e.getMessage()); diff --git a/app/src/main/java/com/tangem/wallet/eth/EthEngine.java b/app/src/main/java/com/tangem/wallet/eth/EthEngine.java index 0567b5bf47..a0aae17a7d 100644 --- a/app/src/main/java/com/tangem/wallet/eth/EthEngine.java +++ b/app/src/main/java/com/tangem/wallet/eth/EthEngine.java @@ -145,18 +145,7 @@ public class EthEngine extends CoinEngine { } if (address.contains("$")) { // PayID - String[] addressParts = address.split("\\$"); - - if (addressParts.length != 2) { - return false; - } - String addressURL = "https://" + addressParts[1] + "/" + addressParts[0]; - try { - new URL(addressURL).toURI(); - return true; - } catch (Exception e) { - return false; - } + return validatePayId(address); } if (!address.startsWith("0x") && !address.startsWith("0X")) { diff --git a/app/src/main/java/com/tangem/wallet/ltc/LtcEngine.java b/app/src/main/java/com/tangem/wallet/ltc/LtcEngine.java index d5020b0fef..6a303a449d 100644 --- a/app/src/main/java/com/tangem/wallet/ltc/LtcEngine.java +++ b/app/src/main/java/com/tangem/wallet/ltc/LtcEngine.java @@ -8,10 +8,13 @@ import com.tangem.App; import com.tangem.data.local.PendingTransactionsStorage; import com.tangem.data.network.Server; import com.tangem.data.network.ServerApiBlockcypher; +import com.tangem.data.network.ServerApiPayId; import com.tangem.data.network.model.BlockcypherFee; import com.tangem.data.network.model.BlockcypherResponse; import com.tangem.data.network.model.BlockcypherTx; import com.tangem.data.network.model.BlockcypherTxref; +import com.tangem.data.network.model.PayIdAddress; +import com.tangem.data.network.model.PayIdResponse; import com.tangem.tangem_card.data.TangemCard; import com.tangem.tangem_card.reader.CardProtocol; import com.tangem.tangem_card.tasks.SignTask; @@ -42,6 +45,11 @@ import java.security.NoSuchProviderException; import java.util.ArrayList; import java.util.Arrays; +import io.reactivex.CompletableObserver; +import io.reactivex.SingleObserver; +import io.reactivex.observers.DisposableCompletableObserver; +import io.reactivex.observers.DisposableSingleObserver; + public class LtcEngine extends BtcEngine { private static final String TAG = LtcEngine.class.getSimpleName(); public BtcData coinData = null; @@ -133,6 +141,10 @@ public class LtcEngine extends BtcEngine { return false; } + if (address.contains("$")) { // PayID + return validatePayId(address); + } + if (address.length() < 25) { return false; } @@ -403,6 +415,14 @@ public class LtcEngine extends BtcEngine { ArrayList unspentOutputs = new ArrayList<>(); checkBlockchainDataExists(); + String destination; + //PayID + if (coinData.getResolvedPayIdAddress() != null) { + destination = coinData.getResolvedPayIdAddress(); + } else { + destination = targetAddress; + } + String myAddress = ctx.getCoinData().getWallet(); byte[] pbKey = ctx.getCard().getWalletPublicKey(); @@ -436,7 +456,7 @@ public class LtcEngine extends BtcEngine { final byte[][] bodyHash = new byte[unspentOutputs.size()][]; for (int i = 0; i < unspentOutputs.size(); ++i) { - txForSign[i] = BTCUtils.buildTXForSign(myAddress, targetAddress, myAddress, unspentOutputs, i, amount, change); + txForSign[i] = BTCUtils.buildTXForSign(myAddress, destination, myAddress, unspentOutputs, i, amount, change); bodyHash[i] = Util.calculateSHA256(txForSign[i]); bodyDoubleHash[i] = Util.calculateSHA256(bodyHash[i]); } @@ -489,7 +509,7 @@ public class LtcEngine extends BtcEngine { unspentOutputs.get(i).scriptForBuild = DerEncodingUtil.packSignDer(r, s, pbKey); } - byte[] txForSend = BTCUtils.buildTXForSend(targetAddress, myAddress, unspentOutputs, amountFinal, changeFinal); + byte[] txForSend = BTCUtils.buildTXForSend(destination, myAddress, unspentOutputs, amountFinal, changeFinal); notifyOnNeedSendTransaction(txForSend); return txForSend; } @@ -632,8 +652,38 @@ public class LtcEngine extends BtcEngine { @Override public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) throws Exception { - final int calcSize = calculateEstimatedTransactionSize(targetAddress, amount.toValueString()); - Log.e(TAG, String.format("Estimated tx size %d", calcSize)); + + CompletableObserver payIdObserver = new DisposableCompletableObserver() { + @Override + public void onComplete() { + int calcSize = calculateEstimatedTransactionSize(coinData.getResolvedPayIdAddress(), amount.toValueString()); + Log.e(TAG, String.format("Estimated tx size %d", calcSize)); + + checkFee(blockchainRequestsCallbacks, calcSize); + + blockchainRequestsCallbacks.onComplete(true); + } + + @Override + public void onError(Throwable e) { + ctx.setError(e.getMessage()); + blockchainRequestsCallbacks.onComplete(false); + } + }; + + if (targetAddress.contains("$")) { // PayID + resolvePayID(targetAddress, payIdObserver); + } else { + int calcSize = calculateEstimatedTransactionSize(targetAddress, amount.toValueString()); + Log.e(TAG, String.format("Estimated tx size %d", calcSize)); + + checkFee(blockchainRequestsCallbacks, calcSize); + + blockchainRequestsCallbacks.onComplete(true); + } + } + + private void checkFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, int calcSize) { coinData.minFee = null; coinData.maxFee = null; coinData.normalFee = null; @@ -692,6 +742,42 @@ public class LtcEngine extends BtcEngine { serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_FEE, "", ""); } + private void resolvePayID(String targetAddress, CompletableObserver observer) { + final ServerApiPayId serverApiPayId = new ServerApiPayId(); + + SingleObserver payIdObserver = new DisposableSingleObserver() { + @Override + public void onSuccess(PayIdResponse payIdResponse) { + try { + String resolvedAddress = null; + for (PayIdAddress address : payIdResponse.getAddresses()) { + if (address.getPaymentNetwork().equals(ctx.getBlockchain().getCurrency()) && + address.getEnvironment().equals("MAINNET")) { + resolvedAddress = address.getAddressDetails().getAddress(); + break; + } + } + if (validateAddress(resolvedAddress)) { + coinData.setResolvedPayIdAddress(resolvedAddress); + observer.onComplete(); + } else { + observer.onError(new Exception("Unknown address format in PayID response")); + } + } catch (Exception e) { + observer.onError(new Exception("Unknown response format on PayID request")); + } + } + + @Override + public void onError(Throwable e) { + Log.i(TAG, "onFail: " + "payID" + " " + e.getMessage()); + observer.onError(new Exception("PayID error:" + e.getMessage())); + } + }; + + serverApiPayId.getAddress(targetAddress, ctx.getBlockchain(), payIdObserver); + } + @Override public void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) throws Exception { final String txStr = BTCUtils.toHex(txForSend); diff --git a/app/src/main/java/com/tangem/wallet/rsk/RskEngine.java b/app/src/main/java/com/tangem/wallet/rsk/RskEngine.java index 27614e94ea..0e981ad42e 100644 --- a/app/src/main/java/com/tangem/wallet/rsk/RskEngine.java +++ b/app/src/main/java/com/tangem/wallet/rsk/RskEngine.java @@ -4,8 +4,11 @@ import android.net.Uri; import android.util.Log; import com.tangem.data.Blockchain; +import com.tangem.data.network.ServerApiPayId; import com.tangem.data.network.ServerApiRootstock; import com.tangem.data.network.model.InfuraResponse; +import com.tangem.data.network.model.PayIdAddress; +import com.tangem.data.network.model.PayIdResponse; import com.tangem.wallet.BTCUtils; import com.tangem.wallet.CoinEngine; import com.tangem.wallet.EthTransaction; @@ -15,6 +18,10 @@ import com.tangem.wallet.eth.EthEngine; import java.math.BigInteger; +import io.reactivex.CompletableObserver; +import io.reactivex.SingleObserver; +import io.reactivex.observers.DisposableSingleObserver; + public class RskEngine extends EthEngine { private static final String TAG = RskEngine.class.getSimpleName(); @@ -132,6 +139,7 @@ public class RskEngine extends EthEngine { @Override public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) { ServerApiRootstock serverApiRootstock = new ServerApiRootstock(); + final ServerApiPayId serverApiPayId = new ServerApiPayId(); // request requestData gasPrice listener ServerApiRootstock.ResponseListener responseListener = new ServerApiRootstock.ResponseListener() { @Override @@ -162,7 +170,11 @@ public class RskEngine extends EthEngine { Log.i(TAG, "normal fee: " + coinData.normalFee.toString()); Log.i(TAG, "max fee : " + coinData.maxFee.toString()); - blockchainRequestsCallbacks.onComplete(true); + if (serverApiRootstock.isRequestsSequenceCompleted() && serverApiPayId.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(!ctx.hasError()); + } else { + blockchainRequestsCallbacks.onProgress(); + } } @Override @@ -173,6 +185,47 @@ public class RskEngine extends EthEngine { }; serverApiRootstock.setResponseListener(responseListener); + if (targetAddress.contains("$")) { // PayID + + SingleObserver observer = new DisposableSingleObserver() { + @Override + public void onSuccess(PayIdResponse payIdResponse) { + try { + String resolvedAddress = null; + for (PayIdAddress address : payIdResponse.getAddresses()) { + if (address.getPaymentNetwork().equals("RSK") && + address.getEnvironment().equals("MAINNET")) { + resolvedAddress = address.getAddressDetails().getAddress(); + break; + } + } + if (validateAddress(resolvedAddress)) { + coinData.setResolvedPayIdAddress(resolvedAddress); + } else { + ctx.setError("Unknown address format in PayID response"); + blockchainRequestsCallbacks.onComplete(false); + } + } catch (Exception e) { + ctx.setError("Unknown response format on PayID request"); + blockchainRequestsCallbacks.onComplete(false); + } + if (serverApiRootstock.isRequestsSequenceCompleted() && serverApiPayId.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(!ctx.hasError()); + } else { + blockchainRequestsCallbacks.onProgress(); + } + } + + @Override + public void onError(Throwable e) { + Log.i(TAG, "onFail: " + "payID" + " " + e.getMessage()); + ctx.setError("PayID error:" + e.getMessage()); + blockchainRequestsCallbacks.onComplete(false); + } + }; + + serverApiPayId.getAddress(targetAddress, ctx.getBlockchain(), observer); + } serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_GAS_PRICE, 67, coinData.getWallet(), "", ""); } diff --git a/app/src/main/java/com/tangem/wallet/rsk/RskTokenEngine.java b/app/src/main/java/com/tangem/wallet/rsk/RskTokenEngine.java index e7ed9eed23..4907b39831 100644 --- a/app/src/main/java/com/tangem/wallet/rsk/RskTokenEngine.java +++ b/app/src/main/java/com/tangem/wallet/rsk/RskTokenEngine.java @@ -4,8 +4,11 @@ import android.net.Uri; import android.util.Log; import com.tangem.data.Blockchain; +import com.tangem.data.network.ServerApiPayId; import com.tangem.data.network.ServerApiRootstock; import com.tangem.data.network.model.InfuraResponse; +import com.tangem.data.network.model.PayIdAddress; +import com.tangem.data.network.model.PayIdResponse; import com.tangem.wallet.BTCUtils; import com.tangem.wallet.CoinEngine; import com.tangem.wallet.EthTransaction; @@ -15,6 +18,10 @@ import com.tangem.wallet.token.TokenEngine; import java.math.BigInteger; +import io.reactivex.CompletableObserver; +import io.reactivex.SingleObserver; +import io.reactivex.observers.DisposableSingleObserver; + public class RskTokenEngine extends TokenEngine { private static final String TAG = RskTokenEngine.class.getSimpleName(); @@ -158,6 +165,7 @@ public class RskTokenEngine extends TokenEngine { @Override public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) { ServerApiRootstock serverApiRootstock = new ServerApiRootstock(); + final ServerApiPayId serverApiPayId = new ServerApiPayId(); // request requestData gasPrice listener ServerApiRootstock.ResponseListener responseListener = new ServerApiRootstock.ResponseListener() { @Override @@ -193,7 +201,11 @@ public class RskTokenEngine extends TokenEngine { } catch (Exception e) { e.printStackTrace(); } - blockchainRequestsCallbacks.onComplete(true); + if (serverApiRootstock.isRequestsSequenceCompleted() && serverApiPayId.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(!ctx.hasError()); + } else { + blockchainRequestsCallbacks.onProgress(); + } } @Override @@ -204,6 +216,47 @@ public class RskTokenEngine extends TokenEngine { }; serverApiRootstock.setResponseListener(responseListener); + if (targetAddress.contains("$")) { // PayID + + SingleObserver observer = new DisposableSingleObserver() { + @Override + public void onSuccess(PayIdResponse payIdResponse) { + try { + String resolvedAddress = null; + for (PayIdAddress address : payIdResponse.getAddresses()) { + if (address.getPaymentNetwork().equals("RSK") && + address.getEnvironment().equals("MAINNET")) { + resolvedAddress = address.getAddressDetails().getAddress(); + break; + } + } + if (validateAddress(resolvedAddress)) { + coinData.setResolvedPayIdAddress(resolvedAddress); + } else { + ctx.setError("Unknown address format in PayID response"); + blockchainRequestsCallbacks.onComplete(false); + } + } catch (Exception e) { + ctx.setError("Unknown response format on PayID request"); + blockchainRequestsCallbacks.onComplete(false); + } + if (serverApiRootstock.isRequestsSequenceCompleted() && serverApiPayId.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(!ctx.hasError()); + } else { + blockchainRequestsCallbacks.onProgress(); + } + } + + @Override + public void onError(Throwable e) { + Log.i(TAG, "onFail: " + "payID" + " " + e.getMessage()); + ctx.setError("PayID error:" + e.getMessage()); + blockchainRequestsCallbacks.onComplete(false); + } + }; + + serverApiPayId.getAddress(targetAddress, ctx.getBlockchain(), observer); + } serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_GAS_PRICE, 67, coinData.getWallet(), "", ""); } diff --git a/app/src/main/java/com/tangem/wallet/xlm/XlmAssetEngine.java b/app/src/main/java/com/tangem/wallet/xlm/XlmAssetEngine.java index 3cbaa9531a..882e4e8c7c 100644 --- a/app/src/main/java/com/tangem/wallet/xlm/XlmAssetEngine.java +++ b/app/src/main/java/com/tangem/wallet/xlm/XlmAssetEngine.java @@ -5,8 +5,11 @@ import android.text.InputFilter; import android.util.Log; import com.tangem.App; +import com.tangem.data.network.ServerApiPayId; import com.tangem.data.network.ServerApiStellar; import com.tangem.data.network.StellarRequest; +import com.tangem.data.network.model.PayIdAddress; +import com.tangem.data.network.model.PayIdResponse; import com.tangem.tangem_card.data.TangemCard; import com.tangem.tangem_card.tasks.SignTask; import com.tangem.tangem_card.util.Util; @@ -31,6 +34,11 @@ import org.stellar.sdk.responses.operations.OperationResponse; import java.io.IOException; import java.math.BigDecimal; +import io.reactivex.CompletableObserver; +import io.reactivex.SingleObserver; +import io.reactivex.observers.DisposableCompletableObserver; +import io.reactivex.observers.DisposableSingleObserver; + /** * Created by dvol on 7.01.2019. *

@@ -143,6 +151,10 @@ public class XlmAssetEngine extends CoinEngine { @Override public boolean validateAddress(String address) { + if (address != null && address.contains("$")) { // PayID + return validatePayId(address); + } + try { KeyPair kp = KeyPair.fromAccountId(address); // TODO is it possible to check address testNet or not @@ -379,6 +391,14 @@ public class XlmAssetEngine extends CoinEngine { public SignTask.TransactionToSign constructTransaction(Amount amountValue, Amount feeValue, boolean IncFee, String targetAddress) throws Exception { checkBlockchainDataExists(); + String destination; + //PayID + if (coinData.getResolvedPayIdAddress() != null) { + destination = coinData.getResolvedPayIdAddress(); + } else { + destination = targetAddress; + } + if (coinData.isAssetBalanceZero() && IncFee) { amountValue = new Amount(amountValue.subtract(feeValue), amountValue.getCurrency()); } @@ -388,12 +408,12 @@ public class XlmAssetEngine extends CoinEngine { operation = new ChangeTrustOperation.Builder(Asset.createNonNativeAsset(ctx.getCard().getTokenSymbol(), KeyPair.fromAccountId(ctx.getCard().getContractAddress())), "900000000000.0000000").build(); } else { if (!coinData.isAssetBalanceZero()) { - operation = new PaymentOperation.Builder(KeyPair.fromAccountId(targetAddress), Asset.createNonNativeAsset(ctx.getCard().getTokenSymbol(), KeyPair.fromAccountId(ctx.getCard().getContractAddress())), amountValue.toValueString()).build(); + operation = new PaymentOperation.Builder(KeyPair.fromAccountId(destination), Asset.createNonNativeAsset(ctx.getCard().getTokenSymbol(), KeyPair.fromAccountId(ctx.getCard().getContractAddress())), amountValue.toValueString()).build(); } else { if (coinData.isTargetAccountCreated()) - operation = new PaymentOperation.Builder(KeyPair.fromAccountId(targetAddress), new AssetTypeNative(), amountValue.toValueString()).build(); + operation = new PaymentOperation.Builder(KeyPair.fromAccountId(destination), new AssetTypeNative(), amountValue.toValueString()).build(); else - operation = new CreateAccountOperation.Builder(KeyPair.fromAccountId(targetAddress), amountValue.toValueString()).build(); + operation = new CreateAccountOperation.Builder(KeyPair.fromAccountId(destination), amountValue.toValueString()).build(); } } TransactionEx transaction = TransactionEx.buildEx(120, coinData.getAccountResponse(), operation); @@ -564,9 +584,62 @@ public class XlmAssetEngine extends CoinEngine { @Override public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) throws Exception { - // TODO: get fee stats? coinData.minFee = coinData.normalFee = coinData.maxFee = coinData.getBaseFee(); - checkTargetAccountCreated(blockchainRequestsCallbacks, targetAddress, amount); + + CompletableObserver payIdObserver = new DisposableCompletableObserver() { + @Override + public void onComplete() { + checkTargetAccountCreated(blockchainRequestsCallbacks, targetAddress, amount); + } + + @Override + public void onError(Throwable e) { + ctx.setError(e.getMessage()); + blockchainRequestsCallbacks.onComplete(false); + } + }; + + if (targetAddress.contains("$")) { // PayID + resolvePayID(targetAddress, payIdObserver); + } else { + checkTargetAccountCreated(blockchainRequestsCallbacks, targetAddress, amount); + } + } + + private void resolvePayID(String targetAddress, CompletableObserver observer) { + final ServerApiPayId serverApiPayId = new ServerApiPayId(); + + SingleObserver payIdObserver = new DisposableSingleObserver() { + @Override + public void onSuccess(PayIdResponse payIdResponse) { + try { + String resolvedAddress = null; + for (PayIdAddress address : payIdResponse.getAddresses()) { + if (address.getPaymentNetwork().equals(ctx.getBlockchain().getCurrency()) && + address.getEnvironment().equals("MAINNET")) { + resolvedAddress = address.getAddressDetails().getAddress(); + break; + } + } + if (validateAddress(resolvedAddress)) { + coinData.setResolvedPayIdAddress(resolvedAddress); + observer.onComplete(); + } else { + observer.onError(new Exception("Unknown address format in PayID response")); + } + } catch (Exception e) { + observer.onError(new Exception("Unknown response format on PayID request")); + } + } + + @Override + public void onError(Throwable e) { + Log.i(TAG, "onFail: " + "payID" + " " + e.getMessage()); + observer.onError(new Exception("PayID error:" + e.getMessage())); + } + }; + + serverApiPayId.getAddress(targetAddress, ctx.getBlockchain(), payIdObserver); } @Override diff --git a/app/src/main/java/com/tangem/wallet/xlm/XlmEngine.java b/app/src/main/java/com/tangem/wallet/xlm/XlmEngine.java index 8476b33a37..ff9c7303cc 100644 --- a/app/src/main/java/com/tangem/wallet/xlm/XlmEngine.java +++ b/app/src/main/java/com/tangem/wallet/xlm/XlmEngine.java @@ -7,8 +7,11 @@ import android.util.Log; import com.tangem.App; import com.tangem.data.Blockchain; +import com.tangem.data.network.ServerApiPayId; import com.tangem.data.network.ServerApiStellar; import com.tangem.data.network.StellarRequest; +import com.tangem.data.network.model.PayIdAddress; +import com.tangem.data.network.model.PayIdResponse; import com.tangem.tangem_card.data.TangemCard; import com.tangem.tangem_card.tasks.SignTask; import com.tangem.tangem_card.util.Util; @@ -31,6 +34,11 @@ import org.stellar.sdk.responses.operations.OperationResponse; import java.io.IOException; import java.math.BigDecimal; +import io.reactivex.CompletableObserver; +import io.reactivex.SingleObserver; +import io.reactivex.observers.DisposableCompletableObserver; +import io.reactivex.observers.DisposableSingleObserver; + /** * Created by dvol on 7.01.2019. *

@@ -123,6 +131,10 @@ public class XlmEngine extends CoinEngine { @Override public boolean validateAddress(String address) { + if (address != null && address.contains("$")) { // PayID + return validatePayId(address); + } + try { KeyPair kp = KeyPair.fromAccountId(address); // TODO is it possible to check address testNet or not @@ -356,18 +368,26 @@ public class XlmEngine extends CoinEngine { StrictMode.setThreadPolicy(policy); + String destination; + //PayID + if (coinData.getResolvedPayIdAddress() != null) { + destination = coinData.getResolvedPayIdAddress(); + } else { + destination = targetAddress; + } + if (IncFee) { amountValue = new Amount(amountValue.subtract(feeValue), amountValue.getCurrency()); } Operation operation; if (coinData.isTargetAccountCreated()) { - operation = new PaymentOperation.Builder(KeyPair.fromAccountId(targetAddress), new AssetTypeNative(), amountValue.toValueString()).build(); + operation = new PaymentOperation.Builder(KeyPair.fromAccountId(destination), new AssetTypeNative(), amountValue.toValueString()).build(); } else { if (amountValue.compareTo(coinData.getReserve()) < 0) { throw new IllegalArgumentException("Target account is not created. Send " + coinData.getReserve().toDescriptionString(getDecimals()) + " or more to create"); } - operation = new CreateAccountOperation.Builder(KeyPair.fromAccountId(targetAddress), amountValue.toValueString()).build(); + operation = new CreateAccountOperation.Builder(KeyPair.fromAccountId(destination), amountValue.toValueString()).build(); } TransactionEx transaction = TransactionEx.buildEx(120, coinData.getAccountResponse(), operation); @@ -541,7 +561,61 @@ public class XlmEngine extends CoinEngine { @Override public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) throws Exception { coinData.minFee = coinData.normalFee = coinData.maxFee = coinData.getBaseFee(); - checkTargetAccountCreated(blockchainRequestsCallbacks, targetAddress, amount); //TODO: move? + + CompletableObserver payIdObserver = new DisposableCompletableObserver() { + @Override + public void onComplete() { + checkTargetAccountCreated(blockchainRequestsCallbacks, targetAddress, amount); + } + + @Override + public void onError(Throwable e) { + ctx.setError(e.getMessage()); + blockchainRequestsCallbacks.onComplete(false); + } + }; + + if (targetAddress.contains("$")) { // PayID + resolvePayID(targetAddress, payIdObserver); + } else { + checkTargetAccountCreated(blockchainRequestsCallbacks, targetAddress, amount); + } + } + + private void resolvePayID(String targetAddress, CompletableObserver observer) { + final ServerApiPayId serverApiPayId = new ServerApiPayId(); + + SingleObserver payIdObserver = new DisposableSingleObserver() { + @Override + public void onSuccess(PayIdResponse payIdResponse) { + try { + String resolvedAddress = null; + for (PayIdAddress address : payIdResponse.getAddresses()) { + if (address.getPaymentNetwork().equals(ctx.getBlockchain().getCurrency()) && + address.getEnvironment().equals("MAINNET")) { + resolvedAddress = address.getAddressDetails().getAddress(); + break; + } + } + if (validateAddress(resolvedAddress)) { + coinData.setResolvedPayIdAddress(resolvedAddress); + observer.onComplete(); + } else { + observer.onError(new Exception("Unknown address format in PayID response")); + } + } catch (Exception e) { + observer.onError(new Exception("Unknown response format on PayID request")); + } + } + + @Override + public void onError(Throwable e) { + Log.i(TAG, "onFail: " + "payID" + " " + e.getMessage()); + observer.onError(new Exception("PayID error:" + e.getMessage())); + } + }; + + serverApiPayId.getAddress(targetAddress, ctx.getBlockchain(), payIdObserver); } @Override diff --git a/app/src/main/java/com/tangem/wallet/xrp/XrpData.java b/app/src/main/java/com/tangem/wallet/xrp/XrpData.java index b54fe8c9e7..c2d33c62b8 100644 --- a/app/src/main/java/com/tangem/wallet/xrp/XrpData.java +++ b/app/src/main/java/com/tangem/wallet/xrp/XrpData.java @@ -19,7 +19,7 @@ public class XrpData extends CoinData { private Boolean accountNotFound, targetAccountCreated = false; - private String resolvedPayIdAddress, resolvedPayIdTag = null; + private String resolvedPayIdTag = null; @Override public void loadFromBundle(Bundle B) { @@ -37,8 +37,6 @@ public class XrpData extends CoinData { else accountNotFound = false; if (B.containsKey("TargetAccountCreated")) targetAccountCreated = B.getBoolean("TargetAccountCreated"); else targetAccountCreated = false; - if (B.containsKey("ResolvedPayIdAddress")) resolvedPayIdAddress = B.getString("ResolvedPayIdAddress"); - else resolvedPayIdAddress = null; if (B.containsKey("ResolvedPayIdTag")) resolvedPayIdTag = B.getString("ResolvedPayIdTag"); else resolvedPayIdTag = null; } @@ -53,7 +51,6 @@ public class XrpData extends CoinData { if (reserve != null) B.putLong("Reserve", reserve); if (accountNotFound != null) B.putBoolean("AccoundNotFound", accountNotFound); if (targetAccountCreated != null) B.putBoolean("TargetAccountCreated", targetAccountCreated); - if (resolvedPayIdAddress != null) B.putString("ResolvedPayIdAddress", resolvedPayIdAddress); if (resolvedPayIdTag != null) B.putString("ResolvedPayIdTag", resolvedPayIdTag); } catch (Exception e) { Log.e("Can't save to bundle ", e.getMessage()); @@ -69,7 +66,6 @@ public class XrpData extends CoinData { reserve = 20000000L; accountNotFound = false; targetAccountCreated = false; - resolvedPayIdAddress = null; resolvedPayIdTag = null; } @@ -135,14 +131,6 @@ public class XrpData extends CoinData { return hasBalanceInfo() && !balanceConfirmed.equals(balanceUnconfirmed); } - public String getResolvedPayIdAddress() { - return resolvedPayIdAddress; - } - - public void setResolvedPayIdAddress(String resolvedPayIdAddress) { - this.resolvedPayIdAddress = resolvedPayIdAddress; - } - public String getResolvedPayIdTag() { return resolvedPayIdTag; } diff --git a/app/src/main/java/com/tangem/wallet/xrp/XrpEngine.java b/app/src/main/java/com/tangem/wallet/xrp/XrpEngine.java index eb63be7a6b..29a57b9457 100644 --- a/app/src/main/java/com/tangem/wallet/xrp/XrpEngine.java +++ b/app/src/main/java/com/tangem/wallet/xrp/XrpEngine.java @@ -124,18 +124,7 @@ public class XrpEngine extends CoinEngine { return false; } if (address.contains("$")) { // PayID - String[] addressParts = address.split("\\$"); - - if (addressParts.length != 2) { - return false; - } - String addressURL = "https://" + addressParts[1] + "/" + addressParts[0]; - try { - new URL(addressURL).toURI(); - return true; - } catch (Exception e) { - return false; - } + return validatePayId(address); } try { Addresses.decodeAccountID(address); diff --git a/dependencies.gradle b/dependencies.gradle index f61f047e5a..f451e17141 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,4 +1,4 @@ ext.versions = [ kotlin : '1.3.72', - build_gradle: '4.0.0', + build_gradle: '4.0.1', ] From 313cce7a0a9e3d6601667720a4792b92826c9434 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 21 Jul 2020 11:09:48 +0300 Subject: [PATCH 5/5] Updated on 2026-08-14 --- app/src/main/java/com/tangem/data/PayId.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/src/main/java/com/tangem/data/PayId.kt b/app/src/main/java/com/tangem/data/PayId.kt index 5698d0cbdd..3c882fbdd7 100644 --- a/app/src/main/java/com/tangem/data/PayId.kt +++ b/app/src/main/java/com/tangem/data/PayId.kt @@ -16,10 +16,7 @@ private val payIdSupported = EnumSet.of( Blockchain.Binance, Blockchain.BinanceAsset, Blockchain.Rootstock, - Blockchain.RootstockToken, - Blockchain.Tezos, - Blockchain.Eos, - Blockchain.Matic + Blockchain.RootstockToken ) fun Blockchain.isPayIdSupported(): Boolean {