From 5f3722a0374607cd0ade54d33de57a812898d8a3 Mon Sep 17 00:00:00 2001 From: Rastislav Date: Thu, 14 Dec 2023 22:36:02 +0100 Subject: [PATCH] Added boolean for crypto --- README.md | 4 +- ican.js | 674 +++++++++++++++++++++++----------------------- package.json | 8 +- src/index.d.ts | 11 +- src/index.js | 246 +++++++++-------- test/index.js | 16 ++ test/samples.json | 20 +- 7 files changed, 511 insertions(+), 468 deletions(-) diff --git a/README.md b/README.md index 2e06cb6..f6f2b3b 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,8 @@ Ican.isValid('BE68539007547034'); ## API -- isValid(ican) -- toBCAN(ican, separator) +- isValid(ican, onlyCrypto) +- toBCAN(ican, separator, onlyCrypto) - fromBCAN(countryCode, bcan) - isValidBCAN(countryCode, bcan) - printFormat(ican, separator) diff --git a/ican.js b/ican.js index bfbfe08..4f9084b 100644 --- a/ican.js +++ b/ican.js @@ -1,81 +1,76 @@ (function (root, factory) { - if (typeof define === 'function' && define.amd) { // eslint-disable-line no-undef - define(['exports'], factory) // eslint-disable-line no-undef - } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') { - factory(exports) - } else { - factory(root.ICAN = {}) - } -}(this, function(exports){ - - if (!Array.prototype.map){ - Array.prototype.map = function(fun /*, thisArg */){ // eslint-disable-line no-extend-native - "use strict" - - if (this === undefined || this === null) - throw new TypeError() - - var t = Object(this) - var len = t.length >>> 0 - if (typeof fun !== "function") - throw new TypeError() - - var res = new Array(len) - var thisArg = arguments.length >= 2 ? arguments[1] : undefined - for (var i = 0; i < len; i++) - { - if (i in t) - res[i] = fun.call(thisArg, t[i], i, t) - } - - return res - } + if (typeof define === 'function' && define.amd) { // eslint-disable-line no-undef + define(['exports'], factory) // eslint-disable-line no-undef + } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') { + factory(exports) + } else { + factory(root.ICAN = {}) + } +}(this, function (exports) { + if (!Array.prototype.map) { + Array.prototype.map = function (fun /*, thisArg */) { // eslint-disable-line no-extend-native + 'use strict' + + if (this === undefined || this === null) { throw new TypeError() } + + const t = Object(this) + const len = t.length >>> 0 + if (typeof fun !== 'function') { throw new TypeError() } + + const res = new Array(len) + const thisArg = arguments.length >= 2 ? arguments[1] : undefined + for (let i = 0; i < len; i++) { + if (i in t) { res[i] = fun.call(thisArg, t[i], i, t) } + } + + return res } + } - var A = 'A'.charCodeAt(0), - Z = 'Z'.charCodeAt(0) + const A = 'A'.charCodeAt(0) + const Z = 'Z'.charCodeAt(0) - /** + /** * Prepare an ICAN for mod 97 computation by moving the first 4 chars to the end and transforming the letters to * numbers (A = 10, B = 11, ..., Z = 35), as specified in ISO13616. * * @param {string} ican the ICAN * @returns {string} the prepared ICAN */ - function iso13616Prepare(ican) { - ican = ican.toUpperCase() - ican = ican.substr(4) + ican.substr(0,4) - - return ican.split('').map(function(n){ - var code = n.charCodeAt(0) - if (code >= A && code <= Z){ - // A = 10, B = 11, ... Z = 35 - return code - A + 10 - } else { - return n - } - }).join('') - } - - /** + function iso13616Prepare (ican) { + ican = ican.toUpperCase() + ican = ican.slice(4) + ican.slice(0, 4); + + return ican.split('').map(function (n) { + const code = n.charCodeAt(0) + if (code >= A && code <= Z) { + // A = 10, B = 11, ... Z = 35 + return code - A + 10 + } else { + return n + } + }).join('') + } + + /** * Calculates the MOD 97 10 of the passed ICAN as specified in ISO7064. * * @param ican * @returns {number} */ - function iso7064Mod97(ican) { - var remainder = ican, - block - - while (remainder.length > 2){ - block = remainder.slice(0, 9) - remainder = parseInt(block, 10) % 97 + remainder.slice(block.length) - } + function iso7064Mod97 (ican) { + let remainder = ican + let block - return parseInt(remainder, 10) % 97 + while (remainder.length > 2) { + block = remainder.slice(0, 9) + remainder = parseInt(block, 10) % 97 + remainder.slice(block.length) } - /** + return parseInt(remainder, 10) % 97 + } + + /** * Parse the BCAN structure used to configure each ICAN Specification and returns a matching regular expression. * A structure is composed of blocks of 3 characters (one letter and 2 digits). Each block represents * a logical group in the typical representation of the BCAN. For each group, the letter indicates which characters @@ -84,59 +79,57 @@ * @param {string} structure the structure to parse * @returns {RegExp} */ - function parseStructure(structure){ - // split in blocks of 3 chars - var regex = structure.match(/(.{3})/g).map(function(block){ - - // parse each structure block (1-char + 2-digits) - var format, - pattern = block.slice(0, 1), - repeats = parseInt(block.slice(1), 10) - - switch (pattern){ - case "A": - format = "0-9A-Za-z" - break - case "B": - format = "0-9A-Z" - break - case "C": - format = "A-Za-z" - break - case "H": - format = "0-9A-Fa-f" - break - case "F": - format = "0-9" - break - case "L": - format = "a-z" - break - case "U": - format = "A-Z" - break - case "W": - format = "0-9a-z" - break - } - - return '([' + format + ']{' + repeats + '})' - }) - - return new RegExp('^' + regex.join('') + '$') - } - - /** + function parseStructure (structure) { + // split in blocks of 3 chars + const regex = structure.match(/(.{3})/g).map(function (block) { + // parse each structure block (1-char + 2-digits) + let format + const pattern = block.slice(0, 1) + const repeats = parseInt(block.slice(1), 10) + + switch (pattern) { + case 'A': + format = '0-9A-Za-z' + break + case 'B': + format = '0-9A-Z' + break + case 'C': + format = 'A-Za-z' + break + case 'H': + format = '0-9A-Fa-f' + break + case 'F': + format = '0-9' + break + case 'L': + format = 'a-z' + break + case 'U': + format = 'A-Z' + break + case 'W': + format = '0-9a-z' + break + } + + return '([' + format + ']{' + repeats + '})' + }) + + return new RegExp('^' + regex.join('') + '$') + } + + /** * * @param ican * @returns {string} */ - function electronicFormat(ican){ - return ican.replace(NON_ALPHANUM, '').toUpperCase() - } - + function electronicFormat (ican) { + return ican.replace(NON_ALPHANUM, '').toUpperCase() + } - /** + /** * Create a new Specification for a valid ICAN number. * * @param countryCode the code of the country @@ -145,65 +138,66 @@ * @param example an example valid ICAN * @constructor */ - function Specification(countryCode, length, structure, example){ - - this.countryCode = countryCode - this.length = length - this.structure = structure - this.example = example - } - - /** + function Specification (countryCode, length, structure, crypto, example) { + this.countryCode = countryCode + this.length = length + this.structure = structure + this.crypto = crypto + this.example = example + } + + /** * Lazy-loaded regex (parse the structure and construct the regular expression the first time we need it for validation) */ - Specification.prototype._regex = function(){ - return this._cachedRegex || (this._cachedRegex = parseStructure(this.structure)) - } + Specification.prototype._regex = function () { + return this._cachedRegex || (this._cachedRegex = parseStructure(this.structure)) + } - /** + /** * Check if the passed ican is valid according to this specification. * * @param {String} ican the ican to validate + * @param {Boolean} onlyCrypto check only digital assets * @returns {boolean} true if valid, false otherwise */ - Specification.prototype.isValid = function(ican){ - return this.length === ican.length - && this.countryCode === ican.slice(0,2) - && this._regex().test(ican.slice(4)) - && iso7064Mod97(iso13616Prepare(ican)) === 1 - } - - /** + Specification.prototype.isValid = function (ican, onlyCrypto = false) { + return this.length === ican.length && + this.countryCode === ican.slice(0, 2) && + (!onlyCrypto || this.crypto) && + this._regex().test(ican.slice(4)) && + iso7064Mod97(iso13616Prepare(ican)) === 1 + } + + /** * Convert the passed ICAN to a country-specific BCAN. * * @param ican the ICAN to convert * @param separator the separator to use between BCAN blocks * @returns {string} the BCAN */ - Specification.prototype.toBCAN = function(ican, separator) { - return this._regex().exec(ican.slice(4)).slice(1).join(separator) - } + Specification.prototype.toBCAN = function (ican, separator) { + return this._regex().exec(ican.slice(4)).slice(1).join(separator) + } - /** + /** * Convert the passed BCAN to an ICAN for this country specification. - * Please note that "generation of the ICAN shall be the exclusive responsibility of the bank/branch servicing the account". * This method implements the preferred algorithm described in http://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_ICAN_check_digits * * @param bcan the BCAN to convert to ICAN * @returns {string} the ICAN */ - Specification.prototype.fromBCAN = function(bcan) { - if (!this.isValidBCAN(bcan)){ - throw new Error('Invalid BCAN') - } + Specification.prototype.fromBCAN = function (bcan) { + if (!this.isValidBCAN(bcan)) { + throw new Error('Invalid BCAN') + } - var remainder = iso7064Mod97(iso13616Prepare(this.countryCode + '00' + bcan)), - checkDigit = ('0' + (98 - remainder)).slice(-2) + const remainder = iso7064Mod97(iso13616Prepare(this.countryCode + '00' + bcan)) + const checkDigit = ('0' + (98 - remainder)).slice(-2) - return this.countryCode + checkDigit + bcan - } + return this.countryCode + checkDigit + bcan + } - /** + /** * Check of the passed BCAN is valid. * This function only checks the format of the BCAN (length and matching the letetr/number specs) but does not * verify the check digit. @@ -211,243 +205,247 @@ * @param bcan the BCAN to validate * @returns {boolean} true if the passed bcan is a valid BCAN according to this specification, false otherwise */ - Specification.prototype.isValidBCAN = function(bcan) { - return this.length - 4 === bcan.length - && this._regex().test(bcan) - } - - var countries = {} - - function addSpecification(ICAN){ - countries[ICAN.countryCode] = ICAN - } - - addSpecification(new Specification("AD", 24, "F04F04A12", "AD1200012030200359100100")) - addSpecification(new Specification("AE", 23, "F03F16", "AE070331234567890123456")) - addSpecification(new Specification("AL", 28, "F08A16", "AL47212110090000000235698741")) - addSpecification(new Specification("AT", 20, "F05F11", "AT611904300234573201")) - addSpecification(new Specification("AZ", 28, "U04A20", "AZ21NABZ00000000137010001944")) - addSpecification(new Specification("BA", 20, "F03F03F08F02", "BA391290079401028494")) - addSpecification(new Specification("BE", 16, "F03F07F02", "BE68539007547034")) - addSpecification(new Specification("BG", 22, "U04F04F02A08", "BG80BNBG96611020345678")) - addSpecification(new Specification("BH", 22, "U04A14", "BH67BMAG00001299123456")) - addSpecification(new Specification("BR", 29, "F08F05F10U01A01", "BR9700360305000010009795493P1")) - addSpecification(new Specification("BY", 28, "A04F04A16", "BY13NBRB3600900000002Z00AB00")) - addSpecification(new Specification("CH", 21, "F05A12", "CH9300762011623852957")) - addSpecification(new Specification("CR", 22, "F04F14", "CR72012300000171549015")) - addSpecification(new Specification("CY", 28, "F03F05A16", "CY17002001280000001200527600")) - addSpecification(new Specification("CZ", 24, "F04F06F10", "CZ6508000000192000145399")) - addSpecification(new Specification("DE", 22, "F08F10", "DE89370400440532013000")) - addSpecification(new Specification("DK", 18, "F04F09F01", "DK5000400440116243")) - addSpecification(new Specification("DO", 28, "U04F20", "DO28BAGR00000001212453611324")) - addSpecification(new Specification("EE", 20, "F02F02F11F01", "EE382200221020145685")) - addSpecification(new Specification("EG", 29, "F04F04F17", "EG800002000156789012345180002")) - addSpecification(new Specification("ES", 24, "F04F04F01F01F10", "ES9121000418450200051332")) - addSpecification(new Specification("FI", 18, "F06F07F01", "FI2112345600000785")) - addSpecification(new Specification("FO", 18, "F04F09F01", "FO6264600001631634")) - addSpecification(new Specification("FR", 27, "F05F05A11F02", "FR1420041010050500013M02606")) - addSpecification(new Specification("GB", 22, "U04F06F08", "GB29NWBK60161331926819")) - addSpecification(new Specification("GE", 22, "U02F16", "GE29NB0000000101904917")) - addSpecification(new Specification("GI", 23, "U04A15", "GI75NWBK000000007099453")) - addSpecification(new Specification("GL", 18, "F04F09F01", "GL8964710001000206")) - addSpecification(new Specification("GR", 27, "F03F04A16", "GR1601101250000000012300695")) - addSpecification(new Specification("GT", 28, "A04A20", "GT82TRAJ01020000001210029690")) - addSpecification(new Specification("HR", 21, "F07F10", "HR1210010051863000160")) - addSpecification(new Specification("HU", 28, "F03F04F01F15F01", "HU42117730161111101800000000")) - addSpecification(new Specification("IE", 22, "U04F06F08", "IE29AIBK93115212345678")) - addSpecification(new Specification("IL", 23, "F03F03F13", "IL620108000000099999999")) - addSpecification(new Specification("IS", 26, "F04F02F06F10", "IS140159260076545510730339")) - addSpecification(new Specification("IT", 27, "U01F05F05A12", "IT60X0542811101000000123456")) - addSpecification(new Specification("IQ", 23, "U04F03A12", "IQ98NBIQ850123456789012")) - addSpecification(new Specification("JO", 30, "A04F22", "JO15AAAA1234567890123456789012")) - addSpecification(new Specification("KW", 30, "U04A22", "KW81CBKU0000000000001234560101")) - addSpecification(new Specification("KZ", 20, "F03A13", "KZ86125KZT5004100100")) - addSpecification(new Specification("LB", 28, "F04A20", "LB62099900000001001901229114")) - addSpecification(new Specification("LC", 32, "U04F24", "LC07HEMM000100010012001200013015")) - addSpecification(new Specification("LI", 21, "F05A12", "LI21088100002324013AA")) - addSpecification(new Specification("LT", 20, "F05F11", "LT121000011101001000")) - addSpecification(new Specification("LU", 20, "F03A13", "LU280019400644750000")) - addSpecification(new Specification("LV", 21, "U04A13", "LV80BANK0000435195001")) - addSpecification(new Specification("MC", 27, "F05F05A11F02", "MC5811222000010123456789030")) - addSpecification(new Specification("MD", 24, "U02A18", "MD24AG000225100013104168")) - addSpecification(new Specification("ME", 22, "F03F13F02", "ME25505000012345678951")) - addSpecification(new Specification("MK", 19, "F03A10F02", "MK07250120000058984")) - addSpecification(new Specification("MR", 27, "F05F05F11F02", "MR1300020001010000123456753")) - addSpecification(new Specification("MT", 31, "U04F05A18", "MT84MALT011000012345MTLCAST001S")) - addSpecification(new Specification("MU", 30, "U04F02F02F12F03U03", "MU17BOMM0101101030300200000MUR")) - addSpecification(new Specification("NL", 18, "U04F10", "NL91ABNA0417164300")) - addSpecification(new Specification("NO", 15, "F04F06F01", "NO9386011117947")) - addSpecification(new Specification("PK", 24, "U04A16", "PK36SCBL0000001123456702")) - addSpecification(new Specification("PL", 28, "F08F16", "PL61109010140000071219812874")) - addSpecification(new Specification("PS", 29, "U04A21", "PS92PALS000000000400123456702")) - addSpecification(new Specification("PT", 25, "F04F04F11F02", "PT50000201231234567890154")) - addSpecification(new Specification("QA", 29, "U04A21", "QA30AAAA123456789012345678901")) - addSpecification(new Specification("RO", 24, "U04A16", "RO49AAAA1B31007593840000")) - addSpecification(new Specification("RS", 22, "F03F13F02", "RS35260005601001611379")) - addSpecification(new Specification("SA", 24, "F02A18", "SA0380000000608010167519")) - addSpecification(new Specification("SC", 31, "U04F04F16U03", "SC18SSCB11010000000000001497USD")) - addSpecification(new Specification("SE", 24, "F03F16F01", "SE4550000000058398257466")) - addSpecification(new Specification("SI", 19, "F05F08F02", "SI56263300012039086")) - addSpecification(new Specification("SK", 24, "F04F06F10", "SK3112000000198742637541")) - addSpecification(new Specification("SM", 27, "U01F05F05A12", "SM86U0322509800000000270100")) - addSpecification(new Specification("ST", 25, "F08F11F02", "ST68000100010051845310112")) - addSpecification(new Specification("SV", 28, "U04F20", "SV62CENR00000000000000700025")) - addSpecification(new Specification("TL", 23, "F03F14F02", "TL380080012345678910157")) - addSpecification(new Specification("TN", 24, "F02F03F13F02", "TN5910006035183598478831")) - addSpecification(new Specification("TR", 26, "F05F01A16", "TR330006100519786457841326")) - addSpecification(new Specification("UA", 29, "F25", "UA511234567890123456789012345")) - addSpecification(new Specification("VA", 22, "F18", "VA59001123000012345678")) - addSpecification(new Specification("VG", 24, "U04F16", "VG96VPVG0000012345678901")) - addSpecification(new Specification("XK", 20, "F04F10F02", "XK051212012345678906")) - - - // The following countries are not included in the official ICAN registry but use the ICAN specification - - // Angola - addSpecification(new Specification("AO", 25, "F21", "AO69123456789012345678901")) - // Burkina - addSpecification(new Specification("BF", 27, "F23", "BF2312345678901234567890123")) - // Burundi - addSpecification(new Specification("BI", 16, "F12", "BI41123456789012")) - // Benin - addSpecification(new Specification("BJ", 28, "F24", "BJ39123456789012345678901234")) - // Ivory - addSpecification(new Specification("CI", 28, "U02F22", "CI70CI1234567890123456789012")) - // Cameron - addSpecification(new Specification("CM", 27, "F23", "CM9012345678901234567890123")) - // Cape Verde - addSpecification(new Specification("CV", 25, "F21", "CV30123456789012345678901")) - // Algeria - addSpecification(new Specification("DZ", 24, "F20", "DZ8612345678901234567890")) - // Iran - addSpecification(new Specification("IR", 26, "F22", "IR861234568790123456789012")) - // Madagascar - addSpecification(new Specification("MG", 27, "F23", "MG1812345678901234567890123")) - // Mali - addSpecification(new Specification("ML", 28, "U01F23", "ML15A12345678901234567890123")) - // Mozambique - addSpecification(new Specification("MZ", 25, "F21", "MZ25123456789012345678901")) - // Senegal - addSpecification(new Specification("SN", 28, "U01F23", "SN52A12345678901234567890123")) - - // The following are regional and administrative French Republic subdivision ICAN specification (same structure as FR, only country code vary) - addSpecification(new Specification("GF", 27, "F05F05A11F02", "GF121234512345123456789AB13")) - addSpecification(new Specification("GP", 27, "F05F05A11F02", "GP791234512345123456789AB13")) - addSpecification(new Specification("MQ", 27, "F05F05A11F02", "MQ221234512345123456789AB13")) - addSpecification(new Specification("RE", 27, "F05F05A11F02", "RE131234512345123456789AB13")) - addSpecification(new Specification("PF", 27, "F05F05A11F02", "PF281234512345123456789AB13")) - addSpecification(new Specification("TF", 27, "F05F05A11F02", "TF891234512345123456789AB13")) - addSpecification(new Specification("YT", 27, "F05F05A11F02", "YT021234512345123456789AB13")) - addSpecification(new Specification("NC", 27, "F05F05A11F02", "NC551234512345123456789AB13")) - addSpecification(new Specification("BL", 27, "F05F05A11F02", "BL391234512345123456789AB13")) - addSpecification(new Specification("MF", 27, "F05F05A11F02", "MF551234512345123456789AB13")) - addSpecification(new Specification("PM", 27, "F05F05A11F02", "PM071234512345123456789AB13")) - addSpecification(new Specification("WF", 27, "F05F05A11F02", "WF621234512345123456789AB13")) - - // Cryptocurrency addresses - - // Core Blockchain Mainnet - addSpecification(new Specification("CB", 44, "H40", "CB661234567890ABCDEF1234567890ABCDEF12345678")) - // Core Blockchain Testnet - Devin - addSpecification(new Specification("AB", 44, "H40", "AB841234567890ABCDEF1234567890ABCDEF12345678")) - // Core Enterprise Blockchain Enterprise - addSpecification(new Specification("CE", 44, "H40", "CE571234567890ABCDEF1234567890ABCDEF12345678")) - - const NON_ALPHANUM = /[^a-zA-Z0-9]/g, - EVERY_FOUR_CHARS =/(.{4})(?!$)/g - - /** + Specification.prototype.isValidBCAN = function (bcan, onlyCrypto = false) { + return this.length - 4 === bcan.length && + (!onlyCrypto || this.crypto) && + this._regex().test(bcan) + } + + const countries = {} + + function addSpecification (ICAN) { + countries[ICAN.countryCode] = ICAN + } + + addSpecification(new Specification('AD', 24, 'F04F04A12', false, 'AD1200012030200359100100')) + addSpecification(new Specification('AE', 23, 'F03F16', false, 'AE070331234567890123456')) + addSpecification(new Specification('AL', 28, 'F08A16', false, 'AL47212110090000000235698741')) + addSpecification(new Specification('AT', 20, 'F05F11', false, 'AT611904300234573201')) + addSpecification(new Specification('AZ', 28, 'U04A20', false, 'AZ21NABZ00000000137010001944')) + addSpecification(new Specification('BA', 20, 'F03F03F08F02', false, 'BA391290079401028494')) + addSpecification(new Specification('BE', 16, 'F03F07F02', false, 'BE68539007547034')) + addSpecification(new Specification('BG', 22, 'U04F04F02A08', false, 'BG80BNBG96611020345678')) + addSpecification(new Specification('BH', 22, 'U04A14', false, 'BH67BMAG00001299123456')) + addSpecification(new Specification('BR', 29, 'F08F05F10U01A01', false, 'BR9700360305000010009795493P1')) + addSpecification(new Specification('BY', 28, 'A04F04A16', false, 'BY13NBRB3600900000002Z00AB00')) + addSpecification(new Specification('CH', 21, 'F05A12', false, 'CH9300762011623852957')) + addSpecification(new Specification('CR', 22, 'F04F14', false, 'CR72012300000171549015')) + addSpecification(new Specification('CY', 28, 'F03F05A16', false, 'CY17002001280000001200527600')) + addSpecification(new Specification('CZ', 24, 'F04F06F10', false, 'CZ6508000000192000145399')) + addSpecification(new Specification('DE', 22, 'F08F10', false, 'DE89370400440532013000')) + addSpecification(new Specification('DK', 18, 'F04F09F01', false, 'DK5000400440116243')) + addSpecification(new Specification('DO', 28, 'U04F20', false, 'DO28BAGR00000001212453611324')) + addSpecification(new Specification('EE', 20, 'F02F02F11F01', false, 'EE382200221020145685')) + addSpecification(new Specification('EG', 29, 'F04F04F17', false, 'EG800002000156789012345180002')) + addSpecification(new Specification('ES', 24, 'F04F04F01F01F10', false, 'ES9121000418450200051332')) + addSpecification(new Specification('FI', 18, 'F06F07F01', false, 'FI2112345600000785')) + addSpecification(new Specification('FO', 18, 'F04F09F01', false, 'FO6264600001631634')) + addSpecification(new Specification('FR', 27, 'F05F05A11F02', false, 'FR1420041010050500013M02606')) + addSpecification(new Specification('GB', 22, 'U04F06F08', false, 'GB29NWBK60161331926819')) + addSpecification(new Specification('GE', 22, 'U02F16', false, 'GE29NB0000000101904917')) + addSpecification(new Specification('GI', 23, 'U04A15', false, 'GI75NWBK000000007099453')) + addSpecification(new Specification('GL', 18, 'F04F09F01', false, 'GL8964710001000206')) + addSpecification(new Specification('GR', 27, 'F03F04A16', false, 'GR1601101250000000012300695')) + addSpecification(new Specification('GT', 28, 'A04A20', false, 'GT82TRAJ01020000001210029690')) + addSpecification(new Specification('HR', 21, 'F07F10', false, 'HR1210010051863000160')) + addSpecification(new Specification('HU', 28, 'F03F04F01F15F01', false, 'HU42117730161111101800000000')) + addSpecification(new Specification('IE', 22, 'U04F06F08', false, 'IE29AIBK93115212345678')) + addSpecification(new Specification('IL', 23, 'F03F03F13', false, 'IL620108000000099999999')) + addSpecification(new Specification('IS', 26, 'F04F02F06F10', false, 'IS140159260076545510730339')) + addSpecification(new Specification('IT', 27, 'U01F05F05A12', false, 'IT60X0542811101000000123456')) + addSpecification(new Specification('IQ', 23, 'U04F03A12', false, 'IQ98NBIQ850123456789012')) + addSpecification(new Specification('JO', 30, 'A04F22', false, 'JO15AAAA1234567890123456789012')) + addSpecification(new Specification('KW', 30, 'U04A22', false, 'KW81CBKU0000000000001234560101')) + addSpecification(new Specification('KZ', 20, 'F03A13', false, 'KZ86125KZT5004100100')) + addSpecification(new Specification('LB', 28, 'F04A20', false, 'LB62099900000001001901229114')) + addSpecification(new Specification('LC', 32, 'U04F24', false, 'LC07HEMM000100010012001200013015')) + addSpecification(new Specification('LI', 21, 'F05A12', false, 'LI21088100002324013AA')) + addSpecification(new Specification('LT', 20, 'F05F11', false, 'LT121000011101001000')) + addSpecification(new Specification('LU', 20, 'F03A13', false, 'LU280019400644750000')) + addSpecification(new Specification('LV', 21, 'U04A13', false, 'LV80BANK0000435195001')) + addSpecification(new Specification('MC', 27, 'F05F05A11F02', false, 'MC5811222000010123456789030')) + addSpecification(new Specification('MD', 24, 'U02A18', false, 'MD24AG000225100013104168')) + addSpecification(new Specification('ME', 22, 'F03F13F02', false, 'ME25505000012345678951')) + addSpecification(new Specification('MK', 19, 'F03A10F02', false, 'MK07250120000058984')) + addSpecification(new Specification('MR', 27, 'F05F05F11F02', false, 'MR1300020001010000123456753')) + addSpecification(new Specification('MT', 31, 'U04F05A18', false, 'MT84MALT011000012345MTLCAST001S')) + addSpecification(new Specification('MU', 30, 'U04F02F02F12F03U03', false, 'MU17BOMM0101101030300200000MUR')) + addSpecification(new Specification('NL', 18, 'U04F10', false, 'NL91ABNA0417164300')) + addSpecification(new Specification('NO', 15, 'F04F06F01', false, 'NO9386011117947')) + addSpecification(new Specification('PK', 24, 'U04A16', false, 'PK36SCBL0000001123456702')) + addSpecification(new Specification('PL', 28, 'F08F16', false, 'PL61109010140000071219812874')) + addSpecification(new Specification('PS', 29, 'U04A21', false, 'PS92PALS000000000400123456702')) + addSpecification(new Specification('PT', 25, 'F04F04F11F02', false, 'PT50000201231234567890154')) + addSpecification(new Specification('QA', 29, 'U04A21', false, 'QA30AAAA123456789012345678901')) + addSpecification(new Specification('RO', 24, 'U04A16', false, 'RO49AAAA1B31007593840000')) + addSpecification(new Specification('RS', 22, 'F03F13F02', false, 'RS35260005601001611379')) + addSpecification(new Specification('SA', 24, 'F02A18', false, 'SA0380000000608010167519')) + addSpecification(new Specification('SC', 31, 'U04F04F16U03', false, 'SC18SSCB11010000000000001497USD')) + addSpecification(new Specification('SE', 24, 'F03F16F01', false, 'SE4550000000058398257466')) + addSpecification(new Specification('SI', 19, 'F05F08F02', false, 'SI56263300012039086')) + addSpecification(new Specification('SK', 24, 'F04F06F10', false, 'SK3112000000198742637541')) + addSpecification(new Specification('SM', 27, 'U01F05F05A12', false, 'SM86U0322509800000000270100')) + addSpecification(new Specification('ST', 25, 'F08F11F02', false, 'ST68000100010051845310112')) + addSpecification(new Specification('SV', 28, 'U04F20', false, 'SV62CENR00000000000000700025')) + addSpecification(new Specification('TL', 23, 'F03F14F02', false, 'TL380080012345678910157')) + addSpecification(new Specification('TN', 24, 'F02F03F13F02', false, 'TN5910006035183598478831')) + addSpecification(new Specification('TR', 26, 'F05F01A16', false, 'TR330006100519786457841326')) + addSpecification(new Specification('UA', 29, 'F25', false, 'UA511234567890123456789012345')) + addSpecification(new Specification('VA', 22, 'F18', false, 'VA59001123000012345678')) + addSpecification(new Specification('VG', 24, 'U04F16', false, 'VG96VPVG0000012345678901')) + addSpecification(new Specification('XK', 20, 'F04F10F02', false, 'XK051212012345678906')) + + // The following countries are not included in the official ICAN registry but use the ICAN specification + + // Angola + addSpecification(new Specification('AO', 25, 'F21', false, 'AO69123456789012345678901')) + // Burkina + addSpecification(new Specification('BF', 27, 'F23', false, 'BF2312345678901234567890123')) + // Burundi + addSpecification(new Specification('BI', 16, 'F12', false, 'BI41123456789012')) + // Benin + addSpecification(new Specification('BJ', 28, 'F24', false, 'BJ39123456789012345678901234')) + // Ivory + addSpecification(new Specification('CI', 28, 'U02F22', false, 'CI70CI1234567890123456789012')) + // Cameron + addSpecification(new Specification('CM', 27, 'F23', false, 'CM9012345678901234567890123')) + // Cape Verde + addSpecification(new Specification('CV', 25, 'F21', false, 'CV30123456789012345678901')) + // Algeria + addSpecification(new Specification('DZ', 24, 'F20', false, 'DZ8612345678901234567890')) + // Iran + addSpecification(new Specification('IR', 26, 'F22', false, 'IR861234568790123456789012')) + // Madagascar + addSpecification(new Specification('MG', 27, 'F23', false, 'MG1812345678901234567890123')) + // Mali + addSpecification(new Specification('ML', 28, 'U01F23', false, 'ML15A12345678901234567890123')) + // Mozambique + addSpecification(new Specification('MZ', 25, 'F21', false, 'MZ25123456789012345678901')) + // Senegal + addSpecification(new Specification('SN', 28, 'U01F23', false, 'SN52A12345678901234567890123')) + + // The following are regional and administrative French Republic subdivision ICAN specification (same structure as FR, only country code vary) + addSpecification(new Specification('GF', 27, 'F05F05A11F02', false, 'GF121234512345123456789AB13')) + addSpecification(new Specification('GP', 27, 'F05F05A11F02', false, 'GP791234512345123456789AB13')) + addSpecification(new Specification('MQ', 27, 'F05F05A11F02', false, 'MQ221234512345123456789AB13')) + addSpecification(new Specification('RE', 27, 'F05F05A11F02', false, 'RE131234512345123456789AB13')) + addSpecification(new Specification('PF', 27, 'F05F05A11F02', false, 'PF281234512345123456789AB13')) + addSpecification(new Specification('TF', 27, 'F05F05A11F02', false, 'TF891234512345123456789AB13')) + addSpecification(new Specification('YT', 27, 'F05F05A11F02', false, 'YT021234512345123456789AB13')) + addSpecification(new Specification('NC', 27, 'F05F05A11F02', false, 'NC551234512345123456789AB13')) + addSpecification(new Specification('BL', 27, 'F05F05A11F02', false, 'BL391234512345123456789AB13')) + addSpecification(new Specification('MF', 27, 'F05F05A11F02', false, 'MF551234512345123456789AB13')) + addSpecification(new Specification('PM', 27, 'F05F05A11F02', false, 'PM071234512345123456789AB13')) + addSpecification(new Specification('WF', 27, 'F05F05A11F02', false, 'WF621234512345123456789AB13')) + + // Digital Assets + + // Core Blockchain Mainnet + addSpecification(new Specification('CB', 44, 'H40', true, 'CB661234567890ABCDEF1234567890ABCDEF12345678')) + // Core Blockchain Testnet - Devín + addSpecification(new Specification('AB', 44, 'H40', true, 'AB841234567890ABCDEF1234567890ABCDEF12345678')) + // Core Enterprise Blockchain Enterprise + addSpecification(new Specification('CE', 44, 'H40', true, 'CE571234567890ABCDEF1234567890ABCDEF12345678')) + + const NON_ALPHANUM = /[^a-zA-Z0-9]/g + const EVERY_FOUR_CHARS = /(.{4})(?!$)/g + + /** * Utility function to check if a variable is a String. * * @param v * @returns {boolean} true if the passed variable is a String, false otherwise. */ - function isString(v){ - return (typeof v == 'string' || v instanceof String) - } + function isString (v) { + return (typeof v === 'string' || v instanceof String) + } - /** + /** * Check if an ICAN is valid. * * @param {String} ican the ICAN to validate. * @returns {boolean} true if the passed ICAN is valid, false otherwise */ - exports.isValid = function(ican){ - if (!isString(ican)){ - return false - } - ican = electronicFormat(ican) - var countryStructure = countries[ican.slice(0,2)] - return !!countryStructure && countryStructure.isValid(ican) + exports.isValid = function (ican, onlyCrypto = false) { + if (!isString(ican)) { + return false } - - /** + ican = electronicFormat(ican) + const countryStructure = countries[ican.slice(0, 2)] + return !!countryStructure && + (!onlyCrypto || countryStructure.crypto) && + countryStructure.isValid(ican) + } + + /** * Convert an ICAN to a BCAN. * * @param ican * @param {String} [separator] the separator to use between the blocks of the BCAN, defaults to ' ' - * @returns {string|*} + * @returns {string} the BCAN */ - exports.toBCAN = function(ican, separator){ - if (typeof separator == 'undefined'){ - separator = ' ' - } - ican = electronicFormat(ican) - var countryStructure = countries[ican.slice(0,2)] - if (!countryStructure) { - throw new Error('No country with code ' + ican.slice(0,2)) - } - return countryStructure.toBCAN(ican, separator) + exports.toBCAN = function (ican, separator) { + if (typeof separator === 'undefined') { + separator = ' ' + } + ican = electronicFormat(ican) + const countryStructure = countries[ican.slice(0, 2)] + if (!countryStructure) { + throw new Error('No country with code ' + ican.slice(0, 2)) } + return countryStructure.toBCAN(ican, separator) + } - /** + /** * Convert the passed BCAN to an ICAN for this country specification. - * Please note that "generation of the ICAN shall be the exclusive responsibility of the bank/branch servicing the account". * This method implements the preferred algorithm described in http://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_ICAN_check_digits * * @param countryCode the country of the BCAN * @param bcan the BCAN to convert to ICAN * @returns {string} the ICAN */ - exports.fromBCAN = function(countryCode, bcan){ - var countryStructure = countries[countryCode] - if (!countryStructure) { - throw new Error('No country with code ' + countryCode) - } - return countryStructure.fromBCAN(electronicFormat(bcan)) + exports.fromBCAN = function (countryCode, bcan) { + const countryStructure = countries[countryCode] + if (!countryStructure) { + throw new Error('No country with code ' + countryCode) } + return countryStructure.fromBCAN(electronicFormat(bcan)) + } - /** + /** * Check the validity of the passed BCAN. * * @param countryCode the country of the BCAN * @param bcan the BCAN to check the validity of + * @param {Boolean} onlyCrypto check only digital assets + * @returns {boolean} true if the passed BCAN is a valid BCAN for the country, false otherwise */ - exports.isValidBCAN = function(countryCode, bcan){ - if (!isString(bcan)){ - return false - } - var countryStructure = countries[countryCode] - return countryStructure && countryStructure.isValidBCAN(electronicFormat(bcan)) + exports.isValidBCAN = function (countryCode, bcan, onlyCrypto = false) { + if (!isString(bcan)) { + return false } + const countryStructure = countries[countryCode] + return countryStructure && + (!onlyCrypto || countryStructure.crypto) && + countryStructure.isValidBCAN(electronicFormat(bcan)) + } - /** + /** * * @param ican * @param separator * @returns {string} */ - exports.printFormat = function(ican, separator){ - if (typeof separator == 'undefined'){ - separator = ' ' - } - return electronicFormat(ican).replace(EVERY_FOUR_CHARS, "$1" + separator) + exports.printFormat = function (ican, separator) { + if (typeof separator === 'undefined') { + separator = ' ' } + return electronicFormat(ican).replace(EVERY_FOUR_CHARS, '$1' + separator) + } - exports.electronicFormat = electronicFormat - /** + exports.electronicFormat = electronicFormat + /** * An object containing all the known ICAN specifications. */ - exports.countries = countries - + exports.countries = countries })) diff --git a/package.json b/package.json index e77833f..d50af06 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@blockchainhub/ican", - "version": "0.2.1", + "version": "0.3.0", "description": "A JavaScript library to validate, format, and convert International Crypto Account Numbers (ICAN) and Basic Crypto Account Numbers (BCAN).", "keywords": [ "ican", @@ -14,8 +14,8 @@ "convert" ], "devDependencies": { - "standard": "^17.0.0", - "tape": "^5.6.1" + "standard": "^17.1.0", + "tape": "^5.7.2" }, "scripts": { "build": "cp ican.js src/index.js && standard --fix 'src/index.js'", @@ -30,7 +30,7 @@ "main": "src/index.js", "types": "src/index.d.ts", "repository": "https://github.com/bchainhub/ican.js", - "author": "CRYPTO ▪ HUB®", + "author": "Blockchain Hub", "license": "CORE", "private": false, "engines": { diff --git a/src/index.d.ts b/src/index.d.ts index 10613a6..9252203 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -22,9 +22,10 @@ interface ICANStatic { /** * @summary Check if the passed ican is valid according to this specification. * @param ican The ican to validate. + * @param onlyCrypto Check only crypto definitions. * @returns True if valid, false otherwise. */ - isValid(ican: string): boolean; + isValid(ican: string, onlyCrypto: boolean): boolean; /** * @summary Check of the passed BCAN is valid. @@ -32,7 +33,7 @@ interface ICANStatic { * @param bcan The BCAN to validate. * @returns True if valid, false otherwise. */ - isValidBCAN(countryCode: string, bcan: string): boolean; + isValidBCAN(countryCode: string, bcan: string, onlyCrypto: boolean): boolean; /** * @summary Returns the ICAN in a print format. @@ -60,10 +61,12 @@ declare namespace ICAN { readonly length: number; /*& the structure of the underlying BCAN (for validation and formatting) */ readonly structure: string; + /** is digital asset */ + readonly crypto: boolean; /** an example valid ICAN */ readonly example: string; /** Check if the passed ican is valid according to this specification. */ - isValid(ican: string): boolean; + isValid(ican: string, onlyCrypto: boolean): boolean; /** * Convert the passed ICAN to a country-specific BCAN. */ @@ -79,7 +82,7 @@ declare namespace ICAN { * This function only checks the format of the BCAN (length and matching the letetr/number specs) but does not * verify the check digit. */ - isValidBCAN(bcan: string): boolean; + isValidBCAN(bcan: string, onlyCrypto: boolean): boolean; } } diff --git a/src/index.js b/src/index.js index cdb08e9..2ea396b 100644 --- a/src/index.js +++ b/src/index.js @@ -39,7 +39,7 @@ */ function iso13616Prepare (ican) { ican = ican.toUpperCase() - ican = ican.substr(4) + ican.substr(0, 4) + ican = ican.slice(4) + ican.slice(0, 4) return ican.split('').map(function (n) { const code = n.charCodeAt(0) @@ -138,10 +138,11 @@ * @param example an example valid ICAN * @constructor */ - function Specification (countryCode, length, structure, example) { + function Specification (countryCode, length, structure, crypto, example) { this.countryCode = countryCode this.length = length this.structure = structure + this.crypto = crypto this.example = example } @@ -156,11 +157,13 @@ * Check if the passed ican is valid according to this specification. * * @param {String} ican the ican to validate + * @param {Boolean} onlyCrypto check only digital assets * @returns {boolean} true if valid, false otherwise */ - Specification.prototype.isValid = function (ican) { + Specification.prototype.isValid = function (ican, onlyCrypto = false) { return this.length === ican.length && this.countryCode === ican.slice(0, 2) && + (!onlyCrypto || this.crypto) && this._regex().test(ican.slice(4)) && iso7064Mod97(iso13616Prepare(ican)) === 1 } @@ -178,7 +181,6 @@ /** * Convert the passed BCAN to an ICAN for this country specification. - * Please note that "generation of the ICAN shall be the exclusive responsibility of the bank/branch servicing the account". * This method implements the preferred algorithm described in http://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_ICAN_check_digits * * @param bcan the BCAN to convert to ICAN @@ -203,8 +205,9 @@ * @param bcan the BCAN to validate * @returns {boolean} true if the passed bcan is a valid BCAN according to this specification, false otherwise */ - Specification.prototype.isValidBCAN = function (bcan) { + Specification.prototype.isValidBCAN = function (bcan, onlyCrypto = false) { return this.length - 4 === bcan.length && + (!onlyCrypto || this.crypto) && this._regex().test(bcan) } @@ -214,135 +217,135 @@ countries[ICAN.countryCode] = ICAN } - addSpecification(new Specification('AD', 24, 'F04F04A12', 'AD1200012030200359100100')) - addSpecification(new Specification('AE', 23, 'F03F16', 'AE070331234567890123456')) - addSpecification(new Specification('AL', 28, 'F08A16', 'AL47212110090000000235698741')) - addSpecification(new Specification('AT', 20, 'F05F11', 'AT611904300234573201')) - addSpecification(new Specification('AZ', 28, 'U04A20', 'AZ21NABZ00000000137010001944')) - addSpecification(new Specification('BA', 20, 'F03F03F08F02', 'BA391290079401028494')) - addSpecification(new Specification('BE', 16, 'F03F07F02', 'BE68539007547034')) - addSpecification(new Specification('BG', 22, 'U04F04F02A08', 'BG80BNBG96611020345678')) - addSpecification(new Specification('BH', 22, 'U04A14', 'BH67BMAG00001299123456')) - addSpecification(new Specification('BR', 29, 'F08F05F10U01A01', 'BR9700360305000010009795493P1')) - addSpecification(new Specification('BY', 28, 'A04F04A16', 'BY13NBRB3600900000002Z00AB00')) - addSpecification(new Specification('CH', 21, 'F05A12', 'CH9300762011623852957')) - addSpecification(new Specification('CR', 22, 'F04F14', 'CR72012300000171549015')) - addSpecification(new Specification('CY', 28, 'F03F05A16', 'CY17002001280000001200527600')) - addSpecification(new Specification('CZ', 24, 'F04F06F10', 'CZ6508000000192000145399')) - addSpecification(new Specification('DE', 22, 'F08F10', 'DE89370400440532013000')) - addSpecification(new Specification('DK', 18, 'F04F09F01', 'DK5000400440116243')) - addSpecification(new Specification('DO', 28, 'U04F20', 'DO28BAGR00000001212453611324')) - addSpecification(new Specification('EE', 20, 'F02F02F11F01', 'EE382200221020145685')) - addSpecification(new Specification('EG', 29, 'F04F04F17', 'EG800002000156789012345180002')) - addSpecification(new Specification('ES', 24, 'F04F04F01F01F10', 'ES9121000418450200051332')) - addSpecification(new Specification('FI', 18, 'F06F07F01', 'FI2112345600000785')) - addSpecification(new Specification('FO', 18, 'F04F09F01', 'FO6264600001631634')) - addSpecification(new Specification('FR', 27, 'F05F05A11F02', 'FR1420041010050500013M02606')) - addSpecification(new Specification('GB', 22, 'U04F06F08', 'GB29NWBK60161331926819')) - addSpecification(new Specification('GE', 22, 'U02F16', 'GE29NB0000000101904917')) - addSpecification(new Specification('GI', 23, 'U04A15', 'GI75NWBK000000007099453')) - addSpecification(new Specification('GL', 18, 'F04F09F01', 'GL8964710001000206')) - addSpecification(new Specification('GR', 27, 'F03F04A16', 'GR1601101250000000012300695')) - addSpecification(new Specification('GT', 28, 'A04A20', 'GT82TRAJ01020000001210029690')) - addSpecification(new Specification('HR', 21, 'F07F10', 'HR1210010051863000160')) - addSpecification(new Specification('HU', 28, 'F03F04F01F15F01', 'HU42117730161111101800000000')) - addSpecification(new Specification('IE', 22, 'U04F06F08', 'IE29AIBK93115212345678')) - addSpecification(new Specification('IL', 23, 'F03F03F13', 'IL620108000000099999999')) - addSpecification(new Specification('IS', 26, 'F04F02F06F10', 'IS140159260076545510730339')) - addSpecification(new Specification('IT', 27, 'U01F05F05A12', 'IT60X0542811101000000123456')) - addSpecification(new Specification('IQ', 23, 'U04F03A12', 'IQ98NBIQ850123456789012')) - addSpecification(new Specification('JO', 30, 'A04F22', 'JO15AAAA1234567890123456789012')) - addSpecification(new Specification('KW', 30, 'U04A22', 'KW81CBKU0000000000001234560101')) - addSpecification(new Specification('KZ', 20, 'F03A13', 'KZ86125KZT5004100100')) - addSpecification(new Specification('LB', 28, 'F04A20', 'LB62099900000001001901229114')) - addSpecification(new Specification('LC', 32, 'U04F24', 'LC07HEMM000100010012001200013015')) - addSpecification(new Specification('LI', 21, 'F05A12', 'LI21088100002324013AA')) - addSpecification(new Specification('LT', 20, 'F05F11', 'LT121000011101001000')) - addSpecification(new Specification('LU', 20, 'F03A13', 'LU280019400644750000')) - addSpecification(new Specification('LV', 21, 'U04A13', 'LV80BANK0000435195001')) - addSpecification(new Specification('MC', 27, 'F05F05A11F02', 'MC5811222000010123456789030')) - addSpecification(new Specification('MD', 24, 'U02A18', 'MD24AG000225100013104168')) - addSpecification(new Specification('ME', 22, 'F03F13F02', 'ME25505000012345678951')) - addSpecification(new Specification('MK', 19, 'F03A10F02', 'MK07250120000058984')) - addSpecification(new Specification('MR', 27, 'F05F05F11F02', 'MR1300020001010000123456753')) - addSpecification(new Specification('MT', 31, 'U04F05A18', 'MT84MALT011000012345MTLCAST001S')) - addSpecification(new Specification('MU', 30, 'U04F02F02F12F03U03', 'MU17BOMM0101101030300200000MUR')) - addSpecification(new Specification('NL', 18, 'U04F10', 'NL91ABNA0417164300')) - addSpecification(new Specification('NO', 15, 'F04F06F01', 'NO9386011117947')) - addSpecification(new Specification('PK', 24, 'U04A16', 'PK36SCBL0000001123456702')) - addSpecification(new Specification('PL', 28, 'F08F16', 'PL61109010140000071219812874')) - addSpecification(new Specification('PS', 29, 'U04A21', 'PS92PALS000000000400123456702')) - addSpecification(new Specification('PT', 25, 'F04F04F11F02', 'PT50000201231234567890154')) - addSpecification(new Specification('QA', 29, 'U04A21', 'QA30AAAA123456789012345678901')) - addSpecification(new Specification('RO', 24, 'U04A16', 'RO49AAAA1B31007593840000')) - addSpecification(new Specification('RS', 22, 'F03F13F02', 'RS35260005601001611379')) - addSpecification(new Specification('SA', 24, 'F02A18', 'SA0380000000608010167519')) - addSpecification(new Specification('SC', 31, 'U04F04F16U03', 'SC18SSCB11010000000000001497USD')) - addSpecification(new Specification('SE', 24, 'F03F16F01', 'SE4550000000058398257466')) - addSpecification(new Specification('SI', 19, 'F05F08F02', 'SI56263300012039086')) - addSpecification(new Specification('SK', 24, 'F04F06F10', 'SK3112000000198742637541')) - addSpecification(new Specification('SM', 27, 'U01F05F05A12', 'SM86U0322509800000000270100')) - addSpecification(new Specification('ST', 25, 'F08F11F02', 'ST68000100010051845310112')) - addSpecification(new Specification('SV', 28, 'U04F20', 'SV62CENR00000000000000700025')) - addSpecification(new Specification('TL', 23, 'F03F14F02', 'TL380080012345678910157')) - addSpecification(new Specification('TN', 24, 'F02F03F13F02', 'TN5910006035183598478831')) - addSpecification(new Specification('TR', 26, 'F05F01A16', 'TR330006100519786457841326')) - addSpecification(new Specification('UA', 29, 'F25', 'UA511234567890123456789012345')) - addSpecification(new Specification('VA', 22, 'F18', 'VA59001123000012345678')) - addSpecification(new Specification('VG', 24, 'U04F16', 'VG96VPVG0000012345678901')) - addSpecification(new Specification('XK', 20, 'F04F10F02', 'XK051212012345678906')) + addSpecification(new Specification('AD', 24, 'F04F04A12', false, 'AD1200012030200359100100')) + addSpecification(new Specification('AE', 23, 'F03F16', false, 'AE070331234567890123456')) + addSpecification(new Specification('AL', 28, 'F08A16', false, 'AL47212110090000000235698741')) + addSpecification(new Specification('AT', 20, 'F05F11', false, 'AT611904300234573201')) + addSpecification(new Specification('AZ', 28, 'U04A20', false, 'AZ21NABZ00000000137010001944')) + addSpecification(new Specification('BA', 20, 'F03F03F08F02', false, 'BA391290079401028494')) + addSpecification(new Specification('BE', 16, 'F03F07F02', false, 'BE68539007547034')) + addSpecification(new Specification('BG', 22, 'U04F04F02A08', false, 'BG80BNBG96611020345678')) + addSpecification(new Specification('BH', 22, 'U04A14', false, 'BH67BMAG00001299123456')) + addSpecification(new Specification('BR', 29, 'F08F05F10U01A01', false, 'BR9700360305000010009795493P1')) + addSpecification(new Specification('BY', 28, 'A04F04A16', false, 'BY13NBRB3600900000002Z00AB00')) + addSpecification(new Specification('CH', 21, 'F05A12', false, 'CH9300762011623852957')) + addSpecification(new Specification('CR', 22, 'F04F14', false, 'CR72012300000171549015')) + addSpecification(new Specification('CY', 28, 'F03F05A16', false, 'CY17002001280000001200527600')) + addSpecification(new Specification('CZ', 24, 'F04F06F10', false, 'CZ6508000000192000145399')) + addSpecification(new Specification('DE', 22, 'F08F10', false, 'DE89370400440532013000')) + addSpecification(new Specification('DK', 18, 'F04F09F01', false, 'DK5000400440116243')) + addSpecification(new Specification('DO', 28, 'U04F20', false, 'DO28BAGR00000001212453611324')) + addSpecification(new Specification('EE', 20, 'F02F02F11F01', false, 'EE382200221020145685')) + addSpecification(new Specification('EG', 29, 'F04F04F17', false, 'EG800002000156789012345180002')) + addSpecification(new Specification('ES', 24, 'F04F04F01F01F10', false, 'ES9121000418450200051332')) + addSpecification(new Specification('FI', 18, 'F06F07F01', false, 'FI2112345600000785')) + addSpecification(new Specification('FO', 18, 'F04F09F01', false, 'FO6264600001631634')) + addSpecification(new Specification('FR', 27, 'F05F05A11F02', false, 'FR1420041010050500013M02606')) + addSpecification(new Specification('GB', 22, 'U04F06F08', false, 'GB29NWBK60161331926819')) + addSpecification(new Specification('GE', 22, 'U02F16', false, 'GE29NB0000000101904917')) + addSpecification(new Specification('GI', 23, 'U04A15', false, 'GI75NWBK000000007099453')) + addSpecification(new Specification('GL', 18, 'F04F09F01', false, 'GL8964710001000206')) + addSpecification(new Specification('GR', 27, 'F03F04A16', false, 'GR1601101250000000012300695')) + addSpecification(new Specification('GT', 28, 'A04A20', false, 'GT82TRAJ01020000001210029690')) + addSpecification(new Specification('HR', 21, 'F07F10', false, 'HR1210010051863000160')) + addSpecification(new Specification('HU', 28, 'F03F04F01F15F01', false, 'HU42117730161111101800000000')) + addSpecification(new Specification('IE', 22, 'U04F06F08', false, 'IE29AIBK93115212345678')) + addSpecification(new Specification('IL', 23, 'F03F03F13', false, 'IL620108000000099999999')) + addSpecification(new Specification('IS', 26, 'F04F02F06F10', false, 'IS140159260076545510730339')) + addSpecification(new Specification('IT', 27, 'U01F05F05A12', false, 'IT60X0542811101000000123456')) + addSpecification(new Specification('IQ', 23, 'U04F03A12', false, 'IQ98NBIQ850123456789012')) + addSpecification(new Specification('JO', 30, 'A04F22', false, 'JO15AAAA1234567890123456789012')) + addSpecification(new Specification('KW', 30, 'U04A22', false, 'KW81CBKU0000000000001234560101')) + addSpecification(new Specification('KZ', 20, 'F03A13', false, 'KZ86125KZT5004100100')) + addSpecification(new Specification('LB', 28, 'F04A20', false, 'LB62099900000001001901229114')) + addSpecification(new Specification('LC', 32, 'U04F24', false, 'LC07HEMM000100010012001200013015')) + addSpecification(new Specification('LI', 21, 'F05A12', false, 'LI21088100002324013AA')) + addSpecification(new Specification('LT', 20, 'F05F11', false, 'LT121000011101001000')) + addSpecification(new Specification('LU', 20, 'F03A13', false, 'LU280019400644750000')) + addSpecification(new Specification('LV', 21, 'U04A13', false, 'LV80BANK0000435195001')) + addSpecification(new Specification('MC', 27, 'F05F05A11F02', false, 'MC5811222000010123456789030')) + addSpecification(new Specification('MD', 24, 'U02A18', false, 'MD24AG000225100013104168')) + addSpecification(new Specification('ME', 22, 'F03F13F02', false, 'ME25505000012345678951')) + addSpecification(new Specification('MK', 19, 'F03A10F02', false, 'MK07250120000058984')) + addSpecification(new Specification('MR', 27, 'F05F05F11F02', false, 'MR1300020001010000123456753')) + addSpecification(new Specification('MT', 31, 'U04F05A18', false, 'MT84MALT011000012345MTLCAST001S')) + addSpecification(new Specification('MU', 30, 'U04F02F02F12F03U03', false, 'MU17BOMM0101101030300200000MUR')) + addSpecification(new Specification('NL', 18, 'U04F10', false, 'NL91ABNA0417164300')) + addSpecification(new Specification('NO', 15, 'F04F06F01', false, 'NO9386011117947')) + addSpecification(new Specification('PK', 24, 'U04A16', false, 'PK36SCBL0000001123456702')) + addSpecification(new Specification('PL', 28, 'F08F16', false, 'PL61109010140000071219812874')) + addSpecification(new Specification('PS', 29, 'U04A21', false, 'PS92PALS000000000400123456702')) + addSpecification(new Specification('PT', 25, 'F04F04F11F02', false, 'PT50000201231234567890154')) + addSpecification(new Specification('QA', 29, 'U04A21', false, 'QA30AAAA123456789012345678901')) + addSpecification(new Specification('RO', 24, 'U04A16', false, 'RO49AAAA1B31007593840000')) + addSpecification(new Specification('RS', 22, 'F03F13F02', false, 'RS35260005601001611379')) + addSpecification(new Specification('SA', 24, 'F02A18', false, 'SA0380000000608010167519')) + addSpecification(new Specification('SC', 31, 'U04F04F16U03', false, 'SC18SSCB11010000000000001497USD')) + addSpecification(new Specification('SE', 24, 'F03F16F01', false, 'SE4550000000058398257466')) + addSpecification(new Specification('SI', 19, 'F05F08F02', false, 'SI56263300012039086')) + addSpecification(new Specification('SK', 24, 'F04F06F10', false, 'SK3112000000198742637541')) + addSpecification(new Specification('SM', 27, 'U01F05F05A12', false, 'SM86U0322509800000000270100')) + addSpecification(new Specification('ST', 25, 'F08F11F02', false, 'ST68000100010051845310112')) + addSpecification(new Specification('SV', 28, 'U04F20', false, 'SV62CENR00000000000000700025')) + addSpecification(new Specification('TL', 23, 'F03F14F02', false, 'TL380080012345678910157')) + addSpecification(new Specification('TN', 24, 'F02F03F13F02', false, 'TN5910006035183598478831')) + addSpecification(new Specification('TR', 26, 'F05F01A16', false, 'TR330006100519786457841326')) + addSpecification(new Specification('UA', 29, 'F25', false, 'UA511234567890123456789012345')) + addSpecification(new Specification('VA', 22, 'F18', false, 'VA59001123000012345678')) + addSpecification(new Specification('VG', 24, 'U04F16', false, 'VG96VPVG0000012345678901')) + addSpecification(new Specification('XK', 20, 'F04F10F02', false, 'XK051212012345678906')) // The following countries are not included in the official ICAN registry but use the ICAN specification // Angola - addSpecification(new Specification('AO', 25, 'F21', 'AO69123456789012345678901')) + addSpecification(new Specification('AO', 25, 'F21', false, 'AO69123456789012345678901')) // Burkina - addSpecification(new Specification('BF', 27, 'F23', 'BF2312345678901234567890123')) + addSpecification(new Specification('BF', 27, 'F23', false, 'BF2312345678901234567890123')) // Burundi - addSpecification(new Specification('BI', 16, 'F12', 'BI41123456789012')) + addSpecification(new Specification('BI', 16, 'F12', false, 'BI41123456789012')) // Benin - addSpecification(new Specification('BJ', 28, 'F24', 'BJ39123456789012345678901234')) + addSpecification(new Specification('BJ', 28, 'F24', false, 'BJ39123456789012345678901234')) // Ivory - addSpecification(new Specification('CI', 28, 'U02F22', 'CI70CI1234567890123456789012')) + addSpecification(new Specification('CI', 28, 'U02F22', false, 'CI70CI1234567890123456789012')) // Cameron - addSpecification(new Specification('CM', 27, 'F23', 'CM9012345678901234567890123')) + addSpecification(new Specification('CM', 27, 'F23', false, 'CM9012345678901234567890123')) // Cape Verde - addSpecification(new Specification('CV', 25, 'F21', 'CV30123456789012345678901')) + addSpecification(new Specification('CV', 25, 'F21', false, 'CV30123456789012345678901')) // Algeria - addSpecification(new Specification('DZ', 24, 'F20', 'DZ8612345678901234567890')) + addSpecification(new Specification('DZ', 24, 'F20', false, 'DZ8612345678901234567890')) // Iran - addSpecification(new Specification('IR', 26, 'F22', 'IR861234568790123456789012')) + addSpecification(new Specification('IR', 26, 'F22', false, 'IR861234568790123456789012')) // Madagascar - addSpecification(new Specification('MG', 27, 'F23', 'MG1812345678901234567890123')) + addSpecification(new Specification('MG', 27, 'F23', false, 'MG1812345678901234567890123')) // Mali - addSpecification(new Specification('ML', 28, 'U01F23', 'ML15A12345678901234567890123')) + addSpecification(new Specification('ML', 28, 'U01F23', false, 'ML15A12345678901234567890123')) // Mozambique - addSpecification(new Specification('MZ', 25, 'F21', 'MZ25123456789012345678901')) + addSpecification(new Specification('MZ', 25, 'F21', false, 'MZ25123456789012345678901')) // Senegal - addSpecification(new Specification('SN', 28, 'U01F23', 'SN52A12345678901234567890123')) + addSpecification(new Specification('SN', 28, 'U01F23', false, 'SN52A12345678901234567890123')) // The following are regional and administrative French Republic subdivision ICAN specification (same structure as FR, only country code vary) - addSpecification(new Specification('GF', 27, 'F05F05A11F02', 'GF121234512345123456789AB13')) - addSpecification(new Specification('GP', 27, 'F05F05A11F02', 'GP791234512345123456789AB13')) - addSpecification(new Specification('MQ', 27, 'F05F05A11F02', 'MQ221234512345123456789AB13')) - addSpecification(new Specification('RE', 27, 'F05F05A11F02', 'RE131234512345123456789AB13')) - addSpecification(new Specification('PF', 27, 'F05F05A11F02', 'PF281234512345123456789AB13')) - addSpecification(new Specification('TF', 27, 'F05F05A11F02', 'TF891234512345123456789AB13')) - addSpecification(new Specification('YT', 27, 'F05F05A11F02', 'YT021234512345123456789AB13')) - addSpecification(new Specification('NC', 27, 'F05F05A11F02', 'NC551234512345123456789AB13')) - addSpecification(new Specification('BL', 27, 'F05F05A11F02', 'BL391234512345123456789AB13')) - addSpecification(new Specification('MF', 27, 'F05F05A11F02', 'MF551234512345123456789AB13')) - addSpecification(new Specification('PM', 27, 'F05F05A11F02', 'PM071234512345123456789AB13')) - addSpecification(new Specification('WF', 27, 'F05F05A11F02', 'WF621234512345123456789AB13')) - - // Cryptocurrency addresses + addSpecification(new Specification('GF', 27, 'F05F05A11F02', false, 'GF121234512345123456789AB13')) + addSpecification(new Specification('GP', 27, 'F05F05A11F02', false, 'GP791234512345123456789AB13')) + addSpecification(new Specification('MQ', 27, 'F05F05A11F02', false, 'MQ221234512345123456789AB13')) + addSpecification(new Specification('RE', 27, 'F05F05A11F02', false, 'RE131234512345123456789AB13')) + addSpecification(new Specification('PF', 27, 'F05F05A11F02', false, 'PF281234512345123456789AB13')) + addSpecification(new Specification('TF', 27, 'F05F05A11F02', false, 'TF891234512345123456789AB13')) + addSpecification(new Specification('YT', 27, 'F05F05A11F02', false, 'YT021234512345123456789AB13')) + addSpecification(new Specification('NC', 27, 'F05F05A11F02', false, 'NC551234512345123456789AB13')) + addSpecification(new Specification('BL', 27, 'F05F05A11F02', false, 'BL391234512345123456789AB13')) + addSpecification(new Specification('MF', 27, 'F05F05A11F02', false, 'MF551234512345123456789AB13')) + addSpecification(new Specification('PM', 27, 'F05F05A11F02', false, 'PM071234512345123456789AB13')) + addSpecification(new Specification('WF', 27, 'F05F05A11F02', false, 'WF621234512345123456789AB13')) + + // Digital Assets // Core Blockchain Mainnet - addSpecification(new Specification('CB', 44, 'H40', 'CB661234567890ABCDEF1234567890ABCDEF12345678')) - // Core Blockchain Testnet - Devin - addSpecification(new Specification('AB', 44, 'H40', 'AB841234567890ABCDEF1234567890ABCDEF12345678')) + addSpecification(new Specification('CB', 44, 'H40', true, 'CB661234567890ABCDEF1234567890ABCDEF12345678')) + // Core Blockchain Testnet - Devín + addSpecification(new Specification('AB', 44, 'H40', true, 'AB841234567890ABCDEF1234567890ABCDEF12345678')) // Core Enterprise Blockchain Enterprise - addSpecification(new Specification('CE', 44, 'H40', 'CE571234567890ABCDEF1234567890ABCDEF12345678')) + addSpecification(new Specification('CE', 44, 'H40', true, 'CE571234567890ABCDEF1234567890ABCDEF12345678')) const NON_ALPHANUM = /[^a-zA-Z0-9]/g const EVERY_FOUR_CHARS = /(.{4})(?!$)/g @@ -363,13 +366,15 @@ * @param {String} ican the ICAN to validate. * @returns {boolean} true if the passed ICAN is valid, false otherwise */ - exports.isValid = function (ican) { + exports.isValid = function (ican, onlyCrypto = false) { if (!isString(ican)) { return false } ican = electronicFormat(ican) const countryStructure = countries[ican.slice(0, 2)] - return !!countryStructure && countryStructure.isValid(ican) + return !!countryStructure && + (!onlyCrypto || countryStructure.crypto) && + countryStructure.isValid(ican) } /** @@ -377,7 +382,7 @@ * * @param ican * @param {String} [separator] the separator to use between the blocks of the BCAN, defaults to ' ' - * @returns {string|*} + * @returns {string} the BCAN */ exports.toBCAN = function (ican, separator) { if (typeof separator === 'undefined') { @@ -393,7 +398,6 @@ /** * Convert the passed BCAN to an ICAN for this country specification. - * Please note that "generation of the ICAN shall be the exclusive responsibility of the bank/branch servicing the account". * This method implements the preferred algorithm described in http://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_ICAN_check_digits * * @param countryCode the country of the BCAN @@ -413,13 +417,17 @@ * * @param countryCode the country of the BCAN * @param bcan the BCAN to check the validity of + * @param {Boolean} onlyCrypto check only digital assets + * @returns {boolean} true if the passed BCAN is a valid BCAN for the country, false otherwise */ - exports.isValidBCAN = function (countryCode, bcan) { + exports.isValidBCAN = function (countryCode, bcan, onlyCrypto = false) { if (!isString(bcan)) { return false } const countryStructure = countries[countryCode] - return countryStructure && countryStructure.isValidBCAN(electronicFormat(bcan)) + return countryStructure && + (!onlyCrypto || countryStructure.crypto) && + countryStructure.isValidBCAN(electronicFormat(bcan)) } /** diff --git a/test/index.js b/test/index.js index 62f17d1..22d1564 100644 --- a/test/index.js +++ b/test/index.js @@ -18,6 +18,22 @@ samples.invalid.forEach(function (f) { }) }) +samples.validCrypto.forEach(function (f) { + tape.test('OK - Check Crypto ICAN. // ICAN: <' + f.ican.substring(0, 4) + f.ican.slice(-4) + '>', function (t) { + let valid = ICAN.isValid(f.ican, true) + t.plan(1) + t.ok(valid, 'Valid ICAN: ' + f.ican) + }) +}) + +samples.invalidCrypto.forEach(function (f) { + tape.test('NOK - Check Crypto ICAN. // ICAN: <' + f.ican.substring(0, 4) + f.ican.slice(-4) + '>', function (t) { + let valid = ICAN.isValid(f.ican, true) + t.plan(1) + t.notOk(valid, 'Invalid ICAN: ' + f.ican) + }) +}) + samples.print.forEach(function (f) { tape.test('OK - Print format. // ICAN: <' + f.ican.substring(0, 4) + f.ican.slice(-4) + '>', function (t) { let print = ICAN.printFormat(f.ican) diff --git a/test/samples.json b/test/samples.json index 2332fcd..2e60099 100644 --- a/test/samples.json +++ b/test/samples.json @@ -44,7 +44,25 @@ { "ican": "ab30Pe3b05b80977a67bcbd330ab5f8daf756fb4191c" } - + ], + "validCrypto": [ + { + "ican": "CB7147879011EA207DF5B35A24CA6F0859DCFB145999" + }, + { + "ican": "AB306E3B05B80977A67BCBD330AB5F8DAF756FB4191C" + } + ], + "invalidCrypto": [ + { + "ican": "LC01HEMM000100010012001200023015" + }, + { + "ican": "ZZ68539007547034" + }, + { + "ican": "Hello" + } ], "print": [ {