├── .gitignore ├── LICENSE ├── README.md ├── favicon.ico ├── img ├── bitcoinLogo.png └── lnurlqr.jpg ├── index.html └── js ├── decoder.js ├── pageupdate.js └── utils.js /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # IntelliJ project files 3 | .idea 4 | *.iml 5 | out 6 | gen 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jeff Trimmer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lndecoder 2 | 3 | For decoding lightning network payment requests as defined in [BOLT #11](https://github.com/lightningnetwork/lightning-rfc/blob/master/11-payment-encoding.md). 4 | 5 | Go to https://lndecode.com/, enter a Lightning Payment Request string into the text box. 6 | 7 | Example request string: 8 | ``` 9 | lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp 10 | ``` 11 | 12 | Or you can add the request string to the URL as a parameter. 13 | 14 | Example: 15 | 16 | https://lndecode.com/?invoice=lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp 17 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xtrimmer/lndecode/955f45b2154e91418d747973480c30e3531fb22d/favicon.ico -------------------------------------------------------------------------------- /img/bitcoinLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xtrimmer/lndecode/955f45b2154e91418d747973480c30e3531fb22d/img/bitcoinLogo.png -------------------------------------------------------------------------------- /img/lnurlqr.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xtrimmer/lndecode/955f45b2154e91418d747973480c30e3531fb22d/img/lnurlqr.jpg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 16 | 17 | Decoder for Lightning Payment Requests 18 | 19 | 20 | 21 | 22 | 24 | 41 | 42 | 43 |
44 |
45 |

Lightning Payment Request Decoder

46 |

For decoding lightning network payment requests as defined in BOLT #11.

49 |
50 |
51 | 52 | 53 |
54 |
55 | 56 |
57 |
58 |
59 | https://github.com/Xtrimmer/lndecode 60 |

If you think this little decoder is useful to you, then it's a good reason to do a donation.
61 | Your gratitude and financial help will motivate us to continue project development.

62 |

You can make a donation using the LNURL provided below.

63 |
64 | bitcoin logo 66 |

⚡ ln Address: lndecode@zeuspay.com ⚡

67 |
68 |

Alternatively, you can open a channel with my node using the node address provided.

69 |
70 | 71 |

⚡ 02a446876eafbbdaa96e768af73ecc77988f245c298727ae60313d7e3e420bde4e@66.235.13.249:9735 ⚡

72 |
73 |

As a last resort, you can also make a donation using the on-chain Bitcoin address provided below.

74 |
75 | 76 |

bc1q646de9atgm0f8kr7k5eljc4ynr74vl7337wywx

77 |

Thank you for your support!

