From 7298a6373a1b4801da06bcff03f9c587164ef19e Mon Sep 17 00:00:00 2001
From: woodser <13068859+woodser@users.noreply.github.com>
Date: Sun, 10 Aug 2025 12:38:19 -0400
Subject: [PATCH] support Ripple (XRP)
---
.../haveno/asset/RippleAddressValidator.java | 32 +++++++++++++
.../main/java/haveno/asset/coins/Ripple.java | 28 +++++++++++
.../META-INF/services/haveno.asset.Asset | 1 +
.../java/haveno/asset/coins/RippleTest.java | 44 ++++++++++++++++++
.../java/haveno/core/locale/CurrencyUtil.java | 1 +
.../src/main/java/haveno/desktop/images.css | 4 ++
.../src/main/resources/images/xrp_logo.png | Bin 0 -> 5397 bytes
7 files changed, 110 insertions(+)
create mode 100644 assets/src/main/java/haveno/asset/RippleAddressValidator.java
create mode 100644 assets/src/main/java/haveno/asset/coins/Ripple.java
create mode 100644 assets/src/test/java/haveno/asset/coins/RippleTest.java
create mode 100644 desktop/src/main/resources/images/xrp_logo.png
diff --git a/assets/src/main/java/haveno/asset/RippleAddressValidator.java b/assets/src/main/java/haveno/asset/RippleAddressValidator.java
new file mode 100644
index 0000000000..7325818143
--- /dev/null
+++ b/assets/src/main/java/haveno/asset/RippleAddressValidator.java
@@ -0,0 +1,32 @@
+/*
+ * This file is part of Haveno.
+ *
+ * Haveno is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Haveno is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Haveno. If not, see .
+ */
+
+package haveno.asset;
+
+/**
+ * Validates a Ripple address using a regular expression.
+ */
+public class RippleAddressValidator extends RegexAddressValidator {
+
+ public RippleAddressValidator() {
+ super("^r[1-9A-HJ-NP-Za-km-z]{25,34}$");
+ }
+
+ public RippleAddressValidator(String errorMessageI18nKey) {
+ super("^r[1-9A-HJ-NP-Za-km-z]{25,34}$", errorMessageI18nKey);
+ }
+}
diff --git a/assets/src/main/java/haveno/asset/coins/Ripple.java b/assets/src/main/java/haveno/asset/coins/Ripple.java
new file mode 100644
index 0000000000..04ce84475d
--- /dev/null
+++ b/assets/src/main/java/haveno/asset/coins/Ripple.java
@@ -0,0 +1,28 @@
+/*
+ * This file is part of Haveno.
+ *
+ * Haveno is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Haveno is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Haveno. If not, see .
+ */
+
+package haveno.asset.coins;
+
+import haveno.asset.Coin;
+import haveno.asset.RippleAddressValidator;
+
+public class Ripple extends Coin {
+
+ public Ripple() {
+ super("Ripple", "XRP", new RippleAddressValidator());
+ }
+}
diff --git a/assets/src/main/resources/META-INF/services/haveno.asset.Asset b/assets/src/main/resources/META-INF/services/haveno.asset.Asset
index 80b9cd036d..a10943e769 100644
--- a/assets/src/main/resources/META-INF/services/haveno.asset.Asset
+++ b/assets/src/main/resources/META-INF/services/haveno.asset.Asset
@@ -7,6 +7,7 @@ haveno.asset.coins.BitcoinCash
haveno.asset.coins.Ether
haveno.asset.coins.Litecoin
haveno.asset.coins.Monero
+haveno.asset.coins.Ripple
haveno.asset.tokens.TetherUSDERC20
haveno.asset.tokens.TetherUSDTRC20
haveno.asset.tokens.USDCoinERC20
diff --git a/assets/src/test/java/haveno/asset/coins/RippleTest.java b/assets/src/test/java/haveno/asset/coins/RippleTest.java
new file mode 100644
index 0000000000..d984d78174
--- /dev/null
+++ b/assets/src/test/java/haveno/asset/coins/RippleTest.java
@@ -0,0 +1,44 @@
+/*
+ * This file is part of Haveno.
+ *
+ * Haveno is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Haveno is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Haveno. If not, see .
+ */
+
+package haveno.asset.coins;
+
+import haveno.asset.AbstractAssetTest;
+import org.junit.jupiter.api.Test;
+
+public class RippleTest extends AbstractAssetTest {
+
+ public RippleTest() {
+ super(new Ripple());
+ }
+
+ @Test
+ public void testValidAddresses() {
+ assertValidAddress("r9CxAMAoZAgyVGP8CY9F1arzf9bJg3Y7U8");
+ assertValidAddress("rsXMbDtCAmzSWajWiii7ffWygAjYVNDxY7");
+ assertValidAddress("rE3nYkQy121JEVb37JKX8LSH6wUBnNvNo2");
+ assertValidAddress("rMzucuWFUEE6aM9DC992BqqMgZNPrv4kvi");
+ assertValidAddress("rJUmAFPWE36cpdbN4DUEAFBLtG2xkEavY8");
+ }
+
+ @Test
+ public void testInvalidAddresses() {
+ assertInvalidAddress("RJUmAFPWE36cpdbN4DUEAFBLtG2xkEavY8");
+ assertInvalidAddress("zJUmAFPWE36cpdbN4DUEAFBLtG2xkEavY8");
+ assertInvalidAddress("1LgfapHEPhZbRF9pMd5WPT35hFXcZS1USrW");
+ }
+}
diff --git a/core/src/main/java/haveno/core/locale/CurrencyUtil.java b/core/src/main/java/haveno/core/locale/CurrencyUtil.java
index 25605ab18a..e3b61eefaf 100644
--- a/core/src/main/java/haveno/core/locale/CurrencyUtil.java
+++ b/core/src/main/java/haveno/core/locale/CurrencyUtil.java
@@ -200,6 +200,7 @@ public class CurrencyUtil {
result.add(new CryptoCurrency("BCH", "Bitcoin Cash"));
result.add(new CryptoCurrency("ETH", "Ether"));
result.add(new CryptoCurrency("LTC", "Litecoin"));
+ result.add(new CryptoCurrency("XRP", "Ripple"));
result.add(new CryptoCurrency("DAI-ERC20", "Dai Stablecoin"));
result.add(new CryptoCurrency("USDT-ERC20", "Tether USD"));
result.add(new CryptoCurrency("USDT-TRC20", "Tether USD"));
diff --git a/desktop/src/main/java/haveno/desktop/images.css b/desktop/src/main/java/haveno/desktop/images.css
index c721f2ee7b..549549605e 100644
--- a/desktop/src/main/java/haveno/desktop/images.css
+++ b/desktop/src/main/java/haveno/desktop/images.css
@@ -353,6 +353,10 @@
-fx-image: url("../../images/xmr_logo.png");
}
+#image-xrp-logo {
+ -fx-image: url("../../images/xrp_logo.png");
+}
+
#image-dark-mode-toggle {
-fx-image: url("../../images/dark_mode_toggle.png");
}
diff --git a/desktop/src/main/resources/images/xrp_logo.png b/desktop/src/main/resources/images/xrp_logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..0c1034a6c626764f2eef9499b6b01839c4e6079c
GIT binary patch
literal 5397
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
literal 0
HcmV?d00001