This repository has been archived by the owner on Oct 31, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 21
/
index.js
205 lines (176 loc) · 4.76 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// For simplicity we redefine it, as the default uses lowercase
var BASE36_ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
var bs36 = require('base-x')(BASE36_ALPHABET)
var ICAP = {}
ICAP.decodeBBAN = function (bban) {
var length = bban.length
if (length === 30 || length === 31) {
var tmp = bs36.decode(bban).toString('hex')
// FIXME: horrible padding code
while (tmp.length < 40) {
tmp = '0' + tmp
}
// NOTE: certain tools include an extra leading 0, drop that
if ((tmp.length === 42) && (tmp[0] === '0') && (tmp[1] === '0')) {
tmp = tmp.slice(2)
}
return '0x' + tmp
} else if (length === 16) {
return {
asset: bban.slice(0, 3),
institution: bban.slice(3, 7),
client: bban.slice(7, 16)
}
} else {
throw new Error('Not a valid Ethereum BBAN')
}
}
ICAP.encodeBBAN = function (bban) {
if (typeof bban === 'object') {
if (bban.asset.length !== 3 ||
bban.institution.length !== 4 ||
bban.client.length !== 9) {
throw new Error('Invalid \'indirect\' Ethereum BBAN')
}
return [ bban.asset, bban.institution, bban.client ].join('').toUpperCase()
} else if ((bban.length === 42) && (bban[0] === '0') && (bban[1] === 'x')) {
// Workaround for base-x, see https://github.com/cryptocoinjs/base-x/issues/18
if ((bban[2] === '0') && (bban[3] === '0')) {
bban = bban.slice(4)
} else {
bban = bban.slice(2)
}
return bs36.encode(Buffer.from(bban, 'hex'))
} else {
throw new Error('Not a valid input for Ethereum BBAN')
}
}
// ISO13616 reordering and letter translation
// NOTE: we assume input is uppercase only
// based off code from iban.js
function prepare (iban) {
// move front to the back
iban = iban.slice(4) + iban.slice(0, 4)
// translate letters to numbers
return iban.split('').map(function (n) {
var code = n.charCodeAt(0)
// 65 == A, 90 == Z in ASCII
if (code >= 65 && code <= 90) {
// A = 10, B = 11, ... Z = 35
return code - 65 + 10
} else {
return n
}
}).join('')
}
// Calculate ISO7064 mod 97-10
// NOTE: assumes all numeric input string
function mod9710 (input) {
var m = 0
for (var i = 0; i < input.length; i++) {
m *= 10
m += input.charCodeAt(i) - 48 // parseInt()
m %= 97
}
return m
}
ICAP.encode = function (bban, print) {
bban = ICAP.encodeBBAN(bban)
var checksum = 98 - mod9710(prepare('XE00' + bban))
// format into 2 digits
checksum = ('0' + checksum).slice(-2)
var iban = 'XE' + checksum + bban
if (print === true) {
// split a group of 4 chars with spaces
iban = iban.replace(/(.{4})/g, '$1 ')
}
return iban
}
ICAP.decode = function (iban, novalidity) {
// change from 'print format' to 'electronic format', e.g. remove spaces
iban = iban.replace(/ /g, '')
// check for validity
if (!novalidity) {
if (iban.slice(0, 2) !== 'XE') {
throw new Error('Not in ICAP format')
}
if (mod9710(prepare(iban)) !== 1) {
throw new Error('Invalid checksum in IBAN')
}
}
return ICAP.decodeBBAN(iban.slice(4, 35))
}
/*
* Convert Ethereum address to ICAP
* @method fromAddress
* @param {String} address Address as a hex string.
* @param {bool} nonstd Accept address which will result in non-standard IBAN
* @returns {String}
*/
ICAP.fromAddress = function (address, print, nonstd) {
var ret = ICAP.encode(address, print)
if ((ret.replace(' ', '').length !== 34) && (nonstd !== true)) {
throw new Error('Supplied address will result in invalid an IBAN')
}
return ret
}
/*
* Convert asset into ICAP
* @method fromAsset
* @param {Object} asset Asset object, must contain the fields asset, institution and client
* @returns {String}
*/
ICAP.fromAsset = function (asset, print) {
return ICAP.encode(asset, print)
}
/*
* Convert an ICAP into an address
* @method toAddress
* @param {String} iban IBAN/ICAP, must have an address encoded
* @returns {String}
*/
ICAP.toAddress = function (iban) {
var address = ICAP.decode(iban)
if (typeof address !== 'string') {
throw new Error('Not an address-encoded ICAP')
}
return address
}
/*
* Convert an ICAP into an asset
* @method toAsset
* @param {String} iban IBAN/ICAP, must have an asset encoded
* @returns {Object}
*/
ICAP.toAsset = function (iban) {
var asset = ICAP.decode(iban)
if (typeof asset !== 'object') {
throw new Error('Not an asset-encoded ICAP')
}
return asset
}
ICAP.isICAP = function (iban) {
try {
ICAP.decode(iban)
return true
} catch (e) {
return false
}
}
ICAP.isAddress = function (iban) {
try {
ICAP.toAddress(iban)
return true
} catch (e) {
return false
}
}
ICAP.isAsset = function (iban) {
try {
ICAP.toAsset(iban)
return true
} catch (e) {
return false
}
}
module.exports = ICAP