1 | const qs = require('qs');
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | class URI {
|
7 |
|
8 | |
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 | constructor(coins) {
|
15 |
|
16 | this.coins = coins;
|
17 |
|
18 | this.regex = /([a-z]+):\/?\/?([^?]+)(\?([^]+))?/;
|
19 |
|
20 | }
|
21 |
|
22 | |
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 | parse(str) {
|
35 |
|
36 | if (this.isURI(str)) {
|
37 |
|
38 | return this.parseURI(str);
|
39 |
|
40 | } else {
|
41 |
|
42 | const coin = this.getCoinFromAddress(str);
|
43 |
|
44 | if (coin) {
|
45 |
|
46 | return {
|
47 | coinCode: coin.constructor.code,
|
48 | address: str,
|
49 | };
|
50 |
|
51 | } else {
|
52 |
|
53 | throw new Error(`Unable to parse URI: ${str}`)
|
54 |
|
55 | }
|
56 | }
|
57 | }
|
58 |
|
59 | |
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 | stringify(params) {
|
71 |
|
72 | const { address, coinCode, amount } = params;
|
73 |
|
74 | const coin = this.getCoinFromCode(coinCode);
|
75 |
|
76 | if(!coin) {
|
77 | throw new Error(`Unknown coin code: ${coinCode}`);
|
78 | }
|
79 |
|
80 | let uri = `${coin.getURIPrefix()}:`;
|
81 | uri += address;
|
82 |
|
83 | const options = {};
|
84 |
|
85 | if (amount) {
|
86 | options.amount = amount;
|
87 | }
|
88 |
|
89 | if (Object.keys(options).length > 0) {
|
90 |
|
91 | uri += '?';
|
92 | const query = qs.stringify(options);
|
93 | uri += query;
|
94 |
|
95 | }
|
96 | return uri;
|
97 |
|
98 | }
|
99 |
|
100 |
|
101 | getCoinFromAddress(address) {
|
102 | return this.findCoin(coin =>
|
103 | !coin.constructor.isToken &&
|
104 | coin.validAddress(address)
|
105 | );
|
106 | }
|
107 | getCoinFromPrefix(prefix) {
|
108 | return this.findCoin(coin => coin.getURIPrefix() === prefix);
|
109 | }
|
110 | getCoinFromCode(code) {
|
111 | return this.coins[code];
|
112 | }
|
113 |
|
114 | findCoin(fn) {
|
115 | return Object.keys(this.coins)
|
116 | .map(coinCode => this.coins[coinCode])
|
117 | .find(fn);
|
118 | }
|
119 |
|
120 | isURI(str) {
|
121 | return this.regex.test(str);
|
122 | }
|
123 |
|
124 | isAddress(str) {
|
125 | return !!this.getCoinFromAddress(str);
|
126 | }
|
127 |
|
128 | parseURI(str) {
|
129 | const qregex = this.regex.exec(str);
|
130 | if (!qregex) throw new Error(`Invalid URI: ${str}`);
|
131 |
|
132 | const prefix = qregex[1];
|
133 | const address = qregex[2];
|
134 |
|
135 | const query = qregex[4];
|
136 | const options = qs.parse(query);
|
137 | const { amount } = options;
|
138 |
|
139 | const coin = this.getCoinFromPrefix(prefix);
|
140 |
|
141 | if(!coin) {
|
142 | throw new Error(`Unknown URI protocol: ${prefix}`);
|
143 | }
|
144 |
|
145 | const data = {
|
146 | coinCode: coin.constructor.code,
|
147 | address,
|
148 | amount,
|
149 | };
|
150 |
|
151 | return data;
|
152 | }
|
153 |
|
154 |
|
155 | }
|
156 |
|
157 | module.exports = URI;
|