diff --git a/README.md b/README.md
index d86ed5d62..3a2928312 100644
--- a/README.md
+++ b/README.md
@@ -160,6 +160,7 @@ Validator | Description
**isVariableWidth(str)** | check if the string contains a mixture of full and half-width chars.
**isVAT(str, countryCode)** | checks that the string is a [valid VAT number](https://en.wikipedia.org/wiki/VAT_identification_number) if validation is available for the given country code matching [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2).
Available country codes: `[ 'GB', 'IT' ]`.
**isWhitelisted(str, chars)** | checks characters if they appear in the whitelist.
+**isXRPAddress(str, [, options])** | check if string is valid XRP address.
options object is optional. With options object, we can specify format of xrp address (classic and/or xAddress). In case of xAddress, user must also specify if we should check if address is valid on test/dev network, or on main network, because format of xAddress depends on network.
Example: `{ classicAddress: true, xAddress: { test: false } }`
Note: This does not verify checksum of address. This checks only the format of address.
**matches(str, pattern [, modifiers])** | check if string matches the pattern.
Either `matches('foo', /foo/i)` or `matches('foo', 'foo', 'i')`.
## Sanitizers
diff --git a/src/index.js b/src/index.js
index e91c582da..e99aebb39 100644
--- a/src/index.js
+++ b/src/index.js
@@ -102,6 +102,8 @@ import isMimeType from './lib/isMimeType';
import isLatLong from './lib/isLatLong';
import isPostalCode, { locales as isPostalCodeLocales } from './lib/isPostalCode';
+import isXRPAddress from './lib/isXRPAddress';
+
import ltrim from './lib/ltrim';
import rtrim from './lib/rtrim';
import trim from './lib/trim';
@@ -222,6 +224,7 @@ const validator = {
isDate,
isLicensePlate,
isVAT,
+ isXRPAddress,
};
export default validator;
diff --git a/src/lib/isXRPAddress.js b/src/lib/isXRPAddress.js
new file mode 100644
index 000000000..3e295f69b
--- /dev/null
+++ b/src/lib/isXRPAddress.js
@@ -0,0 +1,30 @@
+import assertString from './util/assertString';
+
+const classicAddressRegex = /^r[A-HJ-NP-Za-km-z1-9]{24,35}$/;
+const xAddressMainnetRegex = /^X[A-HJ-NP-Za-km-z1-9]{46,}$/;
+const xAddressNonMainnetRegex = /^T[A-HJ-NP-Za-km-z1-9]{46,}$/;
+
+export default function isXRPAddress(address, options) {
+ assertString(address);
+
+ if (typeof (options) !== 'object') {
+ // only check for classic address
+ return classicAddressRegex.test(address);
+ }
+
+ if (typeof (options.xAddress) !== 'object') {
+ // just passthrough if user does not want to check anything
+ return options.classicAddress ? classicAddressRegex.test(address) : true;
+ }
+
+ let xAddressRegex = xAddressMainnetRegex;
+ if (options.xAddress.test) {
+ xAddressRegex = xAddressNonMainnetRegex;
+ }
+
+ if (options.classicAddress) {
+ return xAddressRegex.test(address) || classicAddressRegex.test(address);
+ }
+
+ return xAddressRegex.test(address);
+}
diff --git a/test/validators.js b/test/validators.js
index 4a4180a8e..3a3b7ac85 100644
--- a/test/validators.js
+++ b/test/validators.js
@@ -1,6 +1,6 @@
import assert from 'assert';
-import fs from 'fs';
import { format } from 'util';
+import fs from 'fs';
import vm from 'vm';
import validator from '../src/index';
@@ -8589,6 +8589,162 @@ describe('Validators', () => {
});
});
+ it('should validate XRP addresses', () => {
+ test({
+ validator: 'isXRPAddress',
+ valid: [
+ 'rAAAAAAAAAAAAAAAAAAAAAAAAA',
+ 'rUfkLWGb7UwFgpci24kBpu7eCR',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3oTnpLxUG',
+ ],
+ invalid: [
+ 'r4zkbgzeQriVB1iFeZj8rTT1',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3oInpLxUG',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3oOnpLxUG',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3olnpLxUG',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3o+npLxUG',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3o/npLxUG',
+ '4zkbgzeQriVB1iFeZj8rTT1q3oTnpLxUGr',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3oTnpLxUGGGG',
+ ],
+ });
+
+ test({
+ validator: 'isXRPAddress',
+ args: [
+ {
+ classicAddress: false,
+ },
+ ],
+ valid: [
+ 'a',
+ 'xyz',
+ ],
+ });
+
+ test({
+ validator: 'isXRPAddress',
+ args: [
+ {
+ classicAddress: true,
+ },
+ ],
+ valid: [
+ 'rAAAAAAAAAAAAAAAAAAAAAAAAA',
+ 'rUfkLWGb7UwFgpci24kBpu7eCR',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3oTnpLxUG',
+ ],
+ invalid: [
+ 'r4zkbgzeQriVB1iFeZj8rTT1',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3oInpLxUG',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3oOnpLxUG',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3olnpLxUG',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3o+npLxUG',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3o/npLxUG',
+ '4zkbgzeQriVB1iFeZj8rTT1q3oTnpLxUGr',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3oTnpLxUGGGG',
+ ],
+ });
+
+ test({
+ validator: 'isXRPAddress',
+ args: [
+ {
+ xAddress: {
+ test: false,
+ },
+ },
+ ],
+ valid: [
+ 'X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ',
+ 'XV5sbjUmgPpvXv4ixFWZ5ptAYZ6PD2gYsjNFQLKYW33DzBm',
+ 'XV5sbjUmgPpvXv4ixFWZ5ptAYZ6PD28Sq49uo34VyjnmK5H',
+ ],
+ invalid: [
+ 'rAAAAAAAAAAAAAAAAAAAAAAAAA',
+ 'rUfkLWGb7UwFgpci24kBpu7eCR',
+ 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3oTnpLxUG',
+ 'T7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ',
+ ],
+ });
+
+ test({
+ validator: 'isXRPAddress',
+ args: [
+ {
+ xAddress: {
+ test: true,
+ },
+ },
+ ],
+ valid: [
+ 'T7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ',
+ ],
+ invalid: [
+ 'rAAAAAAAAAAAAAAAAAAAAAAAAA',
+ 'rUfkLWGb7UwFgpci24kBpu7eCR',
+ 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3oTnpLxUG',
+ 'T7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cq',
+ 'X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ',
+ ],
+ });
+
+ test({
+ validator: 'isXRPAddress',
+ args: [
+ {
+ classicAddress: true,
+ xAddress: {
+ test: true,
+ },
+ },
+ ],
+ valid: [
+ 'rAAAAAAAAAAAAAAAAAAAAAAAAA',
+ 'rUfkLWGb7UwFgpci24kBpu7eCR',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3oTnpLxUG',
+ 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
+ 'T7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ',
+ ],
+ invalid: [
+ 'T7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cq',
+ 'X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ',
+ ],
+ });
+
+ test({
+ validator: 'isXRPAddress',
+ args: [
+ {
+ classicAddress: true,
+ xAddress: {
+ test: false,
+ },
+ },
+ ],
+ valid: [
+ 'rAAAAAAAAAAAAAAAAAAAAAAAAA',
+ 'rUfkLWGb7UwFgpci24kBpu7eCR',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3oTnpLxUG',
+ 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
+ 'X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ',
+ ],
+ invalid: [
+ 'r4zkbgzeQriVB1iFeZj8rTT1',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3oInpLxUG',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3oOnpLxUG',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3olnpLxUG',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3o+npLxUG',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3o/npLxUG',
+ '4zkbgzeQriVB1iFeZj8rTT1q3oTnpLxUGr',
+ 'r4zkbgzeQriVB1iFeZj8rTT1q3oTnpLxUGGGG',
+ 'T7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cq',
+ ],
+ });
+ });
+
it('should validate booleans', () => {
test({
validator: 'isBoolean',