78 | bitcoin logo 80 |
81 |
82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /js/decoder.js: -------------------------------------------------------------------------------- 1 | //TODO - A reader MUST check that the signature is valid (see the n tagged field) 2 | //TODO - Tagged part of type f: the fallback on-chain address should be decoded into an address format 3 | //TODO - A reader MUST check that the SHA-2 256 in the h field exactly matches the hashed description. 4 | //TODO - A reader MUST use the n field to validate the signature instead of performing signature recovery if a valid n field is provided. 5 | 6 | function decode(paymentRequest) { 7 | if (typeof paymentRequest !== 'string') { 8 | throw new Error('Invalid input: payment request must be a string'); 9 | } 10 | 11 | let input = paymentRequest.replace(/\s+/g, '').toLowerCase(); 12 | let splitPosition = input.lastIndexOf('1'); 13 | let humanReadablePart = input.substring(0, splitPosition); 14 | let data = input.substring(splitPosition + 1, input.length - 6); 15 | let checksum = input.substring(input.length - 6, input.length); 16 | if (!verify_checksum(humanReadablePart, bech32ToFiveBitArray(data + checksum))) { 17 | throw new Error('Malformed request: checksum is incorrect'); // A reader MUST fail if the checksum is incorrect. 18 | } 19 | return { 20 | 'human_readable_part': decodeHumanReadablePart(humanReadablePart), 21 | 'data': decodeData(data, humanReadablePart), 22 | 'checksum': checksum 23 | } 24 | } 25 | 26 | function decodeHumanReadablePart(humanReadablePart) { 27 | let prefixes = ['lnbc', 'lntb', 'lnbcrt', 'lnsb', 'lntbs']; 28 | let prefix; 29 | prefixes.forEach(value => { 30 | if (humanReadablePart.substring(0, value.length) === value) { 31 | prefix = value; 32 | } 33 | }); 34 | if (prefix == null) throw new Error('Malformed request: unknown prefix'); // A reader MUST fail if it does not understand the prefix. 35 | let amount = decodeAmount(humanReadablePart.substring(prefix.length, humanReadablePart.length)); 36 | return { 37 | 'prefix': prefix, 38 | 'amount': amount 39 | } 40 | } 41 | 42 | function decodeData(data, humanReadablePart) { 43 | let date32 = data.substring(0, 7); 44 | let dateEpoch = bech32ToInt(date32); 45 | let signature = data.substring(data.length - 104, data.length); 46 | let tagData = data.substring(7, data.length - 104); 47 | let decodedTags = decodeTags(tagData); 48 | let value = bech32ToFiveBitArray(date32 + tagData); 49 | value = fiveBitArrayTo8BitArray(value, true); 50 | value = textToHexString(humanReadablePart).concat(byteArrayToHexString(value)); 51 | return { 52 | 'time_stamp': dateEpoch, 53 | 'tags': decodedTags, 54 | 'signature': decodeSignature(signature), 55 | 'signing_data': value 56 | } 57 | } 58 | 59 | function decodeSignature(signature) { 60 | let data = fiveBitArrayTo8BitArray(bech32ToFiveBitArray(signature)); 61 | let recoveryFlag = data[data.length - 1]; 62 | let r = byteArrayToHexString(data.slice(0, 32)); 63 | let s = byteArrayToHexString(data.slice(32, data.length - 1)); 64 | return { 65 | 'r': r, 66 | 's': s, 67 | 'recovery_flag': recoveryFlag 68 | } 69 | } 70 | 71 | function decodeAmount(str) { 72 | if (str.length == 0) 73 | { 74 | return 'Any amount' // A reader SHOULD indicate if amount is unspecified 75 | } 76 | let multiplier = isDigit(str.charAt(str.length - 1)) ? '-' : str.charAt(str.length - 1); 77 | let amount = multiplier === '-' ? str : str.substring(0, str.length - 1); 78 | if (amount.substring(0, 1) === '0') { 79 | throw new Error('Malformed request: amount cannot contain leading zeros'); 80 | } 81 | amount = Number(amount); 82 | if (amount < 0 || !Number.isInteger(amount)) { 83 | throw new Error('Malformed request: amount must be a positive decimal integer'); // A reader SHOULD fail if amount contains a non-digit 84 | } 85 | 86 | switch (multiplier) { 87 | case 'p': 88 | return amount / 10; 89 | case 'n': 90 | return amount * 100; 91 | case 'u': 92 | return amount * 100000; 93 | case 'm': 94 | return amount * 100000000; 95 | case '-': 96 | return amount * 100000000000; 97 | default: 98 | // A reader SHOULD fail if amount is followed by anything except a defined multiplier. 99 | throw new Error('Malformed request: undefined amount multiplier'); 100 | } 101 | } 102 | 103 | function decodeTags(tagData) { 104 | let tags = extractTags(tagData); 105 | let decodedTags = []; 106 | tags.forEach(value => decodedTags.push(decodeTag(value.type, value.length, value.data))); 107 | return decodedTags.filter(t => t !== undefined); 108 | } 109 | 110 | function extractTags(str) { 111 | let tags = []; 112 | while (str.length > 0) { 113 | let type = str.charAt(0); 114 | let dataLength = bech32ToInt(str.substring(1, 3)); 115 | let data = str.substring(3, dataLength + 3); 116 | tags.push({ 117 | 'type': type, 118 | 'length': dataLength, 119 | 'data': data 120 | }); 121 | str = str.substring(3 + dataLength, str.length); 122 | } 123 | return tags; 124 | } 125 | 126 | function decodeTag(type, length, data) { 127 | switch (type) { 128 | case 'p': 129 | if (length !== 52) break; // A reader MUST skip over a 'p' field that does not have data_length 52 130 | return { 131 | 'type': type, 132 | 'length': length, 133 | 'description': 'payment_hash', 134 | 'value': byteArrayToHexString(fiveBitArrayTo8BitArray(bech32ToFiveBitArray(data))) 135 | }; 136 | case 's': 137 | if (length !== 52) break; // A reader MUST skip over a 's' field that does not have data_length 52 138 | return { 139 | 'type': type, 140 | 'length': length, 141 | 'description': 'payment_secret', 142 | 'value': byteArrayToHexString(fiveBitArrayTo8BitArray(bech32ToFiveBitArray(data))) 143 | }; 144 | case 'd': 145 | return { 146 | 'type': type, 147 | 'length': length, 148 | 'description': 'description', 149 | 'value': bech32ToUTF8String(data) 150 | }; 151 | case 'n': 152 | if (length !== 53) break; // A reader MUST skip over a 'n' field that does not have data_length 53 153 | return { 154 | 'type': type, 155 | 'length': length, 156 | 'description': 'payee_public_key', 157 | 'value': byteArrayToHexString(fiveBitArrayTo8BitArray(bech32ToFiveBitArray(data))) 158 | }; 159 | case 'h': 160 | if (length !== 52) break; // A reader MUST skip over a 'h' field that does not have data_length 52 161 | return { 162 | 'type': type, 163 | 'length': length, 164 | 'description': 'description_hash', 165 | 'value': data 166 | }; 167 | case 'x': 168 | return { 169 | 'type': type, 170 | 'length': length, 171 | 'description': 'expiry', 172 | 'value': bech32ToInt(data) 173 | }; 174 | case 'c': 175 | return { 176 | 'type': type, 177 | 'length': length, 178 | 'description': 'min_final_cltv_expiry', 179 | 'value': bech32ToInt(data) 180 | }; 181 | case 'f': 182 | let version = bech32ToFiveBitArray(data.charAt(0))[0]; 183 | if (version < 0 || version > 18) break; // a reader MUST skip over an f field with unknown version. 184 | data = data.substring(1, data.length); 185 | return { 186 | 'type': type, 187 | 'length': length, 188 | 'description': 'fallback_address', 189 | 'value': { 190 | 'version': version, 191 | 'fallback_address': data 192 | } 193 | }; 194 | case 'r': 195 | data = fiveBitArrayTo8BitArray(bech32ToFiveBitArray(data)); 196 | let pubkey = data.slice(0, 33); 197 | let shortChannelId = data.slice(33, 41); 198 | let feeBaseMsat = data.slice(41, 45); 199 | let feeProportionalMillionths = data.slice(45, 49); 200 | let cltvExpiryDelta = data.slice(49, 51); 201 | return { 202 | 'type': type, 203 | 'length': length, 204 | 'description': 'routing_information', 205 | 'value': { 206 | 'public_key': byteArrayToHexString(pubkey), 207 | 'short_channel_id': byteArrayToHexString(shortChannelId), 208 | 'fee_base_msat': byteArrayToInt(feeBaseMsat), 209 | 'fee_proportional_millionths': byteArrayToInt(feeProportionalMillionths), 210 | 'cltv_expiry_delta': byteArrayToInt(cltvExpiryDelta) 211 | } 212 | }; 213 | case '9': 214 | return { 215 | 'type': type, 216 | 'length': length, 217 | 'description': 'feature_bits', 218 | 'value': bech32ToBinaryString(bech32ToFiveBitArray(data)) 219 | }; 220 | default: 221 | // reader MUST skip over unknown fields 222 | } 223 | } 224 | 225 | function polymod(values) { 226 | let GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]; 227 | let chk = 1; 228 | values.forEach((value) => { 229 | let b = (chk >> 25); 230 | chk = (chk & 0x1ffffff) << 5 ^ value; 231 | for (let i = 0; i < 5; i++) { 232 | if (((b >> i) & 1) === 1) { 233 | chk ^= GEN[i]; 234 | } else { 235 | chk ^= 0; 236 | } 237 | } 238 | }); 239 | return chk; 240 | } 241 | 242 | function expand(str) { 243 | let array = []; 244 | for (let i = 0; i < str.length; i++) { 245 | array.push(str.charCodeAt(i) >> 5); 246 | } 247 | array.push(0); 248 | for (let i = 0; i < str.length; i++) { 249 | array.push(str.charCodeAt(i) & 31); 250 | } 251 | return array; 252 | } 253 | 254 | function verify_checksum(hrp, data) { 255 | hrp = expand(hrp); 256 | let all = hrp.concat(data); 257 | let bool = polymod(all); 258 | return bool === 1; 259 | } 260 | -------------------------------------------------------------------------------- /js/pageupdate.js: -------------------------------------------------------------------------------- 1 | const textarea = document.getElementById('request-string'); 2 | textarea.addEventListener('input', updatePage); 3 | 4 | const NETWORK = new Map([ 5 | ['lnbc', 'bitcoin mainnet'], 6 | ['lntb', 'bitcoin testnet'], 7 | ['lnbcrt', 'bitcoin regtest'], 8 | ['lnsb', 'bitcoin simnet'], 9 | ['lntbs', 'bitcoin signet'] 10 | ]); 11 | const TAG_TYPES = new Map([ 12 | ['p', 'Payment Hash'], 13 | ['s', 'Payment Secret'], 14 | ['d', 'Description'], 15 | ['n', 'Destination'], 16 | ['h', 'Description Hash'], 17 | ['x', 'Expiration Time'], 18 | ['c', 'Min Final CLTV Expiry'], 19 | ['f', 'Fallback On-Chain Address'], 20 | ['r', 'Routing Info'], 21 | ['9', 'Feature Bits'] 22 | ]); 23 | const KEY_DESCRIPTIONS = new Map([ 24 | ['version', 'Version'], 25 | ['fallback_address', 'Fall Back Address'], 26 | ['public_key', 'Public Key'], 27 | ['short_channel_id', 'Short Channel Id'], 28 | ['fee_base_msat', 'Fee Base Msat'], 29 | ['fee_proportional_millionths', 'Fee Proportional Millimonths'], 30 | ['cltv_expiry_delta', 'CLTV Expiry Delta'], 31 | ['r', 'R value'], 32 | ['s', 'S value'], 33 | ['recovery_flag', 'Recovery Flag'] 34 | ]); 35 | 36 | function updatePage() { 37 | try { 38 | let paymentRequest = document.getElementById('request-string').value.trim(); 39 | let decodedRequest = decode(paymentRequest); 40 | let div = document.getElementById('response'); 41 | div.textContent = ''; 42 | div.appendChild(jsonToHtml(decodedRequest)); 43 | div.classList.remove('hidden'); 44 | div.classList.remove('alert-danger'); 45 | div.classList.add('alert'); 46 | div.classList.add('alert-success'); 47 | } catch (e) { 48 | let div = document.getElementById('response'); 49 | div.innerHTML = 'Uh-Oh! Something is not quite right with this request.
' + e.toString(); 50 | div.classList.remove('hidden'); 51 | div.classList.remove('alert-success'); 52 | div.classList.add('alert'); 53 | div.classList.add('alert-danger'); 54 | } 55 | } 56 | 57 | function createStandardRow() { 58 | let row = document.createElement('div'); 59 | row.classList.add('row'); 60 | row.classList.add('border-bottom'); 61 | for (let i = 0; i < arguments.length; i++) { 62 | let col = document.createElement('div'); 63 | if (i === 0) { 64 | col.classList.add('font-weight-bold'); 65 | col.classList.add('col-sm-4'); 66 | } else { 67 | col.classList.add('break-all'); 68 | col.classList.add('col-sm-8'); 69 | col.classList.add('pl-4'); 70 | col.classList.add('pl-sm-3'); 71 | } 72 | col.textContent = arguments[i]; 73 | row.appendChild(col); 74 | } 75 | return row; 76 | } 77 | 78 | function createMultiRow(data, title) { 79 | let section = document.createElement('div'); 80 | 81 | let row = document.createElement('div'); 82 | row.classList.add('row'); 83 | row.classList.add('border-bottom'); 84 | 85 | let col = document.createElement('div'); 86 | col.classList.add('font-weight-bold'); 87 | col.classList.add('col-sm-4'); 88 | col.textContent = title; 89 | row.appendChild(col); 90 | 91 | let col2 = document.createElement('div'); 92 | col.classList.add('col-sm-8'); 93 | row.appendChild(col2); 94 | section.appendChild(row); 95 | 96 | let keys = Object.keys(data); 97 | let values = Object.values(data); 98 | 99 | for (let i = 0; i < keys.length; i++) { 100 | let subRow = document.createElement('div'); 101 | subRow.classList.add('row'); 102 | subRow.classList.add('border-bottom'); 103 | 104 | col = document.createElement('div'); 105 | col.classList.add('font-italic'); 106 | col.classList.add('col-sm-4'); 107 | col.classList.add('pl-4'); 108 | col.textContent = KEY_DESCRIPTIONS.get(keys[i]); 109 | subRow.appendChild(col); 110 | 111 | col = document.createElement('div'); 112 | col.classList.add('break-all'); 113 | col.classList.add('col-sm-8'); 114 | col.classList.add('pl-5'); 115 | col.classList.add('pl-sm-3'); 116 | col.textContent = values[i]; 117 | subRow.appendChild(col); 118 | 119 | section.appendChild(subRow); 120 | } 121 | return section; 122 | } 123 | 124 | function hasTag(tags, type) { 125 | for (let i = 0; i < tags.length; i++) { 126 | let tagType = tags[i].type; 127 | if (tagType === type) { 128 | return true; 129 | } 130 | } 131 | return false; 132 | } 133 | 134 | function jsonToHtml(json) { 135 | let container = document.createElement('div'); 136 | let pmtInfo = document.createElement('h4'); 137 | pmtInfo.textContent = 'Payment Info:'; 138 | container.appendChild(pmtInfo); 139 | 140 | let hrpDiv = document.createElement('div'); 141 | hrpDiv.classList.add('mb-4'); 142 | let row = createStandardRow('Network', NETWORK.get(json.human_readable_part.prefix)); 143 | hrpDiv.appendChild(row); 144 | 145 | let amount = json.human_readable_part.amount / 100000000000; 146 | amount = Number.isNaN(amount) ? 'any payment amount' : amount; 147 | row = createStandardRow('Amount', toFixed(amount) + ' BTC'); 148 | hrpDiv.appendChild(row); 149 | 150 | row = createStandardRow('Date', epochToDate(json.data.time_stamp)); 151 | hrpDiv.appendChild(row); 152 | 153 | for (let i = 0; i < json.data.tags.length; i++) { 154 | let tag = json.data.tags[i]; 155 | switch (tag.type) { 156 | case 'f': 157 | case 'r': 158 | row = createMultiRow(tag.value, TAG_TYPES.get('r')); 159 | hrpDiv.appendChild(row); 160 | break; 161 | case 'x': 162 | row = createStandardRow(TAG_TYPES.get(tag.type), tag.value + ' seconds'); 163 | hrpDiv.appendChild(row); 164 | break; 165 | case 'p': 166 | case 's': 167 | case 'd': 168 | case 'n': 169 | case 'h': 170 | case 'c': 171 | case '9': 172 | row = createStandardRow(TAG_TYPES.get(tag.type), tag.value); 173 | hrpDiv.appendChild(row); 174 | break; 175 | } 176 | } 177 | 178 | if (!(hasTag(json.data.tags, 'x'))) { 179 | row = createStandardRow(TAG_TYPES.get('x'), 3600 + ' seconds'); 180 | hrpDiv.appendChild(row); 181 | } 182 | 183 | if (!(hasTag(json.data.tags, 'c'))) { 184 | row = createStandardRow(TAG_TYPES.get('c'), 9); 185 | hrpDiv.appendChild(row); 186 | } 187 | 188 | row = createMultiRow(json.data.signature, 'Signature'); 189 | hrpDiv.appendChild(row); 190 | 191 | row = createStandardRow('Signing Data', json.data.signing_data); 192 | hrpDiv.appendChild(row); 193 | 194 | row = createStandardRow('Checksum', json.checksum); 195 | hrpDiv.appendChild(row); 196 | 197 | container.appendChild(hrpDiv); 198 | 199 | let rawData = document.createElement('h4'); 200 | rawData.textContent = 'Raw Data:'; 201 | container.appendChild(rawData); 202 | 203 | let textarea = document.createElement('textarea'); 204 | textarea.rows = JSON.stringify(json, null, 4).split(/\r\n|\r|\n/).length; 205 | textarea.disabled = true; 206 | textarea.classList.add('form-control'); 207 | textarea.style.whiteSpace = 'pre'; 208 | textarea.textContent = JSON.stringify(json, null, 4); 209 | container.appendChild(textarea); 210 | return container; 211 | } 212 | 213 | function getUrlVars() { 214 | var vars = {}; 215 | var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) { 216 | vars[key] = value; 217 | }); 218 | return vars; 219 | } 220 | 221 | function getUrlParam(parameter, defaultvalue){ 222 | var urlparameter = defaultvalue; 223 | if(window.location.href.indexOf(parameter) > -1){ 224 | urlparameter = getUrlVars()[parameter]; 225 | } 226 | return urlparameter; 227 | } 228 | 229 | window.onload = function(){ 230 | var invoice = getUrlParam('invoice',''); 231 | var textbox = document.getElementById('request-string'); 232 | textbox.value = invoice; 233 | if(!isEmptyOrSpaces(invoice)){ 234 | updatePage(); 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /js/utils.js: -------------------------------------------------------------------------------- 1 | const bech32CharValues = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'; 2 | 3 | function byteArrayToInt(byteArray) { 4 | let value = 0; 5 | for (let i = 0; i < byteArray.length; ++i) { 6 | value = (value << 8) + byteArray[i]; 7 | } 8 | return value; 9 | } 10 | 11 | function bech32ToInt(str) { 12 | let sum = 0; 13 | for (let i = 0; i < str.length; i++) { 14 | sum = sum * 32; 15 | sum = sum + bech32CharValues.indexOf(str.charAt(i)); 16 | } 17 | return sum; 18 | } 19 | 20 | function bech32ToFiveBitArray(str) { 21 | let array = []; 22 | for (let i = 0; i < str.length; i++) { 23 | array.push(bech32CharValues.indexOf(str.charAt(i))); 24 | } 25 | return array; 26 | } 27 | 28 | function fiveBitArrayTo8BitArray(int5Array, includeOverflow) { 29 | let count = 0; 30 | let buffer = 0; 31 | let byteArray = []; 32 | int5Array.forEach((value) => { 33 | buffer = (buffer << 5) + value; 34 | count += 5; 35 | if (count >= 8) { 36 | byteArray.push(buffer >> (count - 8) & 255); 37 | count -= 8; 38 | } 39 | }); 40 | if (includeOverflow && count > 0) { 41 | byteArray.push(buffer << (8 - count) & 255); 42 | } 43 | return byteArray; 44 | } 45 | 46 | function bech32ToUTF8String(str) { 47 | let int5Array = bech32ToFiveBitArray(str); 48 | let byteArray = fiveBitArrayTo8BitArray(int5Array); 49 | 50 | let utf8String = ''; 51 | for (let i = 0; i < byteArray.length; i++) { 52 | utf8String += '%' + ('0' + byteArray[i].toString(16)).slice(-2); 53 | } 54 | return decodeURIComponent(utf8String); 55 | } 56 | 57 | function byteArrayToHexString(byteArray) { 58 | return Array.prototype.map.call(byteArray, function (byte) { 59 | return ('0' + (byte & 0xFF).toString(16)).slice(-2); 60 | }).join(''); 61 | } 62 | 63 | function bech32ToBinaryString(byteArray) { 64 | return Array.prototype.map.call(byteArray, function (byte) { 65 | return ('000000' + byte.toString(2)).slice(-5); 66 | }).join(''); 67 | } 68 | 69 | function textToHexString(text) { 70 | let hexString = ''; 71 | for (let i = 0; i < text.length; i++) { 72 | hexString += text.charCodeAt(i).toString(16); 73 | } 74 | return hexString; 75 | } 76 | 77 | function epochToDate(int) { 78 | let date = new Date(int * 1000); 79 | return date.toUTCString(); 80 | } 81 | 82 | function isEmptyOrSpaces(str){ 83 | return str === null || str.match(/^ *$/) !== null; 84 | } 85 | 86 | function toFixed(x) { 87 | if (Math.abs(x) < 1.0) { 88 | var e = parseInt(x.toString().split('e-')[1]); 89 | if (e) { 90 | x *= Math.pow(10,e-1); 91 | x = '0.' + (new Array(e)).join('0') + x.toString().substring(2); 92 | } 93 | } else { 94 | var e = parseInt(x.toString().split('+')[1]); 95 | if (e > 20) { 96 | e -= 20; 97 | x /= Math.pow(10,e); 98 | x += (new Array(e+1)).join('0'); 99 | } 100 | } 101 | return x; 102 | } 103 | 104 | function isDigit(str) { return str >= '0' && str <= '9' } --------------------------------------------------------------------------------