/* This file is part of web3.js. web3.js is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. web3.js 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ /** * @file iban.js * * Details: https://github.com/ethereum/wiki/wiki/ICAP:-Inter-exchange-Client-Address-Protocol * * @author Marek Kotewicz * @date 2015 */ "use strict"; var utils = require('web3-utils'); var BigNumber = require('bn.js'); var leftPad = function (string, bytes) { var result = string; while (result.length < bytes * 2) { result = '0' + result; } return result; }; /** * Prepare an IBAN 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. * * @method iso13616Prepare * @param {String} iban the IBAN * @returns {String} the prepared IBAN */ var iso13616Prepare = function (iban) { var A = 'A'.charCodeAt(0); var Z = 'Z'.charCodeAt(0); iban = iban.toUpperCase(); iban = iban.substr(4) + iban.substr(0, 4); return iban.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(''); }; /** * Calculates the MOD 97 10 of the passed IBAN as specified in ISO7064. * * @method mod9710 * @param {String} iban * @returns {Number} */ var mod9710 = function (iban) { var remainder = iban, block; while (remainder.length > 2) { block = remainder.slice(0, 9); remainder = parseInt(block, 10) % 97 + remainder.slice(block.length); } return parseInt(remainder, 10) % 97; }; /** * This prototype should be used to create iban object from iban correct string * * @param {String} iban */ var Iban = function Iban(iban) { this._iban = iban; }; /** * This method should be used to create an ethereum address from a direct iban address * * @method toAddress * @param {String} iban address * @return {String} the ethereum address */ Iban.toAddress = function (ib) { ib = new Iban(ib); if (!ib.isDirect()) { throw new Error('IBAN is indirect and can\'t be converted'); } return ib.toAddress(); }; /** * This method should be used to create iban address from an ethereum address * * @method toIban * @param {String} address * @return {String} the IBAN address */ Iban.toIban = function (address) { return Iban.fromAddress(address).toString(); }; /** * This method should be used to create iban object from an ethereum address * * @method fromAddress * @param {String} address * @return {Iban} the IBAN object */ Iban.fromAddress = function (address) { if (!utils.isAddress(address)) { throw new Error('Provided address is not a valid address: ' + address); } address = address.replace('0x', '').replace('0X', ''); var asBn = new BigNumber(address, 16); var base36 = asBn.toString(36); var padded = leftPad(base36, 15); return Iban.fromBban(padded.toUpperCase()); }; /** * Convert the passed BBAN to an IBAN for this country specification. * Please note that "generation of the IBAN 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_IBAN_check_digits * * @method fromBban * @param {String} bban the BBAN to convert to IBAN * @returns {Iban} the IBAN object */ Iban.fromBban = function (bban) { var countryCode = 'XE'; var remainder = mod9710(iso13616Prepare(countryCode + '00' + bban)); var checkDigit = ('0' + (98 - remainder)).slice(-2); return new Iban(countryCode + checkDigit + bban); }; /** * Should be used to create IBAN object for given institution and identifier * * @method createIndirect * @param {Object} options, required options are "institution" and "identifier" * @return {Iban} the IBAN object */ Iban.createIndirect = function (options) { return Iban.fromBban('ETH' + options.institution + options.identifier); }; /** * This method should be used to check if given string is valid iban object * * @method isValid * @param {String} iban string * @return {Boolean} true if it is valid IBAN */ Iban.isValid = function (iban) { var i = new Iban(iban); return i.isValid(); }; /** * Should be called to check if iban is correct * * @method isValid * @returns {Boolean} true if it is, otherwise false */ Iban.prototype.isValid = function () { return /^XE[0-9]{2}(ETH[0-9A-Z]{13}|[0-9A-Z]{30,31})$/.test(this._iban) && mod9710(iso13616Prepare(this._iban)) === 1; }; /** * Should be called to check if iban number is direct * * @method isDirect * @returns {Boolean} true if it is, otherwise false */ Iban.prototype.isDirect = function () { return this._iban.length === 34 || this._iban.length === 35; }; /** * Should be called to check if iban number if indirect * * @method isIndirect * @returns {Boolean} true if it is, otherwise false */ Iban.prototype.isIndirect = function () { return this._iban.length === 20; }; /** * Should be called to get iban checksum * Uses the mod-97-10 checksumming protocol (ISO/IEC 7064:2003) * * @method checksum * @returns {String} checksum */ Iban.prototype.checksum = function () { return this._iban.substr(2, 2); }; /** * Should be called to get institution identifier * eg. XREG * * @method institution * @returns {String} institution identifier */ Iban.prototype.institution = function () { return this.isIndirect() ? this._iban.substr(7, 4) : ''; }; /** * Should be called to get client identifier within institution * eg. GAVOFYORK * * @method client * @returns {String} client identifier */ Iban.prototype.client = function () { return this.isIndirect() ? this._iban.substr(11) : ''; }; /** * Should be called to get client direct address * * @method toAddress * @returns {String} ethereum address */ Iban.prototype.toAddress = function () { if (this.isDirect()) { var base36 = this._iban.substr(4); var asBn = new BigNumber(base36, 36); return utils.toChecksumAddress(asBn.toString(16, 20)); } return ''; }; Iban.prototype.toString = function () { return this._iban; }; module.exports = Iban;