Updated on 2026-08-14

This commit is contained in:
Tangem 2020-06-11 18:13:38 +03:00
parent 6e5b272987
commit 29e47d0782
3 changed files with 67 additions and 19 deletions

View file

@ -34,8 +34,6 @@ import java.util.Arrays;
import io.reactivex.SingleObserver;
import io.reactivex.observers.DisposableSingleObserver;
import io.xpring.xrpl.ClassicAddress;
import io.xpring.xrpl.Utils;
public class XrpEngine extends CoinEngine {
@ -142,12 +140,7 @@ public class XrpEngine extends CoinEngine {
Addresses.decodeAccountID(address);
return true;
} catch (Exception e) {
// X-address
ClassicAddress classicAddress = Utils.decodeXAddress(address);
if (classicAddress == null) {
return false;
}
return !classicAddress.isTest();
return XrpXAddressService.Companion.validate(address);
}
}
@ -367,10 +360,10 @@ public class XrpEngine extends CoinEngine {
}
//X-address
ClassicAddress classicAddress = Utils.decodeXAddress(destination);
if (classicAddress != null) {
destination = classicAddress.address();
destinationTag = classicAddress.tag().get();
XrpXAddressDecoded decodedXAddress = XrpXAddressService.Companion.decode(destination);
if (decodedXAddress != null) {
destination = decodedXAddress.getAddress();
destinationTag = decodedXAddress.getDestinationTag();
}
if (IncFee) {
@ -564,7 +557,7 @@ public class XrpEngine extends CoinEngine {
public void onSuccess(String method, RippleResponse rippleResponse) {
Log.i(TAG, "onSuccess: " + method);
switch (method) {
case ServerApiRipple.RIPPLE_ACCOUNT_INFO: {
case ServerApiRipple.RIPPLE_ACCOUNT_INFO: { //check if target account is created
try {
if (rippleResponse.getResult().getError_code().equals(19)) { // "Account not found"
coinData.setTargetAccountCreated(false);
@ -630,13 +623,12 @@ public class XrpEngine extends CoinEngine {
if (validateAddress(resolvedAddress)) {
coinData.setResolvedPayIdAddress(resolvedAddress);
//check if target account is created
serverApiRipple.requestData(ServerApiRipple.RIPPLE_ACCOUNT_INFO, resolvedAddress, ""); //TODO: maybe just assume PayID account is created?
} else {
ctx.setError("Unknown address format in PayID response");
}
} catch (Exception e) {
ctx.setError("FAIL payID Exception");
ctx.setError("Unknown response format on PayID request");
}
if (serverApiRipple.isRequestsSequenceCompleted() && serverApiPayId.isRequestsSequenceCompleted()) {
blockchainRequestsCallbacks.onComplete(!ctx.hasError());
@ -648,15 +640,19 @@ public class XrpEngine extends CoinEngine {
@Override
public void onError(Throwable e) {
Log.i(TAG, "onFail: " + "payID" + " " + e.getMessage());
ctx.setError(e.getMessage());
ctx.setError("PayID error:" + e.getMessage());
blockchainRequestsCallbacks.onComplete(false);
}
};
serverApiPayId.getAddress(targetAddress, observer);
} else {
//check if target account is created
serverApiRipple.requestData(ServerApiRipple.RIPPLE_ACCOUNT_INFO, targetAddress, "");
XrpXAddressDecoded xAddressDecoded = XrpXAddressService.Companion.decode(targetAddress);
if (xAddressDecoded == null) { // classic address
serverApiRipple.requestData(ServerApiRipple.RIPPLE_ACCOUNT_INFO, targetAddress, "");
} else { // X-address
serverApiRipple.requestData(ServerApiRipple.RIPPLE_ACCOUNT_INFO, xAddressDecoded.getAddress(), "");
}
}
}

View file

@ -0,0 +1,53 @@
package com.tangem.wallet.xrp
import com.ripple.encodings.addresses.Addresses
import com.ripple.encodings.base58.B58
import org.kethereum.extensions.toBigInteger
class XrpXAddressService {
companion object {
private val xrpBase58 = B58("rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz")
private val xrpMainnetPrefix = byteArrayOf(0x05, 0x44)
private val zeroTagBytes = ByteArray(4) { 0 }
fun validate(address: String): Boolean {
return decode(address) != null
}
fun decode(address: String): XrpXAddressDecoded? {
try {
val addressBytes = xrpBase58.decodeChecked(address)
if (addressBytes.size != 31) return null
val prefix = addressBytes.slice(0..1).toByteArray()
if (!prefix.contentEquals(xrpMainnetPrefix)) return null
val accountBytes = addressBytes.slice(2..21).toByteArray()
val classicAddress = Addresses.encodeAccountID(accountBytes)
val flag = addressBytes[22]
val tagBytes = addressBytes.slice(23..26).toByteArray()
val reservedTagBytes = addressBytes.slice(27..30).toByteArray()
if (!reservedTagBytes.contentEquals(zeroTagBytes)) return null
var tag: Int? = null
when (flag) {
0.toByte() -> if (!tagBytes.contentEquals(zeroTagBytes)) return null
1.toByte() -> {
tag = tagBytes.reversedArray().toBigInteger().toInt()
}
else -> return null
}
return XrpXAddressDecoded(classicAddress, tag)
} catch (e: Exception) {
return null
}
}
}
}
data class XrpXAddressDecoded(
val address: String,
val destinationTag: Int?
)