From e6db2a18b29e4e2749748667ec9e45d26c6b4f2c Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 18 Jun 2020 12:26:15 +0300 Subject: [PATCH 1/5] Updated on 2026-08-14 --- app/src/main/java/com/tangem/data/Blockchain.java | 2 +- .../com/tangem/ui/fragment/wallet/LoadedWalletFragment.kt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/tangem/data/Blockchain.java b/app/src/main/java/com/tangem/data/Blockchain.java index 25117cc331..278732d3f1 100644 --- a/app/src/main/java/com/tangem/data/Blockchain.java +++ b/app/src/main/java/com/tangem/data/Blockchain.java @@ -38,7 +38,7 @@ 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); + 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; 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 e445b52dd1..f25a07ce5f 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 @@ -375,10 +375,10 @@ class LoadedWalletFragment : BaseFragment(), NavigationResultListener, NfcAdapte } private fun createPayId(payId: String) { - val network = if (ctx.blockchain == Blockchain.Ripple) "XRPL" else ctx.blockchain.id + val network = if (ctx.blockchain == Blockchain.Ripple) "XRPL" else ctx.blockchain.currency viewModel.setPayId( - Util.byteArrayToHexString(ctx.card!!.cid!!), - Util.byteArrayToHexString(ctx.card!!.cardPublicKey!!), + Util.byteArrayToHexString(ctx.card.cid!!), + Util.byteArrayToHexString(ctx.card.cardPublicKey!!), payId, ctx.coinData.wallet, network From 233be12f4e0d566175aeb20280aa862c819fce26 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 18 Jun 2020 13:23:27 +0300 Subject: [PATCH 2/5] Updated on 2026-08-14 --- .../com/tangem/wallet/token/TokenEngine.java | 89 ++++++++++++++++++- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/tangem/wallet/token/TokenEngine.java b/app/src/main/java/com/tangem/wallet/token/TokenEngine.java index 7668f0fcae..4fabf745ed 100644 --- a/app/src/main/java/com/tangem/wallet/token/TokenEngine.java +++ b/app/src/main/java/com/tangem/wallet/token/TokenEngine.java @@ -10,10 +10,13 @@ import com.tangem.App; import com.tangem.data.Blockchain; import com.tangem.data.network.ServerApiBlockcypher; import com.tangem.data.network.ServerApiInfura; +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.BlockcypherTxref; import com.tangem.data.network.model.InfuraResponse; +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.util.CryptoUtil; @@ -34,8 +37,12 @@ import org.bitcoinj.core.ECKey; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; +import java.net.URL; import java.util.Arrays; +import io.reactivex.SingleObserver; +import io.reactivex.observers.DisposableSingleObserver; + /** * Created by Ilia on 20.03.2018. */ @@ -165,6 +172,21 @@ public class TokenEngine 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; + } + } + if (!address.startsWith("0x") && !address.startsWith("0X")) { return false; } @@ -439,10 +461,18 @@ public class TokenEngine extends CoinEngine { @Override public SignTask.TransactionToSign constructTransaction(Amount amountValue, Amount feeValue, boolean IncFee, String targetAddress) throws Exception { - if (amountValue.getCurrency().equals(this.getBlockchain().getCurrency())) { - return constructTransactionCoin(feeValue, amountValue, IncFee, targetAddress); + String destination; + //PayID + if (coinData.getResolvedPayIdAddress() != null) { + destination = coinData.getResolvedPayIdAddress(); } else { - return constructTransactionToken(feeValue, amountValue, IncFee, targetAddress); + destination = targetAddress; + } + + if (amountValue.getCurrency().equals(this.getBlockchain().getCurrency())) { + return constructTransactionCoin(feeValue, amountValue, IncFee, destination); + } else { + return constructTransactionToken(feeValue, amountValue, IncFee, destination); } } @@ -776,6 +806,8 @@ public class TokenEngine extends CoinEngine { @Override public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) { ServerApiInfura serverApiInfura = new ServerApiInfura(); + final ServerApiPayId serverApiPayId = new ServerApiPayId(); + // request requestData eth gasPrice listener ServerApiInfura.ResponseListener responseListener = new ServerApiInfura.ResponseListener() { @Override @@ -826,6 +858,57 @@ public class TokenEngine extends CoinEngine { }; serverApiInfura.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("ETH") && + address.getEnvironment().equals("MAINNET")) { + resolvedAddress = address.getAddressDetails().getAddress(); + break; + } + } + if (validateAddress(resolvedAddress)) { + if (resolvedAddress.equals(coinData.getWallet())) { + ctx.setError(R.string.prepare_transaction_error_same_address); + blockchainRequestsCallbacks.onComplete(false); + } else { + coinData.setResolvedPayIdAddress(resolvedAddress); + if (serverApiInfura.isRequestsSequenceCompleted() && serverApiPayId.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(!ctx.hasError()); + } else { + blockchainRequestsCallbacks.onProgress(); + } + } + } 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 (serverApiInfura.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); + } serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GAS_PRICE, 67, coinData.getWallet(), "", ""); } From 6d705d747c16704209a096bf25f297e4531019d0 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 18 Jun 2020 16:57:51 +0300 Subject: [PATCH 3/5] Updated on 2026-08-14 --- app/src/main/java/com/tangem/data/network/ServerApiPayId.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 a2f545b580..e0ebf5a849 100644 --- a/app/src/main/java/com/tangem/data/network/ServerApiPayId.java +++ b/app/src/main/java/com/tangem/data/network/ServerApiPayId.java @@ -23,7 +23,9 @@ public class ServerApiPayId { switch (blockchain) { case Ripple: return "application/xrpl-mainnet+json"; case Bitcoin: return "application/btc-mainnet+json"; - case Ethereum: return "application/eth-mainnet+json"; + case Ethereum: + case Token: + return "application/eth-mainnet+json"; default: throw new InvalidParameterException("PayID is not supported for " + blockchain.getOfficialName()); } } From e5a109b367215c2708db9cb38dee1a356fa20c6e Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 18 Jun 2020 22:44:08 +0300 Subject: [PATCH 4/5] Updated on 2026-08-14 --- .../com/tangem/data/network/model/PayIdResponse.kt | 5 ++++- .../main/java/com/tangem/wallet/xrp/XrpData.java | 14 +++++++++++++- .../main/java/com/tangem/wallet/xrp/XrpEngine.java | 12 ++++++++++++ .../xrp/{XrpTaggedAddress.kt => XrpXAddress.kt} | 0 4 files changed, 29 insertions(+), 2 deletions(-) rename app/src/main/java/com/tangem/wallet/xrp/{XrpTaggedAddress.kt => XrpXAddress.kt} (100%) diff --git a/app/src/main/java/com/tangem/data/network/model/PayIdResponse.kt b/app/src/main/java/com/tangem/data/network/model/PayIdResponse.kt index 583ae73462..dfdf6220b3 100644 --- a/app/src/main/java/com/tangem/data/network/model/PayIdResponse.kt +++ b/app/src/main/java/com/tangem/data/network/model/PayIdResponse.kt @@ -20,5 +20,8 @@ data class PayIdAddress( data class PayIdAddressDetails( @SerializedName("address") - var address: String? = null + var address: String? = null, + + @SerializedName("tag") + var tag: String? = null ) \ No newline at end of file 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 09bc6276fa..dab54f8286 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 = null; + private String resolvedPayIdAddress, resolvedPayIdTag = null; @Override public void loadFromBundle(Bundle B) { @@ -39,6 +39,8 @@ public class XrpData extends CoinData { else targetAccountCreated = false; if (B.containsKey("ResolvedPayIdAddress")) resolvedPayIdAddress = B.getString("ResolvedPayIdAddress"); else resolvedPayIdAddress = null; + if (B.containsKey("ResolvedPayIdTag")) resolvedPayIdTag = B.getString("ResolvedPayIdTag"); + else resolvedPayIdAddress = null; } @Override @@ -52,6 +54,7 @@ public class XrpData extends CoinData { 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()); } @@ -67,6 +70,7 @@ public class XrpData extends CoinData { accountNotFound = false; targetAccountCreated = false; resolvedPayIdAddress = null; + resolvedPayIdTag = null; } // balanceUnconfirmed is just the latest balance, it equals balanceConfirmed if no unconfirmed transaction present @@ -138,4 +142,12 @@ public class XrpData extends CoinData { public void setResolvedPayIdAddress(String resolvedPayIdAddress) { this.resolvedPayIdAddress = resolvedPayIdAddress; } + + public String getResolvedPayIdTag() { + return resolvedPayIdTag; + } + + public void setResolvedPayIdTag(String resolvedPayIdTag) { + this.resolvedPayIdTag = 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 2b5e4ca733..eb63be7a6b 100644 --- a/app/src/main/java/com/tangem/wallet/xrp/XrpEngine.java +++ b/app/src/main/java/com/tangem/wallet/xrp/XrpEngine.java @@ -367,6 +367,15 @@ public class XrpEngine extends CoinEngine { destinationTag = decodedXAddress.getDestinationTag(); } + if (coinData.getResolvedPayIdTag() != null) { + int resolvedTag = Integer.parseInt(coinData.getResolvedPayIdTag()); + if (destinationTag == null || destinationTag == resolvedTag) { + destinationTag = resolvedTag; + } else { // Different destination tags in PayID response and in X-address + throw new IllegalArgumentException("Conflicting destination tags found"); + } + } + if (IncFee) { amount = convertToInternalAmount(amountValue).subtract(convertToInternalAmount(feeValue)).setScale(0).toPlainString(); } else { @@ -621,15 +630,18 @@ public class XrpEngine extends CoinEngine { public void onSuccess(PayIdResponse payIdResponse) { try { String resolvedAddress = null; + String resolvedTag = null; for (PayIdAddress address : payIdResponse.getAddresses()) { if (address.getPaymentNetwork().equals("XRPL") && address.getEnvironment().equals("MAINNET")) { resolvedAddress = address.getAddressDetails().getAddress(); + resolvedTag = address.getAddressDetails().getTag(); break; } } if (validateAddress(resolvedAddress)) { coinData.setResolvedPayIdAddress(resolvedAddress); + coinData.setResolvedPayIdTag(resolvedTag); XrpXAddressDecoded xAddressDecoded = XrpXAddressService.Companion.decode(resolvedAddress); if (xAddressDecoded == null) { // classic address diff --git a/app/src/main/java/com/tangem/wallet/xrp/XrpTaggedAddress.kt b/app/src/main/java/com/tangem/wallet/xrp/XrpXAddress.kt similarity index 100% rename from app/src/main/java/com/tangem/wallet/xrp/XrpTaggedAddress.kt rename to app/src/main/java/com/tangem/wallet/xrp/XrpXAddress.kt From 336b949f31e41086a14bde81de5fda57e0ac34b9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 19 Jun 2020 15:38:42 +0300 Subject: [PATCH 5/5] Updated on 2026-08-14 --- app/src/main/java/com/tangem/wallet/xrp/XrpData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 dab54f8286..b54fe8c9e7 100644 --- a/app/src/main/java/com/tangem/wallet/xrp/XrpData.java +++ b/app/src/main/java/com/tangem/wallet/xrp/XrpData.java @@ -40,7 +40,7 @@ public class XrpData extends CoinData { if (B.containsKey("ResolvedPayIdAddress")) resolvedPayIdAddress = B.getString("ResolvedPayIdAddress"); else resolvedPayIdAddress = null; if (B.containsKey("ResolvedPayIdTag")) resolvedPayIdTag = B.getString("ResolvedPayIdTag"); - else resolvedPayIdAddress = null; + else resolvedPayIdTag = null; } @Override