-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy patholga_stamp.html
256 lines (226 loc) · 9.76 KB
/
olga_stamp.html
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>OLGA STAMP MINTER</title>
<link rel="stylesheet" href="css/main.css">
<script src="js/xcp/hex.js"></script>
<script src="js/xcp/assets.js"></script>
<script src="js/xcp/build_tx_msg.js"></script>
<script src="js/xcp/decode_tx_msg.js"></script>
<script src="js/xcp/display.js"></script>
<script src="js/xcp/rc4.js"></script>
<script src="js/xcp/validate_address.js"></script>
<script src="js/xcp/cip33.js"></script>
<script src="js/lib/bitcoinjs-lib.js"></script>
<script src="js/lib/bitcoinjs-message.js"></script>
<script src="js/lib/jquery-3.4.1.min.js"></script>
<script src="js/lib/lozad.min.js"></script>
<script src="js/lib/sha256.js"></script>
<script src="js/lib/btc.js"></script>
<script>
let addr = '';
let asset = '';
let longname = '';
let utxo = '';
let div = '';
let max = '';
let description_text = '';
let network = 'bitcoin';
function get_url_parameters() {
//Get url parameter 'address'
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
if (urlParams.has('address')) {
addr = urlParams.get('address');
}
if (urlParams.has('network')) {
network = urlParams.get('network');
}
if (urlParams.has('utxo')) {
utxo = urlParams.get('utxo');
document.getElementById('input_utxo').value = utxo;
document.getElementById('div_utxo').style.display = 'none';
}
}
function display() {
let header = wallet_header;
header = header.replace('{address}', addr_short(addr));
if (utxo != '') {
header = header.replace('{utxo}', '<span onclick="document.getElementById(\'div_utxo\').style.display=\'block\';">UTXO '+utxo.slice(0,4)+'</span>');
} else{
header = header.replace('{utxo}', '');
}
document.getElementById('html_top').innerHTML = header;
}
function randInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function rand_octa_name() {
//Returns a random numeric asset name
// that contains eight eights
let name = '';
let eights = 0;
do {
name = 'A8';
eights = 1;
for (let i=2; i<=17; i++) {
let r = randInt(0,9);
name += r;
if (r == 8) eights += 1;
}
} while (eights != 7);
name += '8';
return name;
}
function show_counter() {
let text = document.getElementById('input_text').value;
let hex = utf8_to_hex(text);
let counter = hex.length/2 + '/54';
document.getElementById('count_chars').innerHTML = counter;
}
function prepare_inputs() {
let inp_utxo = document.getElementById('input_utxo').value; //eg a1870614c4cd1b3e20f98316f062c8622313eca4bbeb3f7bb695a61c164a45e7
let inp_name = rand_octa_name(); //document.getElementById('input_name').value; //eg A269110002080613080 = A + 18 random digits
let inp_supply = document.getElementById('input_supply').value;
let inp_tip_btc = document.getElementById('input_tip').value;
//Inputs formatted ok?
let utxo = inp_utxo.substring(0,64);
utxo = utxo.toLowerCase();
if (utxo.length != 64) {
print_result("UTXO is not valid"); return;
}
let inp_len = utf8_to_hex(description_text).length / 2;
if (inp_len > 54) {
print_result("File name is too long. Max 48 chars."); return; //54 minus 'STAMP:' prefix
}
if (/^[.a-zA-Z0-9_-]+$/.test(file_name) == false) {
print_result("File name must only contain Latin letters (a-zA-Z), numbers, '_', '-' and '.'"); return; //strict to please indexers
}
let file_extension = file_name.split('.').pop().toLowerCase();
console.log(file_extension);
/*if(['jpeg','jpg','gif','png','svg'].includes(file_extension) == false) {
print_result("File extension must be 'jpeg', 'jpg', 'gif', 'png' or 'svg'"); return;
}*/
if (inp_len == 0) {
print_result("No file selected"); return;
}
if (isNaN(inp_tip_btc)) {
print_result("Tip is not a number"); return;
}
let out = '';
let opreturn_unencoded = cip33_msg_issuance(inp_name, parseInt(inp_supply), description_text);
console.log('Op_return unencoded: ' + opreturn_unencoded);
let opreturn = rc4_hex(utxo, opreturn_unencoded);
//Sanity checks. Does decoded tx match inputs?
let info = decode_tx(opreturn, utxo);
if (info.prefix != 'CNTRPRTY') {
print_result("Op_return encoding error. Wrong prefix."); return;
}
if (info.msg_id != 20) {
print_result("Op_return encoding error. Wrong messge ID."); return;
}
if (file_size == 0) {
print_result("File is empty."); return;
}
if (file_size > 6304) {//197 * 32 bytes
print_result("File is too large. Max size 6304 bytes."); return;
}
//Output messages and code
let sum_dust = 0;
let count = 0;
let dust = 0;
let addresses = file_to_addresses(file_hex, network);
let code = '';
for (let i = 0; i<addresses.length; i++) {
dust = dust_limit+count;
code += '\n' + addresses[i] + ',' + sat_to_btc(dust);
count += 1;
sum_dust += dust;
if (i == 0) { //opreturn 2nd output. Transafers and burns ownership.
dust = dust_limit+count;
//for electrum <=4.4 : code += '\nOP_RETURN ' + opreturn + ',' + sat_to_btc(dust);
code += '\nscript(OP_RETURN ' + opreturn + '),' + sat_to_btc(dust);
count += 1;
sum_dust += dust;
}
}
if (network == 'bitcoin') {
code += tip_line(inp_tip_btc);
}
code = code.slice(1);
//Sanity check
if (file_hex != addresses_to_hex(addresses)) {
print_result("File or addresses encoding error."); return;
}
//document.getElementById('warning_message').innerHTML = 'This transaction will forever save "' + file_name + '" on the blockchain.<br>₿' + sat_to_btc(sum_dust) + ' will get burnt.';
out += '<b>Success!</b>'
out += '<br>Make sure all details are correct.';
if (network == 'testnet') out += '<br><b>Network Testnet</b>'
out += '<br>Input coin ' + utxo;
out += '<br>Burn ₿' + sat_to_btc(sum_dust);
out += '<br>Token ' + inp_name;
out += '<br>Supply ' + parseInt(inp_supply);
out += '<br>Text ' + description_text;
//show image
if (file_extension == 'svg') file_extension = 'svg+xml';
let file_base64 = btoa(file_hex.match(/\w{2}/g).map(function(y){return String.fromCharCode(parseInt(y,16));}).join(''));
out += '<br> <img style="image-rendering:pixelated" src="data:image/' + file_extension + ';base64, ' + file_base64 + '" /><br>';
out += '<br>Send from Electrum to mint your token.';
print_result(out, code)
}
function print_result(message = '', code = '') {
document.getElementById('text_output').innerHTML = message;
document.getElementById('code_output').innerHTML = code;
}
</script>
</head>
<body onload="get_url_parameters();display();">
<div id="content">
<div id="html_top"></div>
<h1>OLGA STAMP MINTER</h1>
<div>Generate a Bitcoin Stamp minting transaction.<br><a href="https://jpjanssen.com/olga-stamp-minter-for-electrum/" target="_blank">Instructions</a>.</div><br>
<div id="div_utxo">UTXO (Input Coin)<br>
<input id="input_utxo"><br><br></div>
<div id="div_supply">Token Supply<br>
<input id="input_supply" value="1"><br><br></div>
<div id="div_file">File<br>
<input id="browseOpen" type="file" id="file-input" /><br><br></div>
<div id="div_tip">Optional Tip to Developer (BTC)<span id="suggested_tip" style="float:right" onclick="document.getElementById('input_tip').value='0.0001';">Suggested: 0.0001</span><br>
<input id="input_tip"><br></div>
<button type="button" id="generate" onclick="prepare_inputs();">Generate Transaction</button><br>
<i><span style="font-size:75%" id="warning_message"> </span></i><br>
<span id="text_output" style="white-space:pre;font-size:75%;"> <br> </span>
<textarea id="code_output" rows="5"></textarea>
<button type="button" id="copy" onclick="document.getElementById('code_output').select();document.execCommand('copy');">Copy</button><br><br>
</div>
</body>
<script>
document.getElementById('input_tip').value = tip_btc;
let file_hex = '';
let file_name = '';
let file_size = -1;
var fileInput = document.getElementById("browseOpen");
fileInput.onchange = function () {
file_hex = '';
var fr = new FileReader();
fr.onloadend = function () {
var result = this.result;
for (var i = 0; i < this.result.length; i++) {
var byteStr = result.charCodeAt(i).toString(16);
if (byteStr.length < 2) {
byteStr = "0" + byteStr;
}
file_hex += "" + byteStr;
}
//document.getElementById("input_text").value = 'STAMP:' + file_name;
file_size = file_hex.length / 2;
description_text= 'STAMP:' + file_name;
};
fr.readAsBinaryString(this.files[0]);
file_name = this.files[0].name;
};
</script>
</html>