diff --git a/app/src/main/java/com/tangem/data/network/PayIdApi.java b/app/src/main/java/com/tangem/data/network/PayIdApi.java index 032968baaa..4616c01240 100644 --- a/app/src/main/java/com/tangem/data/network/PayIdApi.java +++ b/app/src/main/java/com/tangem/data/network/PayIdApi.java @@ -4,14 +4,12 @@ import com.tangem.data.network.model.PayIdResponse; import io.reactivex.Single; import retrofit2.http.GET; +import retrofit2.http.Header; import retrofit2.http.Headers; import retrofit2.http.Path; public interface PayIdApi { - @Headers({ - "Accept: application/xrpl-mainnet+json", - "PayID-Version: 1.0" - }) + @Headers("PayID-Version: 1.0") @GET("{user}") - Single getAddress(@Path("user") String user); + Single getAddress(@Path("user") String user, @Header("Accept") String acceptHeader); } 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 237fcb279d..a2f545b580 100644 --- a/app/src/main/java/com/tangem/data/network/ServerApiPayId.java +++ b/app/src/main/java/com/tangem/data/network/ServerApiPayId.java @@ -1,10 +1,12 @@ package com.tangem.data.network; import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; -import com.tangem.App; +import com.tangem.data.Blockchain; import com.tangem.data.network.model.PayIdResponse; import com.tangem.tangem_card.util.Log; +import java.security.InvalidParameterException; + import io.reactivex.Single; import io.reactivex.SingleObserver; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -17,12 +19,21 @@ public class ServerApiPayId { private int requestsCount = 0; + public String getAcceptHeader(Blockchain blockchain) throws InvalidParameterException { + switch (blockchain) { + case Ripple: return "application/xrpl-mainnet+json"; + case Bitcoin: return "application/btc-mainnet+json"; + case Ethereum: return "application/eth-mainnet+json"; + default: throw new InvalidParameterException("PayID is not supported for " + blockchain.getOfficialName()); + } + } + public boolean isRequestsSequenceCompleted() { Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount)); return requestsCount <= 0; } - public void getAddress(String payID, SingleObserver addressObserver) { + public void getAddress(String payID, Blockchain blockchain, SingleObserver addressObserver) throws InvalidParameterException { requestsCount++; Log.i(TAG, "new getAddress request"); @@ -38,7 +49,7 @@ public class ServerApiPayId { PayIdApi api = retrofit.create(PayIdApi.class); - Single addressSingle = api.getAddress(user) + Single addressSingle = api.getAddress(user, getAcceptHeader(blockchain)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnEvent((object, throwable) -> requestsCount--); 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 28648010df..1a892adee9 100644 --- a/app/src/main/java/com/tangem/wallet/btc/BtcData.java +++ b/app/src/main/java/com/tangem/wallet/btc/BtcData.java @@ -22,6 +22,8 @@ public class BtcData extends CoinData { //for blockchain.info private boolean hasUnconfirmed = false; + private String resolvedPayIdAddress = null; + public Unspents getUnspentInputsDescription() { try { int gatheredUnspents = 0; @@ -90,6 +92,8 @@ 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 @@ -107,6 +111,7 @@ 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()); } @@ -119,6 +124,7 @@ public class BtcData extends CoinData { balanceUnconfirmed = null; unspentTransactions = null; hasUnconfirmed = false; + resolvedPayIdAddress = null; } public CoinEngine.InternalAmount getBalanceInInternalUnits() { @@ -160,4 +166,12 @@ 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 e9782bcf5d..c33f48c11d 100644 --- a/app/src/main/java/com/tangem/wallet/btc/BtcEngine.java +++ b/app/src/main/java/com/tangem/wallet/btc/BtcEngine.java @@ -11,6 +11,7 @@ import com.tangem.data.network.Server; import com.tangem.data.network.ServerApiBlockchainInfo; import com.tangem.data.network.ServerApiBlockcypher; import com.tangem.data.network.ServerApiCommon; +import com.tangem.data.network.ServerApiPayId; import com.tangem.data.network.model.BlockchainInfoAddress; import com.tangem.data.network.model.BlockchainInfoAddressAndUnspents; import com.tangem.data.network.model.BlockchainInfoInput; @@ -21,6 +22,8 @@ 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; @@ -47,6 +50,7 @@ import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; +import java.net.URL; import java.nio.ByteBuffer; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; @@ -54,8 +58,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import io.reactivex.CompletableObserver; import io.reactivex.Single; import io.reactivex.SingleObserver; +import io.reactivex.observers.DisposableCompletableObserver; import io.reactivex.observers.DisposableSingleObserver; import okhttp3.ResponseBody; @@ -154,6 +160,21 @@ public class BtcEngine 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("1") || address.startsWith("2") || address.startsWith("3") || address.startsWith("n") || address.startsWith("m")) { if (address.length() < 25) { return false; @@ -458,6 +479,14 @@ public class BtcEngine extends CoinEngine { String myAddress = ctx.getCoinData().getWallet(); byte[] pbKey = ctx.getCard().getWalletPublicKey(); + String destination; + //PayID + if (coinData.getResolvedPayIdAddress() != null) { + destination = coinData.getResolvedPayIdAddress(); + } else { + destination = targetAddress; + } + // /blockcypher/ // for (BtcData.UnspentTransaction utxo : coinData.getUnspentTransactions()) { // unspentOutputs.add(new UnspentOutputInfo(BTCUtils.fromHex(utxo.txID), new Transaction.Script(BTCUtils.fromHex(utxo.script)), utxo.amount, utxo.outputN, -1, utxo.txID, null)); @@ -502,7 +531,7 @@ public class BtcEngine extends CoinEngine { 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]); } @@ -555,7 +584,7 @@ public class BtcEngine extends CoinEngine { 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; } @@ -877,8 +906,6 @@ public class BtcEngine 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)); coinData.minFee = null; coinData.maxFee = null; coinData.normalFee = null; @@ -886,6 +913,26 @@ public class BtcEngine extends CoinEngine { if (!coinData.isUseBlockcypher()) { final ServerApiCommon serverApiCommon = new ServerApiCommon(); + final Integer[] calcSize = new Integer[1]; + + CompletableObserver payIdObserver = new DisposableCompletableObserver() { + @Override + public void onComplete() { + calcSize[0] = calculateEstimatedTransactionSize(coinData.getResolvedPayIdAddress(), amount.toValueString()); + Log.e(TAG, String.format("Estimated tx size %d", calcSize[0])); + + serverApiCommon.requestBtcEstimatedFee(ServerApiCommon.ESTIMATE_FEE_PRIORITY); + serverApiCommon.requestBtcEstimatedFee(ServerApiCommon.ESTIMATE_FEE_NORMAL); + serverApiCommon.requestBtcEstimatedFee(ServerApiCommon.ESTIMATE_FEE_MINIMAL); + } + + @Override + public void onError(Throwable e) { + ctx.setError(e.getMessage()); + blockchainRequestsCallbacks.onComplete(false); + } + }; + final ServerApiCommon.EstimatedFeeListener estimatedFeeListener = new ServerApiCommon.EstimatedFeeListener() { @Override public void onSuccess(int blockCount, String estimateFeeResponse) { @@ -898,8 +945,8 @@ public class BtcEngine extends CoinEngine { return; } - if (calcSize != 0) { - fee = fee.multiply(new BigDecimal(calcSize)).divide(new BigDecimal(1024), BigDecimal.ROUND_DOWN); // per Kb -> per byte + if (calcSize[0] != 0) { + fee = fee.multiply(new BigDecimal(calcSize[0])).divide(new BigDecimal(1024), BigDecimal.ROUND_DOWN); // per Kb -> per byte } else { if (blockchainRequestsCallbacks.allowAdvance()) { serverApiCommon.requestBtcEstimatedFee(blockCount); @@ -948,13 +995,38 @@ public class BtcEngine extends CoinEngine { }; serverApiCommon.setBtcEstimatedFeeListener(estimatedFeeListener); - serverApiCommon.requestBtcEstimatedFee(ServerApiCommon.ESTIMATE_FEE_PRIORITY); - serverApiCommon.requestBtcEstimatedFee(ServerApiCommon.ESTIMATE_FEE_NORMAL); - serverApiCommon.requestBtcEstimatedFee(ServerApiCommon.ESTIMATE_FEE_MINIMAL); + if (targetAddress.contains("$")) { // PayID + resolvePayID(targetAddress, payIdObserver); + } else { + calcSize[0] = calculateEstimatedTransactionSize(targetAddress, amount.toValueString()); + Log.e(TAG, String.format("Estimated tx size %d", calcSize[0])); + + serverApiCommon.requestBtcEstimatedFee(ServerApiCommon.ESTIMATE_FEE_PRIORITY); + serverApiCommon.requestBtcEstimatedFee(ServerApiCommon.ESTIMATE_FEE_NORMAL); + serverApiCommon.requestBtcEstimatedFee(ServerApiCommon.ESTIMATE_FEE_MINIMAL); + } } else { final ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher(); + final Integer[] calcSize = new Integer[1]; + + CompletableObserver payIdObserver = new DisposableCompletableObserver() { + @Override + public void onComplete() { + calcSize[0] = calculateEstimatedTransactionSize(coinData.getResolvedPayIdAddress(), amount.toValueString()); + Log.e(TAG, String.format("Estimated tx size %d", calcSize[0])); + + serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_FEE, "", ""); + } + + @Override + public void onError(Throwable e) { + ctx.setError(e.getMessage()); + blockchainRequestsCallbacks.onComplete(false); + } + }; + ServerApiBlockcypher.ResponseListener blockcypherListener = new ServerApiBlockcypher.ResponseListener() { @Override public void onSuccess(String method, BlockcypherResponse blockcypherResponse) { @@ -970,9 +1042,9 @@ public class BtcEngine extends CoinEngine { BigDecimal normalByteFee = new BigDecimal(blockcypherFee.getMedium_fee_per_kb()).divide(BigDecimal.valueOf(1024)); BigDecimal maxByteFee = new BigDecimal(blockcypherFee.getHigh_fee_per_kb()).divide(BigDecimal.valueOf(1024)); - CoinEngine.InternalAmount minIntAmount = new CoinEngine.InternalAmount(minByteFee.multiply(BigDecimal.valueOf(calcSize)), "satoshi"); - CoinEngine.InternalAmount normalIntAmount = new CoinEngine.InternalAmount(normalByteFee.multiply(BigDecimal.valueOf(calcSize)), "satoshi"); - CoinEngine.InternalAmount maxIntAmount = new CoinEngine.InternalAmount(maxByteFee.multiply(BigDecimal.valueOf(calcSize)), "satoshi"); + CoinEngine.InternalAmount minIntAmount = new CoinEngine.InternalAmount(minByteFee.multiply(BigDecimal.valueOf(calcSize[0])), "satoshi"); + CoinEngine.InternalAmount normalIntAmount = new CoinEngine.InternalAmount(normalByteFee.multiply(BigDecimal.valueOf(calcSize[0])), "satoshi"); + CoinEngine.InternalAmount maxIntAmount = new CoinEngine.InternalAmount(maxByteFee.multiply(BigDecimal.valueOf(calcSize[0])), "satoshi"); coinData.minFee = convertToAmount(minIntAmount); coinData.normalFee = convertToAmount(normalIntAmount); @@ -999,10 +1071,53 @@ public class BtcEngine extends CoinEngine { serverApiBlockcypher.setResponseListener(blockcypherListener); - serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_FEE, "", ""); + if (targetAddress.contains("$")) { // PayID + resolvePayID(targetAddress, payIdObserver); + } else { + calcSize[0] = calculateEstimatedTransactionSize(targetAddress, amount.toValueString()); + Log.e(TAG, String.format("Estimated tx size %d", calcSize[0])); + + 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("BTC") && + 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 { 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 2135c886a1..73511313ca 100644 --- a/app/src/main/java/com/tangem/wallet/eth/EthData.java +++ b/app/src/main/java/com/tangem/wallet/eth/EthData.java @@ -14,6 +14,8 @@ 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); @@ -46,6 +48,7 @@ public class EthData extends CoinData { public void clearInfo() { super.clearInfo(); balance = null; + resolvedPayIdAddress = null; } public CoinEngine.InternalAmount getBalanceInInternalUnits() { @@ -57,6 +60,13 @@ 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) { @@ -73,6 +83,8 @@ 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 @@ -86,6 +98,7 @@ 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 4c31f74772..3aedf1f65d 100644 --- a/app/src/main/java/com/tangem/wallet/eth/EthEngine.java +++ b/app/src/main/java/com/tangem/wallet/eth/EthEngine.java @@ -9,10 +9,13 @@ import com.tangem.Constant; 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; @@ -33,8 +36,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 15.02.2018. */ @@ -137,6 +144,21 @@ public class EthEngine 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; } @@ -266,7 +288,7 @@ public class EthEngine extends CoinEngine { @Override public boolean checkNewTransactionAmount(Amount amount) { - if( BuildConfig.FLAVOR== Constant.FLAVOR_TANGEM_CARDANO ) { + if (BuildConfig.FLAVOR == Constant.FLAVOR_TANGEM_CARDANO) { return true; } Amount balance = getBalance(); @@ -387,6 +409,14 @@ public class EthEngine extends CoinEngine { Log.e(TAG, "Construct transaction " + amountValue.toString() + " with fee " + feeValue.toString() + (IncFee ? " including" : " excluding")); + String destination; + //PayID + if (coinData.getResolvedPayIdAddress() != null) { + destination = coinData.getResolvedPayIdAddress(); + } else { + destination = targetAddress; + } + BigInteger nonceValue = coinData.getConfirmedTXCount(); byte[] pbKey = ctx.getCard().getWalletPublicKey(); @@ -401,13 +431,12 @@ public class EthEngine extends CoinEngine { BigInteger gasLimit = BigInteger.valueOf(21000); Integer chainId = this.getChainIdNum(); - String to = targetAddress; - if (to.startsWith("0x") || to.startsWith("0X")) { - to = to.substring(2); + if (destination.startsWith("0x") || destination.startsWith("0X")) { + destination = destination.substring(2); } - final EthTransaction tx = EthTransaction.create(to, weiAmount, nonceValue, gasPrice, gasLimit, chainId); + final EthTransaction tx = EthTransaction.create(destination, weiAmount, nonceValue, gasPrice, gasLimit, chainId); return new SignTask.TransactionToSign() { @Override @@ -510,7 +539,7 @@ public class EthEngine extends CoinEngine { break; } - if (serverApiInfura.isRequestsSequenceCompleted()&& serverApiBlockcypher.isRequestsSequenceCompleted()) { + if (serverApiInfura.isRequestsSequenceCompleted() && serverApiBlockcypher.isRequestsSequenceCompleted()) { blockchainRequestsCallbacks.onComplete(!ctx.hasError()); } else { blockchainRequestsCallbacks.onProgress(); @@ -521,7 +550,7 @@ public class EthEngine extends CoinEngine { public void onFail(String method, String message) { Log.e(TAG, "onFail: " + method + " " + message); ctx.setError(message); - if (serverApiInfura.isRequestsSequenceCompleted()&& serverApiBlockcypher.isRequestsSequenceCompleted()) { + if (serverApiInfura.isRequestsSequenceCompleted() && serverApiBlockcypher.isRequestsSequenceCompleted()) { blockchainRequestsCallbacks.onComplete(false); } else { blockchainRequestsCallbacks.onProgress(); @@ -556,7 +585,7 @@ public class EthEngine extends CoinEngine { Log.e(TAG, "FAIL BLOCKCYPHER_ADDRESS Exception"); } - if (serverApiInfura.isRequestsSequenceCompleted()&& serverApiBlockcypher.isRequestsSequenceCompleted()) { + if (serverApiInfura.isRequestsSequenceCompleted() && serverApiBlockcypher.isRequestsSequenceCompleted()) { blockchainRequestsCallbacks.onComplete(!ctx.hasError()); } else { blockchainRequestsCallbacks.onProgress(); @@ -565,7 +594,7 @@ public class EthEngine extends CoinEngine { public void onSuccess(String method, BlockcypherFee blockcypherFee) { Log.e(TAG, "Wrong response type for requestBalanceAndUnspentTransactions"); - if (serverApiInfura.isRequestsSequenceCompleted()&& serverApiBlockcypher.isRequestsSequenceCompleted()) { + if (serverApiInfura.isRequestsSequenceCompleted() && serverApiBlockcypher.isRequestsSequenceCompleted()) { blockchainRequestsCallbacks.onComplete(!ctx.hasError()); } else { blockchainRequestsCallbacks.onProgress(); @@ -575,7 +604,7 @@ public class EthEngine extends CoinEngine { @Override public void onFail(String method, String message) { Log.i(TAG, "onFail: " + method + " " + message); - if (serverApiInfura.isRequestsSequenceCompleted()&& serverApiBlockcypher.isRequestsSequenceCompleted()) { + if (serverApiInfura.isRequestsSequenceCompleted() && serverApiBlockcypher.isRequestsSequenceCompleted()) { blockchainRequestsCallbacks.onComplete(!ctx.hasError()); } else { blockchainRequestsCallbacks.onProgress(); @@ -593,6 +622,8 @@ public class EthEngine extends CoinEngine { @Override public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) { ServerApiInfura serverApiInfura = new ServerApiInfura(ctx.getBlockchain()); + final ServerApiPayId serverApiPayId = new ServerApiPayId(); + // request requestData eth gasPrice listener ServerApiInfura.ResponseListener responseListener = new ServerApiInfura.ResponseListener() { @Override @@ -623,7 +654,11 @@ public class EthEngine extends CoinEngine { Log.i(TAG, "normal fee: " + coinData.normalFee.toString()); Log.i(TAG, "max fee : " + coinData.maxFee.toString()); - blockchainRequestsCallbacks.onComplete(true); + if (serverApiInfura.isRequestsSequenceCompleted() && serverApiPayId.isRequestsSequenceCompleted()) { + blockchainRequestsCallbacks.onComplete(!ctx.hasError()); + } else { + blockchainRequestsCallbacks.onProgress(); + } } @Override @@ -634,6 +669,52 @@ public class EthEngine 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)) { + 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(), "", ""); } @@ -648,7 +729,7 @@ public class EthEngine extends CoinEngine { @Override public void onSuccess(String method, InfuraResponse infuraResponse) { if (method.equals(ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION)) { - if (infuraResponse.getResult()==null || infuraResponse.getResult().isEmpty()) { + if (infuraResponse.getResult() == null || infuraResponse.getResult().isEmpty()) { ctx.setError("Rejected by node: " + infuraResponse.getError()); blockchainRequestsCallbacks.onComplete(false); } else { @@ -675,7 +756,9 @@ public class EthEngine extends CoinEngine { serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_SEND_RAW_TRANSACTION, 67, coinData.getWallet(), "", txStr); } - public int pendingTransactionTimeoutInSeconds() { return 10; } + public int pendingTransactionTimeoutInSeconds() { + return 10; + } @Override public boolean needMultipleLinesForBalance() { 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 f7d551992d..749e0170b9 100644 --- a/app/src/main/java/com/tangem/wallet/xrp/XrpEngine.java +++ b/app/src/main/java/com/tangem/wallet/xrp/XrpEngine.java @@ -658,7 +658,7 @@ public class XrpEngine extends CoinEngine { } }; - serverApiPayId.getAddress(targetAddress, observer); + serverApiPayId.getAddress(targetAddress, ctx.getBlockchain(), observer); } else { XrpXAddressDecoded xAddressDecoded = XrpXAddressService.Companion.decode(targetAddress); if (xAddressDecoded == null) { // classic address