|
| 1 | +/* |
| 2 | + This file is part of ethereum.js. |
| 3 | +
|
| 4 | + ethereum.js is free software: you can redistribute it and/or modify |
| 5 | + it under the terms of the GNU Lesser General Public License as published by |
| 6 | + the Free Software Foundation, either version 3 of the License, or |
| 7 | + (at your option) any later version. |
| 8 | +
|
| 9 | + ethereum.js is distributed in the hope that it will be useful, |
| 10 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | + GNU Lesser General Public License for more details. |
| 13 | +
|
| 14 | + You should have received a copy of the GNU Lesser General Public License |
| 15 | + along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. |
| 16 | +*/ |
| 17 | +/** |
| 18 | + * @file iban.js |
| 19 | + * @author Marek Kotewicz <marek@ethdev.com> |
| 20 | + * @date 2015 |
| 21 | + */ |
| 22 | + |
| 23 | +var BigNumber = require('bignumber.js'); |
| 24 | + |
| 25 | +var padLeft = function (string, bytes) { |
| 26 | + var result = string; |
| 27 | + while (result.length < bytes * 2) { |
| 28 | + result = '00' + result; |
| 29 | + } |
| 30 | + return result; |
| 31 | +}; |
| 32 | + |
| 33 | +/** |
| 34 | + * Prepare an IBAN for mod 97 computation by moving the first 4 chars to the end and transforming the letters to |
| 35 | + * numbers (A = 10, B = 11, ..., Z = 35), as specified in ISO13616. |
| 36 | + * |
| 37 | + * @method iso13616Prepare |
| 38 | + * @param {String} iban the IBAN |
| 39 | + * @returns {String} the prepared IBAN |
| 40 | + */ |
| 41 | +var iso13616Prepare = function (iban) { |
| 42 | + var A = 'A'.charCodeAt(0); |
| 43 | + var Z = 'Z'.charCodeAt(0); |
| 44 | + |
| 45 | + iban = iban.toUpperCase(); |
| 46 | + iban = iban.substr(4) + iban.substr(0,4); |
| 47 | + |
| 48 | + return iban.split('').map(function(n){ |
| 49 | + var code = n.charCodeAt(0); |
| 50 | + if (code >= A && code <= Z){ |
| 51 | + // A = 10, B = 11, ... Z = 35 |
| 52 | + return code - A + 10; |
| 53 | + } else { |
| 54 | + return n; |
| 55 | + } |
| 56 | + }).join(''); |
| 57 | +}; |
| 58 | + |
| 59 | +/** |
| 60 | + * Calculates the MOD 97 10 of the passed IBAN as specified in ISO7064. |
| 61 | + * |
| 62 | + * @method mod9710 |
| 63 | + * @param {String} iban |
| 64 | + * @returns {Number} |
| 65 | + */ |
| 66 | +var mod9710 = function (iban) { |
| 67 | + var remainder = iban, |
| 68 | + block; |
| 69 | + |
| 70 | + while (remainder.length > 2){ |
| 71 | + block = remainder.slice(0, 9); |
| 72 | + remainder = parseInt(block, 10) % 97 + remainder.slice(block.length); |
| 73 | + } |
| 74 | + |
| 75 | + return parseInt(remainder, 10) % 97; |
| 76 | +}; |
| 77 | + |
| 78 | +/** |
| 79 | + * This prototype should be used to create iban object from iban correct string |
| 80 | + * |
| 81 | + * @param {String} iban |
| 82 | + */ |
| 83 | +var Iban = function (iban) { |
| 84 | + this._iban = iban; |
| 85 | +}; |
| 86 | + |
| 87 | +/** |
| 88 | + * This method should be used to create iban object from ethereum address |
| 89 | + * |
| 90 | + * @method fromAddress |
| 91 | + * @param {String} address |
| 92 | + * @return {Iban} the IBAN object |
| 93 | + */ |
| 94 | +Iban.fromAddress = function (address) { |
| 95 | + var asBn = new BigNumber(address, 16); |
| 96 | + var base36 = asBn.toString(36); |
| 97 | + var padded = padLeft(base36, 15); |
| 98 | + return Iban.fromBban(padded.toUpperCase()); |
| 99 | +}; |
| 100 | + |
| 101 | +/** |
| 102 | + * Convert the passed BBAN to an IBAN for this country specification. |
| 103 | + * Please note that <i>"generation of the IBAN shall be the exclusive responsibility of the bank/branch servicing the account"</i>. |
| 104 | + * This method implements the preferred algorithm described in http://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits |
| 105 | + * |
| 106 | + * @method fromBban |
| 107 | + * @param {String} bban the BBAN to convert to IBAN |
| 108 | + * @returns {Iban} the IBAN object |
| 109 | + */ |
| 110 | +Iban.fromBban = function (bban) { |
| 111 | + var countryCode = 'XE'; |
| 112 | + |
| 113 | + var remainder = mod9710(iso13616Prepare(countryCode + '00' + bban)); |
| 114 | + var checkDigit = ('0' + (98 - remainder)).slice(-2); |
| 115 | + |
| 116 | + return new Iban(countryCode + checkDigit + bban); |
| 117 | +}; |
| 118 | + |
| 119 | +/** |
| 120 | + * Should be used to create IBAN object for given institution and identifier |
| 121 | + * |
| 122 | + * @method createIndirect |
| 123 | + * @param {Object} options, required options are "institution" and "identifier" |
| 124 | + * @return {Iban} the IBAN object |
| 125 | + */ |
| 126 | +Iban.createIndirect = function (options) { |
| 127 | + return Iban.fromBban('ETH' + options.institution + options.identifier); |
| 128 | +}; |
| 129 | + |
| 130 | +/** |
| 131 | + * Thos method should be used to check if given string is valid iban object |
| 132 | + * |
| 133 | + * @method isValid |
| 134 | + * @param {String} iban string |
| 135 | + * @return {Boolean} true if it is valid IBAN |
| 136 | + */ |
| 137 | +Iban.isValid = function (iban) { |
| 138 | + var i = new Iban(iban); |
| 139 | + return i.isValid(); |
| 140 | +}; |
| 141 | + |
| 142 | +/** |
| 143 | + * Should be called to check if iban is correct |
| 144 | + * |
| 145 | + * @method isValid |
| 146 | + * @returns {Boolean} true if it is, otherwise false |
| 147 | + */ |
| 148 | +Iban.prototype.isValid = function () { |
| 149 | + return /^XE[0-9]{2}(ETH[0-9A-Z]{13}|[0-9A-Z]{30})$/.test(this._iban) && |
| 150 | + mod9710(iso13616Prepare(this._iban)) === 1; |
| 151 | +}; |
| 152 | + |
| 153 | +/** |
| 154 | + * Should be called to check if iban number is direct |
| 155 | + * |
| 156 | + * @method isDirect |
| 157 | + * @returns {Boolean} true if it is, otherwise false |
| 158 | + */ |
| 159 | +Iban.prototype.isDirect = function () { |
| 160 | + return this._iban.length === 34; |
| 161 | +}; |
| 162 | + |
| 163 | +/** |
| 164 | + * Should be called to check if iban number if indirect |
| 165 | + * |
| 166 | + * @method isIndirect |
| 167 | + * @returns {Boolean} true if it is, otherwise false |
| 168 | + */ |
| 169 | +Iban.prototype.isIndirect = function () { |
| 170 | + return this._iban.length === 20; |
| 171 | +}; |
| 172 | + |
| 173 | +/** |
| 174 | + * Should be called to get iban checksum |
| 175 | + * Uses the mod-97-10 checksumming protocol (ISO/IEC 7064:2003) |
| 176 | + * |
| 177 | + * @method checksum |
| 178 | + * @returns {String} checksum |
| 179 | + */ |
| 180 | +Iban.prototype.checksum = function () { |
| 181 | + return this._iban.substr(2, 2); |
| 182 | +}; |
| 183 | + |
| 184 | +/** |
| 185 | + * Should be called to get institution identifier |
| 186 | + * eg. XREG |
| 187 | + * |
| 188 | + * @method institution |
| 189 | + * @returns {String} institution identifier |
| 190 | + */ |
| 191 | +Iban.prototype.institution = function () { |
| 192 | + return this.isIndirect() ? this._iban.substr(7, 4) : ''; |
| 193 | +}; |
| 194 | + |
| 195 | +/** |
| 196 | + * Should be called to get client identifier within institution |
| 197 | + * eg. GAVOFYORK |
| 198 | + * |
| 199 | + * @method client |
| 200 | + * @returns {String} client identifier |
| 201 | + */ |
| 202 | +Iban.prototype.client = function () { |
| 203 | + return this.isIndirect() ? this._iban.substr(11) : ''; |
| 204 | +}; |
| 205 | + |
| 206 | +/** |
| 207 | + * Should be called to get client direct address |
| 208 | + * |
| 209 | + * @method address |
| 210 | + * @returns {String} client direct address |
| 211 | + */ |
| 212 | +Iban.prototype.address = function () { |
| 213 | + if (this.isDirect()) { |
| 214 | + var base36 = this._iban.substr(4); |
| 215 | + var asBn = new BigNumber(base36, 36); |
| 216 | + return padLeft(asBn.toString(16), 20); |
| 217 | + } |
| 218 | + |
| 219 | + return ''; |
| 220 | +}; |
| 221 | + |
| 222 | +module.exports = Iban; |
| 223 | + |
0 commit comments